Importing rustc-1.58.0 Bug: 213921092 Test: ./build.py --lto=thin Change-Id: Icc9fe2d5bc3327afd20177fc973a6e44941f787f
diff --git a/compiler/rustc_apfloat/src/ieee.rs b/compiler/rustc_apfloat/src/ieee.rs index 739c6fd..9627795 100644 --- a/compiler/rustc_apfloat/src/ieee.rs +++ b/compiler/rustc_apfloat/src/ieee.rs
@@ -389,7 +389,6 @@ let _: Loss = sig::shift_right(&mut sig, &mut exp, trailing_zeros as usize); // Change the exponent from 2^e to 10^e. - #[allow(clippy::comparison_chain)] if exp == 0 { // Nothing to do. } else if exp > 0 { @@ -2527,7 +2526,6 @@ if *a_sign ^ b_sign { let (reverse, loss); - #[allow(clippy::comparison_chain)] if bits == 0 { reverse = cmp(a_sig, b_sig) == Ordering::Less; loss = Loss::ExactlyZero;
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 6d5f47a..6f9ecb9c 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs
@@ -19,7 +19,6 @@ #![feature(rustc_attrs)] #![cfg_attr(test, feature(test))] -use rustc_data_structures::sync; use smallvec::SmallVec; use std::alloc::Layout; @@ -112,7 +111,7 @@ // alloc() will trigger a grow(). ptr: Cell::new(ptr::null_mut()), end: Cell::new(ptr::null_mut()), - chunks: RefCell::new(vec![]), + chunks: Default::default(), _own: PhantomData, } } @@ -326,13 +325,17 @@ unsafe impl<T: Send> Send for TypedArena<T> {} +/// An arena that can hold objects of multiple different types that impl `Copy` +/// and/or satisfy `!mem::needs_drop`. pub struct DroplessArena { /// A pointer to the start of the free space. start: Cell<*mut u8>, /// A pointer to the end of free space. /// - /// The allocation proceeds from the end of the chunk towards the start. + /// The allocation proceeds downwards from the end of the chunk towards the + /// start. (This is slightly simpler and faster than allocating upwards, + /// see <https://fitzgeraldnick.com/2019/11/01/always-bump-downwards.html>.) /// When this pointer crosses the start pointer, a new chunk is allocated. end: Cell<*mut u8>, @@ -517,130 +520,16 @@ } } -/// Calls the destructor for an object when dropped. -struct DropType { - drop_fn: unsafe fn(*mut u8), - obj: *mut u8, -} - -// SAFETY: we require `T: Send` before type-erasing into `DropType`. -#[cfg(parallel_compiler)] -unsafe impl sync::Send for DropType {} - -impl DropType { - #[inline] - unsafe fn new<T: sync::Send>(obj: *mut T) -> Self { - unsafe fn drop_for_type<T>(to_drop: *mut u8) { - std::ptr::drop_in_place(to_drop as *mut T) - } - - DropType { drop_fn: drop_for_type::<T>, obj: obj as *mut u8 } - } -} - -impl Drop for DropType { - fn drop(&mut self) { - unsafe { (self.drop_fn)(self.obj) } - } -} - -/// An arena which can be used to allocate any type. -/// -/// # Safety -/// -/// Allocating in this arena is unsafe since the type system -/// doesn't know which types it contains. In order to -/// allocate safely, you must store a `PhantomData<T>` -/// alongside this arena for each type `T` you allocate. -#[derive(Default)] -pub struct DropArena { - /// A list of destructors to run when the arena drops. - /// Ordered so `destructors` gets dropped before the arena - /// since its destructor can reference memory in the arena. - destructors: RefCell<Vec<DropType>>, - arena: DroplessArena, -} - -impl DropArena { - #[inline] - pub unsafe fn alloc<T>(&self, object: T) -> &mut T - where - T: sync::Send, - { - let mem = self.arena.alloc_raw(Layout::new::<T>()) as *mut T; - // Write into uninitialized memory. - ptr::write(mem, object); - let result = &mut *mem; - // Record the destructor after doing the allocation as that may panic - // and would cause `object`'s destructor to run twice if it was recorded before. - self.destructors.borrow_mut().push(DropType::new(result)); - result - } - - #[inline] - pub unsafe fn alloc_from_iter<T, I>(&self, iter: I) -> &mut [T] - where - T: sync::Send, - I: IntoIterator<Item = T>, - { - let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); - if vec.is_empty() { - return &mut []; - } - let len = vec.len(); - - let start_ptr = self.arena.alloc_raw(Layout::array::<T>(len).unwrap()) as *mut T; - - let mut destructors = self.destructors.borrow_mut(); - // Reserve space for the destructors so we can't panic while adding them. - destructors.reserve(len); - - // Move the content to the arena by copying it and then forgetting - // the content of the SmallVec. - vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); - mem::forget(vec.drain(..)); - - // Record the destructors after doing the allocation as that may panic - // and would cause `object`'s destructor to run twice if it was recorded before. - for i in 0..len { - destructors.push(DropType::new(start_ptr.add(i))); - } - - slice::from_raw_parts_mut(start_ptr, len) - } -} - -pub macro arena_for_type { - ([][$ty:ty]) => { - $crate::TypedArena<$ty> - }, - ([few $(, $attrs:ident)*][$ty:ty]) => { - ::std::marker::PhantomData<$ty> - }, - ([$ignore:ident $(, $attrs:ident)*]$args:tt) => { - $crate::arena_for_type!([$($attrs),*]$args) - }, -} - -pub macro which_arena_for_type { - ([][$arena:expr]) => { - ::std::option::Option::Some($arena) - }, - ([few$(, $attrs:ident)*][$arena:expr]) => { - ::std::option::Option::None - }, - ([$ignore:ident$(, $attrs:ident)*]$args:tt) => { - $crate::which_arena_for_type!([$($attrs),*]$args) - }, -} - +// Declare an `Arena` containing one dropless arena and many typed arenas (the +// types of the typed arenas are specified by the arguments). The dropless +// arena will be used for any types that impl `Copy`, and also for any of the +// specified types that satisfy `!mem::needs_drop`. #[rustc_macro_transparency = "semitransparent"] -pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) { +pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) { #[derive(Default)] - pub struct Arena<$tcx> { + pub struct Arena<'tcx> { pub dropless: $crate::DroplessArena, - drop: $crate::DropArena, - $($name: $crate::arena_for_type!($a[$ty]),)* + $($name: $crate::TypedArena<$ty>,)* } pub trait ArenaAllocatable<'tcx, T = Self>: Sized { @@ -651,6 +540,7 @@ ) -> &'a mut [Self]; } + // Any type that impls `Copy` can be arena-allocated in the `DroplessArena`. impl<'tcx, T: Copy> ArenaAllocatable<'tcx, ()> for T { #[inline] fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self { @@ -663,36 +553,27 @@ ) -> &'a mut [Self] { arena.dropless.alloc_from_iter(iter) } - } $( - impl<$tcx> ArenaAllocatable<$tcx, $ty> for $ty { + impl<'tcx> ArenaAllocatable<'tcx, $ty> for $ty { #[inline] - fn allocate_on<'a>(self, arena: &'a Arena<$tcx>) -> &'a mut Self { + fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self { if !::std::mem::needs_drop::<Self>() { - return arena.dropless.alloc(self); - } - match $crate::which_arena_for_type!($a[&arena.$name]) { - ::std::option::Option::<&$crate::TypedArena<Self>>::Some(ty_arena) => { - ty_arena.alloc(self) - } - ::std::option::Option::None => unsafe { arena.drop.alloc(self) }, + arena.dropless.alloc(self) + } else { + arena.$name.alloc(self) } } #[inline] fn allocate_from_iter<'a>( - arena: &'a Arena<$tcx>, + arena: &'a Arena<'tcx>, iter: impl ::std::iter::IntoIterator<Item = Self>, ) -> &'a mut [Self] { if !::std::mem::needs_drop::<Self>() { - return arena.dropless.alloc_from_iter(iter); - } - match $crate::which_arena_for_type!($a[&arena.$name]) { - ::std::option::Option::<&$crate::TypedArena<Self>>::Some(ty_arena) => { - ty_arena.alloc_from_iter(iter) - } - ::std::option::Option::None => unsafe { arena.drop.alloc_from_iter(iter) }, + arena.dropless.alloc_from_iter(iter) + } else { + arena.$name.alloc_from_iter(iter) } } } @@ -704,6 +585,7 @@ value.allocate_on(self) } + // Any type that impls `Copy` can have slices be arena-allocated in the `DroplessArena`. #[inline] pub fn alloc_slice<T: ::std::marker::Copy>(&self, value: &[T]) -> &mut [T] { if value.is_empty() {
diff --git a/compiler/rustc_ast/README.md b/compiler/rustc_ast/README.md index dd407db..b2b90fe 100644 --- a/compiler/rustc_ast/README.md +++ b/compiler/rustc_ast/README.md
@@ -1,6 +1,5 @@ The `rustc_ast` crate contains those things concerned purely with syntax -– that is, the AST ("abstract syntax tree"), parser, pretty-printer, -lexer, macro expander, and utilities for traversing ASTs. +– that is, the AST ("abstract syntax tree"), along with some definitions for tokens and token streams, data structures/traits for mutating ASTs, and shared definitions for other AST-related parts of the compiler (like the lexer and macro-expansion). For more information about how these things work in rustc, see the rustc dev guide:
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e2424e7..55b243a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs
@@ -405,6 +405,21 @@ pub kind: GenericParamKind, } +impl GenericParam { + pub fn span(&self) -> Span { + match &self.kind { + GenericParamKind::Lifetime | GenericParamKind::Type { default: None } => { + self.ident.span + } + GenericParamKind::Type { default: Some(ty) } => self.ident.span.to(ty.span), + GenericParamKind::Const { kw_span, default: Some(default), .. } => { + kw_span.to(default.value.span) + } + GenericParamKind::Const { kw_span, default: None, ty } => kw_span.to(ty.span), + } + } +} + /// Represents lifetime, type and const parameters attached to a declaration of /// a function, enum, trait, etc. #[derive(Clone, Encodable, Decodable, Debug)] @@ -2058,7 +2073,7 @@ pub template: Vec<InlineAsmTemplatePiece>, pub template_strs: Box<[(Symbol, Option<Symbol>, Span)]>, pub operands: Vec<(InlineAsmOperand, Span)>, - pub clobber_abi: Option<(Symbol, Span)>, + pub clobber_abis: Vec<(Symbol, Span)>, pub options: InlineAsmOptions, pub line_spans: Vec<Span>, } @@ -2645,34 +2660,42 @@ } #[derive(Clone, Encodable, Decodable, Debug)] -pub struct TraitKind( - pub IsAuto, - pub Unsafe, - pub Generics, - pub GenericBounds, - pub Vec<P<AssocItem>>, -); - -#[derive(Clone, Encodable, Decodable, Debug)] -pub struct TyAliasKind(pub Defaultness, pub Generics, pub GenericBounds, pub Option<P<Ty>>); - -#[derive(Clone, Encodable, Decodable, Debug)] -pub struct ImplKind { +pub struct Trait { pub unsafety: Unsafe, - pub polarity: ImplPolarity, - pub defaultness: Defaultness, - pub constness: Const, + pub is_auto: IsAuto, pub generics: Generics, + pub bounds: GenericBounds, + pub items: Vec<P<AssocItem>>, +} +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct TyAlias { + pub defaultness: Defaultness, + pub generics: Generics, + pub bounds: GenericBounds, + pub ty: Option<P<Ty>>, +} + +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct Impl { + pub defaultness: Defaultness, + pub unsafety: Unsafe, + pub generics: Generics, + pub constness: Const, + pub polarity: ImplPolarity, /// The trait being implemented, if any. pub of_trait: Option<TraitRef>, - pub self_ty: P<Ty>, pub items: Vec<P<AssocItem>>, } #[derive(Clone, Encodable, Decodable, Debug)] -pub struct FnKind(pub Defaultness, pub FnSig, pub Generics, pub Option<P<Block>>); +pub struct Fn { + pub defaultness: Defaultness, + pub generics: Generics, + pub sig: FnSig, + pub body: Option<P<Block>>, +} #[derive(Clone, Encodable, Decodable, Debug)] pub enum ItemKind { @@ -2695,7 +2718,7 @@ /// A function declaration (`fn`). /// /// E.g., `fn foo(bar: usize) -> usize { .. }`. - Fn(Box<FnKind>), + Fn(Box<Fn>), /// A module declaration (`mod`). /// /// E.g., `mod foo;` or `mod foo { .. }`. @@ -2707,11 +2730,11 @@ /// E.g., `extern {}` or `extern "C" {}`. ForeignMod(ForeignMod), /// Module-level inline assembly (from `global_asm!()`). - GlobalAsm(InlineAsm), + GlobalAsm(Box<InlineAsm>), /// A type alias (`type`). /// /// E.g., `type Foo = Bar<u8>;`. - TyAlias(Box<TyAliasKind>), + TyAlias(Box<TyAlias>), /// An enum definition (`enum`). /// /// E.g., `enum Foo<A, B> { C<A>, D<B> }`. @@ -2727,7 +2750,7 @@ /// A trait declaration (`trait`). /// /// E.g., `trait Foo { .. }`, `trait Foo<T> { .. }` or `auto trait Foo {}`. - Trait(Box<TraitKind>), + Trait(Box<Trait>), /// Trait alias /// /// E.g., `trait Foo = Bar + Quux;`. @@ -2735,7 +2758,7 @@ /// An implementation. /// /// E.g., `impl<A> Foo<A> { .. }` or `impl<A> Trait for Foo<A> { .. }`. - Impl(Box<ImplKind>), + Impl(Box<Impl>), /// A macro invocation. /// /// E.g., `foo!(..)`. @@ -2782,14 +2805,14 @@ pub fn generics(&self) -> Option<&Generics> { match self { - Self::Fn(box FnKind(_, _, generics, _)) - | Self::TyAlias(box TyAliasKind(_, generics, ..)) + Self::Fn(box Fn { generics, .. }) + | Self::TyAlias(box TyAlias { generics, .. }) | Self::Enum(_, generics) | Self::Struct(_, generics) | Self::Union(_, generics) - | Self::Trait(box TraitKind(_, _, generics, ..)) + | Self::Trait(box Trait { generics, .. }) | Self::TraitAlias(generics, _) - | Self::Impl(box ImplKind { generics, .. }) => Some(generics), + | Self::Impl(box Impl { generics, .. }) => Some(generics), _ => None, } } @@ -2812,9 +2835,9 @@ /// If `def` is parsed, then the constant is provided, and otherwise required. Const(Defaultness, P<Ty>, Option<P<Expr>>), /// An associated function. - Fn(Box<FnKind>), + Fn(Box<Fn>), /// An associated type. - TyAlias(Box<TyAliasKind>), + TyAlias(Box<TyAlias>), /// A macro expanding to associated items. MacCall(MacCall), } @@ -2825,9 +2848,9 @@ impl AssocItemKind { pub fn defaultness(&self) -> Defaultness { match *self { - Self::Const(def, ..) - | Self::Fn(box FnKind(def, ..)) - | Self::TyAlias(box TyAliasKind(def, ..)) => def, + Self::Const(defaultness, ..) + | Self::Fn(box Fn { defaultness, .. }) + | Self::TyAlias(box TyAlias { defaultness, .. }) => defaultness, Self::MacCall(..) => Defaultness::Final, } } @@ -2864,9 +2887,9 @@ /// A foreign static item (`static FOO: u8`). Static(P<Ty>, Mutability, Option<P<Expr>>), /// An foreign function. - Fn(Box<FnKind>), + Fn(Box<Fn>), /// An foreign type. - TyAlias(Box<TyAliasKind>), + TyAlias(Box<TyAlias>), /// A macro expanding to foreign items. MacCall(MacCall), }
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 5f17008..927d7c6 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -62,7 +62,7 @@ self.meta_item().and_then(|meta_item| meta_item.ident()) } pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or_else(Ident::invalid).name + self.ident().unwrap_or_else(Ident::empty).name } /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a @@ -131,7 +131,7 @@ } } pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or_else(Ident::invalid).name + self.ident().unwrap_or_else(Ident::empty).name } pub fn value_str(&self) -> Option<Symbol> { @@ -166,7 +166,7 @@ if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None } } pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or_else(Ident::invalid).name + self.ident().unwrap_or_else(Ident::empty).name } // Example:
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index e3c6105..b9db2a7 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs
@@ -1,4 +1,4 @@ -//! The Rust parser and macro expander. +//! The Rust Abstract Syntax Tree (AST). //! //! # Note //! @@ -16,6 +16,7 @@ #![feature(nll)] #![feature(min_specialization)] #![recursion_limit = "256"] +#![feature(slice_internals)] #[macro_use] extern crate rustc_macros; @@ -25,6 +26,7 @@ pub mod comments; pub mod literal; pub mod parser; + pub mod unicode; } pub mod ast;
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index ba86036..fc5cc96 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -37,9 +37,7 @@ /// Mutable token visiting only exists for the `macro_rules` token marker and should not be /// used otherwise. Token visitor would be entirely separate from the regular visitor if /// the marker didn't have to visit AST fragments in nonterminal tokens. - fn token_visiting_enabled(&self) -> bool { - false - } + const VISIT_TOKENS: bool = false; // Methods in this trait have one of three forms: // @@ -363,7 +361,7 @@ } MacArgs::Eq(eq_span, token) => { vis.visit_span(eq_span); - if vis.token_visiting_enabled() { + if T::VISIT_TOKENS { visit_token(token, vis); } else { // The value in `#[key = VALUE]` must be visited as an expression for backward @@ -461,7 +459,8 @@ vis.visit_mt(mt); } TyKind::BareFn(bft) => { - let BareFnTy { unsafety: _, ext: _, generic_params, decl } = bft.deref_mut(); + let BareFnTy { unsafety, ext: _, generic_params, decl } = bft.deref_mut(); + visit_unsafety(unsafety, vis); generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param)); vis.visit_fn_decl(decl); } @@ -490,7 +489,8 @@ } pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis: &mut T) { - let ForeignMod { unsafety: _, abi: _, items } = foreign_mod; + let ForeignMod { unsafety, abi: _, items } = foreign_mod; + visit_unsafety(unsafety, vis); items.flat_map_in_place(|item| vis.flat_map_foreign_item(item)); } @@ -682,7 +682,7 @@ // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. pub fn visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) { - if vis.token_visiting_enabled() && !tts.is_empty() { + if T::VISIT_TOKENS && !tts.is_empty() { let tts = Lrc::make_mut(tts); visit_vec(tts, |(tree, _is_joint)| visit_tt(tree, vis)); } @@ -692,14 +692,14 @@ AttrAnnotatedTokenStream(tts): &mut AttrAnnotatedTokenStream, vis: &mut T, ) { - if vis.token_visiting_enabled() && !tts.is_empty() { + if T::VISIT_TOKENS && !tts.is_empty() { let tts = Lrc::make_mut(tts); visit_vec(tts, |(tree, _is_joint)| visit_attr_annotated_tt(tree, vis)); } } pub fn visit_lazy_tts_opt_mut<T: MutVisitor>(lazy_tts: Option<&mut LazyTokenStream>, vis: &mut T) { - if vis.token_visiting_enabled() { + if T::VISIT_TOKENS { if let Some(lazy_tts) = lazy_tts { let mut tts = lazy_tts.create_token_stream(); visit_attr_annotated_tts(&mut tts, vis); @@ -790,6 +790,38 @@ } } +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. +pub fn visit_defaultness<T: MutVisitor>(defaultness: &mut Defaultness, vis: &mut T) { + match defaultness { + Defaultness::Default(span) => vis.visit_span(span), + Defaultness::Final => {} + } +} + +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. +pub fn visit_unsafety<T: MutVisitor>(unsafety: &mut Unsafe, vis: &mut T) { + match unsafety { + Unsafe::Yes(span) => vis.visit_span(span), + Unsafe::No => {} + } +} + +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. +pub fn visit_polarity<T: MutVisitor>(polarity: &mut ImplPolarity, vis: &mut T) { + match polarity { + ImplPolarity::Positive => {} + ImplPolarity::Negative(span) => vis.visit_span(span), + } +} + +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. +pub fn visit_constness<T: MutVisitor>(constness: &mut Const, vis: &mut T) { + match constness { + Const::Yes(span) => vis.visit_span(span), + Const::No => {} + } +} + pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut Async, vis: &mut T) { match asyncness { Async::Yes { span: _, closure_id, return_impl_trait_id } => { @@ -957,25 +989,35 @@ match kind { ItemKind::ExternCrate(_orig_name) => {} ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree), - ItemKind::Static(ty, _, expr) | ItemKind::Const(_, ty, expr) => { + ItemKind::Static(ty, _, expr) => { vis.visit_ty(ty); visit_opt(expr, |expr| vis.visit_expr(expr)); } - ItemKind::Fn(box FnKind(_, sig, generics, body)) => { + ItemKind::Const(defaultness, ty, expr) => { + visit_defaultness(defaultness, vis); + vis.visit_ty(ty); + visit_opt(expr, |expr| vis.visit_expr(expr)); + } + ItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { + visit_defaultness(defaultness, vis); visit_fn_sig(sig, vis); vis.visit_generics(generics); visit_opt(body, |body| vis.visit_block(body)); } - ItemKind::Mod(_unsafety, mod_kind) => match mod_kind { - ModKind::Loaded(items, _inline, inner_span) => { - vis.visit_span(inner_span); - items.flat_map_in_place(|item| vis.flat_map_item(item)); + ItemKind::Mod(unsafety, mod_kind) => { + visit_unsafety(unsafety, vis); + match mod_kind { + ModKind::Loaded(items, _inline, inner_span) => { + vis.visit_span(inner_span); + items.flat_map_in_place(|item| vis.flat_map_item(item)); + } + ModKind::Unloaded => {} } - ModKind::Unloaded => {} - }, + } ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm), ItemKind::GlobalAsm(asm) => noop_visit_inline_asm(asm, vis), - ItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => { + ItemKind::TyAlias(box TyAlias { defaultness, generics, bounds, ty }) => { + visit_defaultness(defaultness, vis); vis.visit_generics(generics); visit_bounds(bounds, vis); visit_opt(ty, |ty| vis.visit_ty(ty)); @@ -988,22 +1030,27 @@ vis.visit_variant_data(variant_data); vis.visit_generics(generics); } - ItemKind::Impl(box ImplKind { - unsafety: _, - polarity: _, - defaultness: _, - constness: _, + ItemKind::Impl(box Impl { + defaultness, + unsafety, generics, + constness, + polarity, of_trait, self_ty, items, }) => { + visit_defaultness(defaultness, vis); + visit_unsafety(unsafety, vis); vis.visit_generics(generics); + visit_constness(constness, vis); + visit_polarity(polarity, vis); visit_opt(of_trait, |trait_ref| vis.visit_trait_ref(trait_ref)); vis.visit_ty(self_ty); items.flat_map_in_place(|item| vis.flat_map_impl_item(item)); } - ItemKind::Trait(box TraitKind(.., generics, bounds, items)) => { + ItemKind::Trait(box Trait { unsafety, is_auto: _, generics, bounds, items }) => { + visit_unsafety(unsafety, vis); vis.visit_generics(generics); visit_bounds(bounds, vis); items.flat_map_in_place(|item| vis.flat_map_trait_item(item)); @@ -1027,16 +1074,19 @@ visitor.visit_vis(vis); visit_attrs(attrs, visitor); match kind { - AssocItemKind::Const(_, ty, expr) => { + AssocItemKind::Const(defaultness, ty, expr) => { + visit_defaultness(defaultness, visitor); visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } - AssocItemKind::Fn(box FnKind(_, sig, generics, body)) => { + AssocItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { + visit_defaultness(defaultness, visitor); visitor.visit_generics(generics); visit_fn_sig(sig, visitor); visit_opt(body, |body| visitor.visit_block(body)); } - AssocItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => { + AssocItemKind::TyAlias(box TyAlias { defaultness, generics, bounds, ty }) => { + visit_defaultness(defaultness, visitor); visitor.visit_generics(generics); visit_bounds(bounds, visitor); visit_opt(ty, |ty| visitor.visit_ty(ty)); @@ -1049,8 +1099,10 @@ } pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) { - let FnHeader { unsafety: _, asyncness, constness: _, ext: _ } = header; + let FnHeader { unsafety, asyncness, constness, ext: _ } = header; + visit_constness(constness, vis); vis.visit_asyncness(asyncness); + visit_unsafety(unsafety, vis); } // FIXME: Avoid visiting the crate as a `Mod` item, flat map only the inner items if possible, @@ -1060,7 +1112,7 @@ let item_vis = Visibility { kind: VisibilityKind::Public, span: span.shrink_to_lo(), tokens: None }; let item = P(Item { - ident: Ident::invalid(), + ident: Ident::empty(), attrs, id: DUMMY_NODE_ID, vis: item_vis, @@ -1116,12 +1168,14 @@ visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } - ForeignItemKind::Fn(box FnKind(_, sig, generics, body)) => { + ForeignItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { + visit_defaultness(defaultness, visitor); visitor.visit_generics(generics); visit_fn_sig(sig, visitor); visit_opt(body, |body| visitor.visit_block(body)); } - ForeignItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => { + ForeignItemKind::TyAlias(box TyAlias { defaultness, generics, bounds, ty }) => { + visit_defaultness(defaultness, visitor); visitor.visit_generics(generics); visit_bounds(bounds, visitor); visit_opt(ty, |ty| visitor.visit_ty(ty));
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 5d994db..51cabb5 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -221,12 +221,6 @@ for attr in &data.attrs { match attr.style { crate::AttrStyle::Outer => { - assert!( - inner_attrs.len() == 0, - "Found outer attribute {:?} after inner attrs {:?}", - attr, - inner_attrs - ); outer_attrs.push(attr); } crate::AttrStyle::Inner => {
diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs index 542a330..80a06fa 100644 --- a/compiler/rustc_ast/src/util/comments.rs +++ b/compiler/rustc_ast/src/util/comments.rs
@@ -38,7 +38,7 @@ i += 1; } // like the first, a last line of all stars should be omitted - if j > i && lines[j - 1].chars().skip(1).all(|c| c == '*') { + if j > i && !lines[j - 1].is_empty() && lines[j - 1].chars().all(|c| c == '*') { j -= 1; } @@ -169,7 +169,7 @@ if let Some(mut idx) = token_text.find('\n') { code_to_the_left = false; while let Some(next_newline) = &token_text[idx + 1..].find('\n') { - idx = idx + 1 + next_newline; + idx += 1 + next_newline; comments.push(Comment { style: CommentStyle::BlankLine, lines: vec![],
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 078dd4b..742a7d1 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs
@@ -212,7 +212,8 @@ /// parentheses while having a high degree of confidence on the correctness of the suggestion. pub fn can_continue_expr_unambiguously(&self) -> bool { use AssocOp::*; - match self { + matches!( + self, BitXor | // `{ 42 } ^ 3` Assign | // `{ 42 } = { 42 }` Divide | // `{ 42 } / 42` @@ -225,9 +226,8 @@ As | // `{ 42 } as usize` // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. - Colon => true, // `{ 42 }: usize` - _ => false, - } + Colon, // `{ 42 }: usize` + ) } } @@ -357,13 +357,13 @@ } } -/// In `let p = e`, operators with precedence `<=` this one requires parenthesis in `e`. +/// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`. pub fn prec_let_scrutinee_needs_par() -> usize { AssocOp::LAnd.precedence() } /// Suppose we have `let _ = e` and the `order` of `e`. -/// Is the `order` such that `e` in `let _ = e` needs parenthesis when it is on the RHS? +/// Is the `order` such that `e` in `let _ = e` needs parentheses when it is on the RHS? /// /// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`. /// Can we print this as `let _ = a OP b`?
diff --git a/compiler/rustc_ast/src/util/unicode.rs b/compiler/rustc_ast/src/util/unicode.rs new file mode 100644 index 0000000..f009f7b --- /dev/null +++ b/compiler/rustc_ast/src/util/unicode.rs
@@ -0,0 +1,35 @@ +pub const TEXT_FLOW_CONTROL_CHARS: &[char] = &[ + '\u{202A}', '\u{202B}', '\u{202D}', '\u{202E}', '\u{2066}', '\u{2067}', '\u{2068}', '\u{202C}', + '\u{2069}', +]; + +#[inline] +pub fn contains_text_flow_control_chars(s: &str) -> bool { + // Char - UTF-8 + // U+202A - E2 80 AA + // U+202B - E2 80 AB + // U+202C - E2 80 AC + // U+202D - E2 80 AD + // U+202E - E2 80 AE + // U+2066 - E2 81 A6 + // U+2067 - E2 81 A7 + // U+2068 - E2 81 A8 + // U+2069 - E2 81 A9 + let mut bytes = s.as_bytes(); + loop { + match core::slice::memchr::memchr(0xE2, &bytes) { + Some(idx) => { + // bytes are valid UTF-8 -> E2 must be followed by two bytes + let ch = &bytes[idx..idx + 3]; + match ch { + [_, 0x80, 0xAA..=0xAE] | [_, 0x81, 0xA6..=0xA9] => break true, + _ => {} + } + bytes = &bytes[idx + 3..]; + } + None => { + break false; + } + } + } +}
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index b380310..be794ed 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs
@@ -285,7 +285,7 @@ visitor.visit_ty(typ); walk_list!(visitor, visit_expr, expr); } - ItemKind::Fn(box FnKind(_, ref sig, ref generics, ref body)) => { + ItemKind::Fn(box Fn { defaultness: _, ref generics, ref sig, ref body }) => { visitor.visit_generics(generics); let kind = FnKind::Fn(FnCtxt::Free, item.ident, sig, &item.vis, body.as_deref()); visitor.visit_fn(kind, item.span, item.id) @@ -300,7 +300,7 @@ walk_list!(visitor, visit_foreign_item, &foreign_module.items); } ItemKind::GlobalAsm(ref asm) => walk_inline_asm(visitor, asm), - ItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref ty)) => { + ItemKind::TyAlias(box TyAlias { defaultness: _, ref generics, ref bounds, ref ty }) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_ty, ty); @@ -309,12 +309,12 @@ visitor.visit_generics(generics); visitor.visit_enum_def(enum_definition, generics, item.id, item.span) } - ItemKind::Impl(box ImplKind { - unsafety: _, - polarity: _, + ItemKind::Impl(box Impl { defaultness: _, - constness: _, + unsafety: _, ref generics, + constness: _, + polarity: _, ref of_trait, ref self_ty, ref items, @@ -329,7 +329,13 @@ visitor.visit_generics(generics); visitor.visit_variant_data(struct_definition); } - ItemKind::Trait(box TraitKind(.., ref generics, ref bounds, ref items)) => { + ItemKind::Trait(box Trait { + unsafety: _, + is_auto: _, + ref generics, + ref bounds, + ref items, + }) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait); @@ -547,12 +553,12 @@ visitor.visit_ty(ty); walk_list!(visitor, visit_expr, expr); } - ForeignItemKind::Fn(box FnKind(_, sig, generics, body)) => { + ForeignItemKind::Fn(box Fn { defaultness: _, ref generics, ref sig, ref body }) => { visitor.visit_generics(generics); let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, body.as_deref()); visitor.visit_fn(kind, span, id); } - ForeignItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => { + ForeignItemKind::TyAlias(box TyAlias { defaultness: _, generics, bounds, ty }) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_ty, ty); @@ -653,12 +659,12 @@ visitor.visit_ty(ty); walk_list!(visitor, visit_expr, expr); } - AssocItemKind::Fn(box FnKind(_, sig, generics, body)) => { + AssocItemKind::Fn(box Fn { defaultness: _, ref generics, ref sig, ref body }) => { visitor.visit_generics(generics); let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, body.as_deref()); visitor.visit_fn(kind, span, id); } - AssocItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => { + AssocItemKind::TyAlias(box TyAlias { defaultness: _, generics, bounds, ty }) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_ty, ty);
diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index f4859ee..7989af2 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml
@@ -14,6 +14,7 @@ rustc_target = { path = "../rustc_target" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } +rustc_query_system = { path = "../rustc_query_system" } rustc_span = { path = "../rustc_span" } rustc_errors = { path = "../rustc_errors" } rustc_session = { path = "../rustc_session" }
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 957b14f..cfa97ff 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -2,22 +2,45 @@ use rustc_ast::*; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_set::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_span::{Span, Symbol}; +use rustc_session::parse::feature_err; +use rustc_span::{sym, Span, Symbol}; use rustc_target::asm; use std::collections::hash_map::Entry; use std::fmt::Write; impl<'a, 'hir> LoweringContext<'a, 'hir> { crate fn lower_inline_asm(&mut self, sp: Span, asm: &InlineAsm) -> &'hir hir::InlineAsm<'hir> { - // Rustdoc needs to support asm! from foriegn architectures: don't try - // lowering the register contraints in this case. + // 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(); } + if let Some(asm_arch) = asm_arch { + // Inline assembly is currently only stable for these architectures. + let is_stable = matches!( + asm_arch, + asm::InlineAsmArch::X86 + | asm::InlineAsmArch::X86_64 + | asm::InlineAsmArch::Arm + | asm::InlineAsmArch::AArch64 + | asm::InlineAsmArch::RiscV32 + | asm::InlineAsmArch::RiscV64 + ); + if !is_stable && !self.sess.features_untracked().asm_experimental_arch { + feature_err( + &self.sess.parse_sess, + sym::asm_experimental_arch, + sp, + "inline assembly is not stable yet on this architecture", + ) + .emit(); + } + } if asm.options.contains(InlineAsmOptions::ATT_SYNTAX) && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64)) && !self.sess.opts.actually_rustdoc @@ -27,22 +50,47 @@ .emit(); } - let mut clobber_abi = None; + let mut clobber_abis = FxHashMap::default(); if let Some(asm_arch) = asm_arch { - if let Some((abi_name, abi_span)) = asm.clobber_abi { - match asm::InlineAsmClobberAbi::parse(asm_arch, &self.sess.target, abi_name) { - Ok(abi) => clobber_abi = Some((abi, abi_span)), + for (abi_name, abi_span) in &asm.clobber_abis { + match asm::InlineAsmClobberAbi::parse(asm_arch, &self.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( + *abi_span, + &format!("`{}` ABI specified multiple times", prev_name), + ); + err.span_label(*prev_sp, "previously specified here"); + + // 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(); + if source_map.span_to_snippet(*prev_sp) + != source_map.span_to_snippet(*abi_span) + { + err.note("these ABIs are equivalent on the current target"); + } + + err.emit(); + } + None => { + clobber_abis.insert(abi, (abi_name, *abi_span)); + } + } + } Err(&[]) => { self.sess .struct_span_err( - abi_span, + *abi_span, "`clobber_abi` is not supported on this target", ) .emit(); } Err(supported_abis) => { let mut err = - self.sess.struct_span_err(abi_span, "invalid ABI for `clobber_abi`"); + self.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); @@ -121,10 +169,30 @@ out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)), } } - InlineAsmOperand::Const { ref anon_const } => hir::InlineAsmOperand::Const { - anon_const: self.lower_anon_const(anon_const), - }, + InlineAsmOperand::Const { ref anon_const } => { + if !self.sess.features_untracked().asm_const { + feature_err( + &self.sess.parse_sess, + sym::asm_const, + *op_sp, + "const operands for inline assembly are unstable", + ) + .emit(); + } + hir::InlineAsmOperand::Const { + anon_const: self.lower_anon_const(anon_const), + } + } InlineAsmOperand::Sym { ref expr } => { + if !self.sess.features_untracked().asm_sym { + feature_err( + &self.sess.parse_sess, + sym::asm_sym, + *op_sp, + "sym operands for inline assembly are unstable", + ) + .emit(); + } hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) } } }; @@ -214,9 +282,7 @@ // means that we disallow passing a value in/out of the asm and // require that the operand name an explicit register, not a // register class. - if reg_class.is_clobber_only(asm_arch.unwrap()) - && !(op.is_clobber() && matches!(reg, asm::InlineAsmRegOrRegClass::Reg(_))) - { + if reg_class.is_clobber_only(asm_arch.unwrap()) && !op.is_clobber() { let msg = format!( "register class `{}` can only be used as a clobber, \ not as an input or output", @@ -308,8 +374,14 @@ // If a clobber_abi is specified, add the necessary clobbers to the // operands list. - if let Some((abi, abi_span)) = clobber_abi { + let mut clobbered = FxHashSet::default(); + for (abi, (_, abi_span)) in clobber_abis { for &clobber in abi.clobbered_regs() { + // Don't emit a clobber for a register already clobbered + if clobbered.contains(&clobber) { + continue; + } + let mut output_used = false; clobber.overlapping_regs(|reg| { if used_output_regs.contains_key(®) { @@ -326,6 +398,7 @@ }, self.lower_span(abi_span), )); + clobbered.insert(clobber); } } }
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 6027027..9c57920 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -13,7 +13,7 @@ use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP}; +use rustc_span::DUMMY_SP; impl<'hir> LoweringContext<'_, 'hir> { fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] { @@ -252,9 +252,10 @@ } // Merge attributes into the inner expression. if !e.attrs.is_empty() { - let old_attrs = self.attrs.get(&ex.hir_id).map(|la| *la).unwrap_or(&[]); + let old_attrs = + self.attrs.get(&ex.hir_id.local_id).map(|la| *la).unwrap_or(&[]); self.attrs.insert( - ex.hir_id, + ex.hir_id.local_id, &*self.arena.alloc_from_iter( e.attrs .iter() @@ -914,14 +915,22 @@ ); } if !self.sess.features_untracked().destructuring_assignment { - feature_err( + let mut err = feature_err( &self.sess.parse_sess, sym::destructuring_assignment, eq_sign_span, "destructuring assignments are unstable", - ) - .span_label(lhs.span, "cannot assign to this expression") - .emit(); + ); + err.span_label(lhs.span, "cannot assign to this expression"); + if self.is_in_loop_condition { + err.span_suggestion_verbose( + lhs.span.shrink_to_lo(), + "you might have meant to use pattern destructuring", + "let ".to_string(), + rustc_errors::Applicability::MachineApplicable, + ); + } + err.emit(); } let mut assignments = vec![]; @@ -1307,16 +1316,13 @@ /// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into: /// ```rust /// { - /// let result = match ::std::iter::IntoIterator::into_iter(<head>) { + /// let result = match IntoIterator::into_iter(<head>) { /// mut iter => { /// [opt_ident]: loop { - /// let mut __next; - /// match ::std::iter::Iterator::next(&mut iter) { - /// ::std::option::Option::Some(val) => __next = val, - /// ::std::option::Option::None => break + /// match Iterator::next(&mut iter) { + /// None => break, + /// Some(<pat>) => <body>, /// }; - /// let <pat> = __next; - /// StmtKind::Expr(<body>); /// } /// } /// }; @@ -1331,136 +1337,75 @@ body: &Block, opt_label: Option<Label>, ) -> hir::Expr<'hir> { - let orig_head_span = head.span; - // expand <head> - let mut head = self.lower_expr_mut(head); - let desugared_span = self.mark_span_with_reason( - DesugaringKind::ForLoop(ForLoopLoc::Head), - orig_head_span, - None, - ); - head.span = self.lower_span(desugared_span); + let head = self.lower_expr_mut(head); + let pat = self.lower_pat(pat); + let for_span = + self.mark_span_with_reason(DesugaringKind::ForLoop, self.lower_span(e.span), None); + let head_span = self.mark_span_with_reason(DesugaringKind::ForLoop, head.span, None); + let pat_span = self.mark_span_with_reason(DesugaringKind::ForLoop, pat.span, None); - let iter = Ident::with_dummy_span(sym::iter); - - let next_ident = Ident::with_dummy_span(sym::__next); - let (next_pat, next_pat_hid) = self.pat_ident_binding_mode( - desugared_span, - next_ident, - hir::BindingAnnotation::Mutable, - ); - - // `::std::option::Option::Some(val) => __next = val` - let pat_arm = { - let val_ident = Ident::with_dummy_span(sym::val); - let (val_pat, val_pat_hid) = self.pat_ident(pat.span, val_ident); - let val_expr = self.expr_ident(pat.span, val_ident, val_pat_hid); - let next_expr = self.expr_ident(pat.span, next_ident, next_pat_hid); - let assign = self.arena.alloc(self.expr( - pat.span, - hir::ExprKind::Assign(next_expr, val_expr, self.lower_span(pat.span)), - ThinVec::new(), - )); - let some_pat = self.pat_some(pat.span, val_pat); - self.arm(some_pat, assign) - }; - - // `::std::option::Option::None => break` - let break_arm = { + // `None => break` + let none_arm = { let break_expr = - self.with_loop_scope(e.id, |this| this.expr_break_alloc(e.span, ThinVec::new())); - let pat = self.pat_none(e.span); + self.with_loop_scope(e.id, |this| this.expr_break_alloc(for_span, ThinVec::new())); + let pat = self.pat_none(for_span); self.arm(pat, break_expr) }; - // `mut iter` - let (iter_pat, iter_pat_nid) = - self.pat_ident_binding_mode(desugared_span, iter, hir::BindingAnnotation::Mutable); + // Some(<pat>) => <body>, + let some_arm = { + let some_pat = self.pat_some(pat_span, pat); + let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false)); + let body_expr = self.arena.alloc(self.expr_block(body_block, ThinVec::new())); + self.arm(some_pat, body_expr) + }; - // `match ::std::iter::Iterator::next(&mut iter) { ... }` + // `mut iter` + let iter = Ident::with_dummy_span(sym::iter); + let (iter_pat, iter_pat_nid) = + self.pat_ident_binding_mode(head_span, iter, hir::BindingAnnotation::Mutable); + + // `match Iterator::next(&mut iter) { ... }` let match_expr = { - let iter = self.expr_ident(desugared_span, iter, iter_pat_nid); - let ref_mut_iter = self.expr_mut_addr_of(desugared_span, iter); + let iter = self.expr_ident(head_span, iter, iter_pat_nid); + let ref_mut_iter = self.expr_mut_addr_of(head_span, iter); let next_expr = self.expr_call_lang_item_fn( - desugared_span, + head_span, hir::LangItem::IteratorNext, arena_vec![self; ref_mut_iter], ); - let arms = arena_vec![self; pat_arm, break_arm]; + let arms = arena_vec![self; none_arm, some_arm]; - self.expr_match(desugared_span, next_expr, arms, hir::MatchSource::ForLoopDesugar) + self.expr_match(head_span, next_expr, arms, hir::MatchSource::ForLoopDesugar) }; - let match_stmt = self.stmt_expr(desugared_span, match_expr); + let match_stmt = self.stmt_expr(for_span, match_expr); - let next_expr = self.expr_ident(desugared_span, next_ident, next_pat_hid); - - // `let mut __next` - let next_let = self.stmt_let_pat( - None, - desugared_span, - None, - next_pat, - hir::LocalSource::ForLoopDesugar, - ); - - // `let <pat> = __next` - let pat = self.lower_pat(pat); - let pat_let = self.stmt_let_pat( - None, - desugared_span, - Some(next_expr), - pat, - hir::LocalSource::ForLoopDesugar, - ); - - let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false)); - let body_expr = self.expr_block(body_block, ThinVec::new()); - let body_stmt = self.stmt_expr(body.span, body_expr); - - let loop_block = self.block_all( - e.span, - arena_vec![self; next_let, match_stmt, pat_let, body_stmt], - None, - ); + let loop_block = self.block_all(for_span, arena_vec![self; match_stmt], None); // `[opt_ident]: loop { ... }` let kind = hir::ExprKind::Loop( loop_block, self.lower_label(opt_label), hir::LoopSource::ForLoop, - self.lower_span(e.span.with_hi(orig_head_span.hi())), + self.lower_span(for_span.with_hi(head.span.hi())), ); - let loop_expr = self.arena.alloc(hir::Expr { - hir_id: self.lower_node_id(e.id), - kind, - span: self.lower_span(e.span), - }); + let loop_expr = + self.arena.alloc(hir::Expr { hir_id: self.lower_node_id(e.id), kind, span: for_span }); // `mut iter => { ... }` let iter_arm = self.arm(iter_pat, loop_expr); - let into_iter_span = self.mark_span_with_reason( - DesugaringKind::ForLoop(ForLoopLoc::IntoIter), - orig_head_span, - None, - ); - // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }` let into_iter_expr = { self.expr_call_lang_item_fn( - into_iter_span, + head_span, hir::LangItem::IntoIterIntoIter, arena_vec![self; head], ) }; - // #82462: to correctly diagnose borrow errors, the block that contains - // the iter expr needs to have a span that covers the loop body. - let desugared_full_span = - self.mark_span_with_reason(DesugaringKind::ForLoop(ForLoopLoc::Head), e.span, None); - let match_expr = self.arena.alloc(self.expr_match( - desugared_full_span, + for_span, into_iter_expr, arena_vec![self; iter_arm], hir::MatchSource::ForLoopDesugar, @@ -1474,7 +1419,7 @@ // surrounding scope of the `match` since the `match` is not a terminating scope. // // Also, add the attributes to the outer returned expr node. - self.expr_drop_temps_mut(desugared_full_span, match_expr, attrs.into()) + self.expr_drop_temps_mut(for_span, match_expr, attrs.into()) } /// Desugar `ExprKind::Try` from: `<expr>?` into:
diff --git a/compiler/rustc_middle/src/hir/map/collector.rs b/compiler/rustc_ast_lowering/src/index.rs similarity index 61% rename from compiler/rustc_middle/src/hir/map/collector.rs rename to compiler/rustc_ast_lowering/src/index.rs index efebf73..8a9dad2 100644 --- a/compiler/rustc_middle/src/hir/map/collector.rs +++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -1,187 +1,107 @@ -use crate::arena::Arena; -use crate::hir::map::Map; -use crate::hir::{IndexedHir, OwnerNodes, ParentedNode}; -use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sorted_map::SortedMap; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::definitions; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::*; use rustc_index::vec::{Idx, IndexVec}; -use rustc_query_system::ich::StableHashingContext; use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::{Span, DUMMY_SP}; -use std::iter::repeat; +use tracing::debug; /// A visitor that walks over the HIR and collects `Node`s into a HIR map. pub(super) struct NodeCollector<'a, 'hir> { - arena: &'hir Arena<'hir>, - - /// The crate - krate: &'hir Crate<'hir>, - /// Source map source_map: &'a SourceMap, + bodies: &'a SortedMap<ItemLocalId, &'hir Body<'hir>>, - map: IndexVec<LocalDefId, Option<&'hir mut OwnerNodes<'hir>>>, - parenting: FxHashMap<LocalDefId, HirId>, + /// Outputs + nodes: IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, + parenting: FxHashMap<LocalDefId, ItemLocalId>, /// The parent of this node - parent_node: hir::HirId, + parent_node: hir::ItemLocalId, - current_dep_node_owner: LocalDefId, + owner: LocalDefId, definitions: &'a definitions::Definitions, - - hcx: StableHashingContext<'a>, } -fn insert_vec_map<K: Idx, V: Clone>(map: &mut IndexVec<K, Option<V>>, k: K, v: V) { - let i = k.index(); - let len = map.len(); - if i >= len { - map.extend(repeat(None).take(i - len + 1)); - } - debug_assert!(map[k].is_none()); - map[k] = Some(v); -} +pub(super) fn index_hir<'hir>( + sess: &Session, + definitions: &definitions::Definitions, + item: hir::OwnerNode<'hir>, + bodies: &SortedMap<ItemLocalId, &'hir Body<'hir>>, +) -> (IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, FxHashMap<LocalDefId, ItemLocalId>) { + let mut nodes = IndexVec::new(); + // This node's parent should never be accessed: the owner's parent is computed by the + // hir_owner_parent query. Make it invalid (= ItemLocalId::MAX) to force an ICE whenever it is + // used. + nodes.push(Some(ParentedNode { parent: ItemLocalId::INVALID, node: item.into() })); + let mut collector = NodeCollector { + source_map: sess.source_map(), + definitions, + owner: item.def_id(), + parent_node: ItemLocalId::new(0), + nodes, + bodies, + parenting: FxHashMap::default(), + }; -fn hash_body( - hcx: &mut StableHashingContext<'_>, - item_like: impl for<'a> HashStable<StableHashingContext<'a>>, -) -> Fingerprint { - let mut stable_hasher = StableHasher::new(); - hcx.while_hashing_hir_bodies(true, |hcx| { - item_like.hash_stable(hcx, &mut stable_hasher); - }); - stable_hasher.finish() + match item { + OwnerNode::Crate(citem) => collector.visit_mod(&citem, citem.inner, hir::CRATE_HIR_ID), + OwnerNode::Item(item) => collector.visit_item(item), + OwnerNode::TraitItem(item) => collector.visit_trait_item(item), + OwnerNode::ImplItem(item) => collector.visit_impl_item(item), + OwnerNode::ForeignItem(item) => collector.visit_foreign_item(item), + }; + + (collector.nodes, collector.parenting) } impl<'a, 'hir> NodeCollector<'a, 'hir> { - pub(super) fn root( - sess: &'a Session, - arena: &'hir Arena<'hir>, - krate: &'hir Crate<'hir>, - definitions: &'a definitions::Definitions, - hcx: StableHashingContext<'a>, - ) -> NodeCollector<'a, 'hir> { - let mut collector = NodeCollector { - arena, - krate, - source_map: sess.source_map(), - parent_node: hir::CRATE_HIR_ID, - current_dep_node_owner: CRATE_DEF_ID, - definitions, - hcx, - map: IndexVec::from_fn_n(|_| None, definitions.def_index_count()), - parenting: FxHashMap::default(), - }; - collector.insert_owner(CRATE_DEF_ID, OwnerNode::Crate(krate.module())); - - collector - } - - pub(super) fn finalize_and_compute_crate_hash(mut self) -> IndexedHir<'hir> { - // Insert bodies into the map - for (id, body) in self.krate.bodies.iter() { - let bodies = &mut self.map[id.hir_id.owner].as_mut().unwrap().bodies; - assert!(bodies.insert(id.hir_id.local_id, body).is_none()); - } - IndexedHir { map: self.map, parenting: self.parenting } - } - - fn insert_owner(&mut self, owner: LocalDefId, node: OwnerNode<'hir>) { - let hash = hash_body(&mut self.hcx, node); - - let mut nodes = IndexVec::new(); - nodes.push(Some(ParentedNode { parent: ItemLocalId::new(0), node: node.into() })); - - debug_assert!(self.map[owner].is_none()); - self.map[owner] = - Some(self.arena.alloc(OwnerNodes { hash, nodes, bodies: FxHashMap::default() })); - } - fn insert(&mut self, span: Span, hir_id: HirId, node: Node<'hir>) { - debug_assert_eq!(self.current_dep_node_owner, hir_id.owner); + debug_assert_eq!(self.owner, hir_id.owner); debug_assert_ne!(hir_id.local_id.as_u32(), 0); // Make sure that the DepNode of some node coincides with the HirId // owner of that node. if cfg!(debug_assertions) { - if hir_id.owner != self.current_dep_node_owner { - let node_str = match self.definitions.opt_hir_id_to_local_def_id(hir_id) { - Some(def_id) => self.definitions.def_path(def_id).to_string_no_crate_verbose(), - None => format!("{:?}", node), - }; - - span_bug!( - span, - "inconsistent DepNode at `{:?}` for `{}`: \ + if hir_id.owner != self.owner { + panic!( + "inconsistent DepNode at `{:?}` for `{:?}`: \ current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})", self.source_map.span_to_diagnostic_string(span), - node_str, - self.definitions - .def_path(self.current_dep_node_owner) - .to_string_no_crate_verbose(), - self.current_dep_node_owner, + node, + self.definitions.def_path(self.owner).to_string_no_crate_verbose(), + self.owner, self.definitions.def_path(hir_id.owner).to_string_no_crate_verbose(), hir_id.owner, ) } } - let nodes = self.map[hir_id.owner].as_mut().unwrap(); - - debug_assert_eq!(self.parent_node.owner, self.current_dep_node_owner); - insert_vec_map( - &mut nodes.nodes, - hir_id.local_id, - ParentedNode { parent: self.parent_node.local_id, node: node }, - ); + self.nodes.insert(hir_id.local_id, ParentedNode { parent: self.parent_node, node: node }); } fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_node_id: HirId, f: F) { + debug_assert_eq!(parent_node_id.owner, self.owner); let parent_node = self.parent_node; - self.parent_node = parent_node_id; + self.parent_node = parent_node_id.local_id; f(self); self.parent_node = parent_node; } - fn with_dep_node_owner(&mut self, dep_node_owner: LocalDefId, f: impl FnOnce(&mut Self)) { - let prev_owner = self.current_dep_node_owner; - let prev_parent = self.parent_node; - - self.current_dep_node_owner = dep_node_owner; - self.parent_node = HirId::make_owner(dep_node_owner); - f(self); - self.current_dep_node_owner = prev_owner; - self.parent_node = prev_parent; - } - fn insert_nested(&mut self, item: LocalDefId) { - #[cfg(debug_assertions)] - { - let dk_parent = self.definitions.def_key(item).parent.unwrap(); - let dk_parent = LocalDefId { local_def_index: dk_parent }; - let dk_parent = self.definitions.local_def_id_to_hir_id(dk_parent); - debug_assert_eq!( - dk_parent.owner, self.parent_node.owner, - "Different parents for {:?}", - item - ) - } - - assert_eq!(self.parenting.insert(item, self.parent_node), None); + self.parenting.insert(item, self.parent_node); } } impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { - type Map = Map<'hir>; + type Map = !; /// Because we want to track parent items and so forth, enable /// deep walking so that we walk nested items in the context of @@ -194,26 +114,24 @@ fn visit_nested_item(&mut self, item: ItemId) { debug!("visit_nested_item: {:?}", item); self.insert_nested(item.def_id); - self.visit_item(self.krate.item(item)); } fn visit_nested_trait_item(&mut self, item_id: TraitItemId) { self.insert_nested(item_id.def_id); - self.visit_trait_item(self.krate.trait_item(item_id)); } fn visit_nested_impl_item(&mut self, item_id: ImplItemId) { self.insert_nested(item_id.def_id); - self.visit_impl_item(self.krate.impl_item(item_id)); } fn visit_nested_foreign_item(&mut self, foreign_id: ForeignItemId) { self.insert_nested(foreign_id.def_id); - self.visit_foreign_item(self.krate.foreign_item(foreign_id)); } fn visit_nested_body(&mut self, id: BodyId) { - self.visit_body(self.krate.body(id)); + debug_assert_eq!(id.hir_id.owner, self.owner); + let body = self.bodies[&id.hir_id.local_id]; + self.visit_body(body); } fn visit_param(&mut self, param: &'hir Param<'hir>) { @@ -226,8 +144,8 @@ fn visit_item(&mut self, i: &'hir Item<'hir>) { debug!("visit_item: {:?}", i); - self.insert_owner(i.def_id, OwnerNode::Item(i)); - self.with_dep_node_owner(i.def_id, |this| { + debug_assert_eq!(i.def_id, self.owner); + self.with_parent(i.hir_id(), |this| { if let ItemKind::Struct(ref struct_def, _) = i.kind { // If this is a tuple or unit-like struct, register the constructor. if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { @@ -239,8 +157,8 @@ } fn visit_foreign_item(&mut self, fi: &'hir ForeignItem<'hir>) { - self.insert_owner(fi.def_id, OwnerNode::ForeignItem(fi)); - self.with_dep_node_owner(fi.def_id, |this| { + debug_assert_eq!(fi.def_id, self.owner); + self.with_parent(fi.hir_id(), |this| { intravisit::walk_foreign_item(this, fi); }); } @@ -257,15 +175,15 @@ } fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) { - self.insert_owner(ti.def_id, OwnerNode::TraitItem(ti)); - self.with_dep_node_owner(ti.def_id, |this| { + debug_assert_eq!(ti.def_id, self.owner); + self.with_parent(ti.hir_id(), |this| { intravisit::walk_trait_item(this, ti); }); } fn visit_impl_item(&mut self, ii: &'hir ImplItem<'hir>) { - self.insert_owner(ii.def_id, OwnerNode::ImplItem(ii)); - self.with_dep_node_owner(ii.def_id, |this| { + debug_assert_eq!(ii.def_id, self.owner); + self.with_parent(ii.hir_id(), |this| { intravisit::walk_impl_item(this, ii); }); } @@ -353,7 +271,8 @@ s: Span, id: HirId, ) { - assert_eq!(self.parent_node, id); + assert_eq!(self.owner, id.owner); + assert_eq!(self.parent_node, id.local_id); intravisit::walk_fn(self, fk, fd, b, s, id); }
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index a5a4de8..692f393 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -10,6 +10,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::LocalDefId; +use rustc_index::vec::Idx; use rustc_span::source_map::{respan, DesugaringKind}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; @@ -39,6 +40,11 @@ } impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> { + fn visit_attribute(&mut self, _: &'a Attribute) { + // We do not want to lower expressions that appear in attributes, + // as they are not accessible to the rest of the HIR. + } + fn visit_item(&mut self, item: &'a Item) { let hir_id = self.lctx.with_hir_id_owner(item.id, |lctx| { let node = lctx.without_in_scope_lifetime_defs(|lctx| lctx.lower_item(item)); @@ -48,7 +54,7 @@ self.lctx.with_parent_item_lifetime_defs(hir_id, |this| { let this = &mut ItemLowerer { lctx: this }; match item.kind { - ItemKind::Impl(box ImplKind { ref of_trait, .. }) => { + ItemKind::Impl(box Impl { ref of_trait, .. }) => { this.with_trait_impl_ref(of_trait, |this| visit::walk_item(this, item)); } _ => visit::walk_item(this, item), @@ -99,11 +105,12 @@ ) -> T { let old_len = self.in_scope_lifetimes.len(); - let parent_generics = match self.owners[parent_hir_id].unwrap().expect_item().kind { - hir::ItemKind::Impl(hir::Impl { ref generics, .. }) - | hir::ItemKind::Trait(_, _, ref generics, ..) => generics.params, - _ => &[], - }; + let parent_generics = + match self.owners[parent_hir_id].as_ref().unwrap().node().expect_item().kind { + hir::ItemKind::Impl(hir::Impl { ref generics, .. }) + | hir::ItemKind::Trait(_, _, ref generics, ..) => generics.params, + _ => &[], + }; let lt_def_names = parent_generics.iter().filter_map(|param| match param.kind { hir::GenericParamKind::Lifetime { .. } => Some(param.name.normalize_to_macros_2_0()), _ => None, @@ -216,12 +223,12 @@ let (ty, body_id) = self.lower_const_item(t, span, e.as_deref()); hir::ItemKind::Const(ty, body_id) } - ItemKind::Fn(box FnKind( - _, - FnSig { ref decl, header, span: fn_sig_span }, + ItemKind::Fn(box Fn { + sig: FnSig { ref decl, header, span: fn_sig_span }, ref generics, ref body, - )) => { + .. + }) => { let fn_def_id = self.resolver.local_def_id(id); self.with_new_scopes(|this| { this.current_item = Some(ident.span); @@ -271,7 +278,7 @@ ItemKind::GlobalAsm(ref asm) => { hir::ItemKind::GlobalAsm(self.lower_inline_asm(span, asm)) } - ItemKind::TyAlias(box TyAliasKind(_, ref gen, _, Some(ref ty))) => { + ItemKind::TyAlias(box TyAlias { ref generics, ty: Some(ref ty), .. }) => { // We lower // // type Foo = impl Trait @@ -286,10 +293,10 @@ capturable_lifetimes: &mut FxHashSet::default(), }, ); - let generics = self.lower_generics(gen, ImplTraitContext::disallowed()); + let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); hir::ItemKind::TyAlias(ty, generics) } - ItemKind::TyAlias(box TyAliasKind(_, ref generics, _, None)) => { + ItemKind::TyAlias(box TyAlias { ref generics, ty: None, .. }) => { let ty = self.arena.alloc(self.ty(span, hir::TyKind::Err)); let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); hir::ItemKind::TyAlias(ty, generics) @@ -316,7 +323,7 @@ self.lower_generics(generics, ImplTraitContext::disallowed()), ) } - ItemKind::Impl(box ImplKind { + ItemKind::Impl(box Impl { unsafety, polarity, defaultness, @@ -382,13 +389,13 @@ items: new_impl_items, }) } - ItemKind::Trait(box TraitKind( + ItemKind::Trait(box Trait { is_auto, unsafety, ref generics, ref bounds, ref items, - )) => { + }) => { let bounds = self.lower_param_bounds(bounds, ImplTraitContext::disallowed()); let items = self .arena @@ -493,7 +500,7 @@ let kind = hir::ItemKind::Use(path, hir::UseKind::Single); let vis = this.rebuild_vis(&vis); if let Some(attrs) = attrs { - this.attrs.insert(hir::HirId::make_owner(new_id), attrs); + this.attrs.insert(hir::ItemLocalId::new(0), attrs); } let item = hir::Item { @@ -568,7 +575,7 @@ let kind = this.lower_use_tree(use_tree, &prefix, id, &mut vis, &mut ident, attrs); if let Some(attrs) = attrs { - this.attrs.insert(hir::HirId::make_owner(new_hir_id), attrs); + this.attrs.insert(hir::ItemLocalId::new(0), attrs); } let item = hir::Item { @@ -653,7 +660,7 @@ def_id, ident: self.lower_ident(i.ident), kind: match i.kind { - ForeignItemKind::Fn(box FnKind(_, ref sig, ref generics, _)) => { + ForeignItemKind::Fn(box Fn { ref sig, ref generics, .. }) => { let fdec = &sig.decl; let (generics, (fn_dec, fn_args)) = self.add_in_band_defs( generics, @@ -770,13 +777,13 @@ let body = default.as_ref().map(|x| self.lower_const_body(i.span, Some(x))); (hir::Generics::empty(), hir::TraitItemKind::Const(ty, body)) } - AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, None)) => { + AssocItemKind::Fn(box Fn { ref sig, ref generics, body: None, .. }) => { let names = self.lower_fn_params_to_names(&sig.decl); let (generics, sig) = self.lower_method_sig(generics, sig, trait_item_def_id, false, None); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names))) } - AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, Some(ref body))) => { + AssocItemKind::Fn(box Fn { ref sig, ref generics, body: Some(ref body), .. }) => { let asyncness = sig.header.asyncness; let body_id = self.lower_maybe_async_body(i.span, &sig.decl, asyncness, Some(&body)); @@ -789,8 +796,8 @@ ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id))) } - AssocItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref default)) => { - let ty = default.as_ref().map(|x| self.lower_ty(x, ImplTraitContext::disallowed())); + AssocItemKind::TyAlias(box TyAlias { ref generics, ref bounds, ref ty, .. }) => { + let ty = ty.as_ref().map(|x| self.lower_ty(x, ImplTraitContext::disallowed())); let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); let kind = hir::TraitItemKind::Type( self.lower_param_bounds(bounds, ImplTraitContext::disallowed()), @@ -816,11 +823,11 @@ fn lower_trait_item_ref(&mut self, i: &AssocItem) -> hir::TraitItemRef { let (kind, has_default) = match &i.kind { AssocItemKind::Const(_, _, default) => (hir::AssocItemKind::Const, default.is_some()), - AssocItemKind::TyAlias(box TyAliasKind(_, _, _, default)) => { - (hir::AssocItemKind::Type, default.is_some()) + AssocItemKind::TyAlias(box TyAlias { ty, .. }) => { + (hir::AssocItemKind::Type, ty.is_some()) } - AssocItemKind::Fn(box FnKind(_, sig, _, default)) => { - (hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }, default.is_some()) + AssocItemKind::Fn(box Fn { sig, body, .. }) => { + (hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }, body.is_some()) } AssocItemKind::MacCall(..) => unimplemented!(), }; @@ -851,7 +858,7 @@ hir::ImplItemKind::Const(ty, self.lower_const_body(i.span, expr.as_deref())), ) } - AssocItemKind::Fn(box FnKind(_, sig, generics, body)) => { + AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => { self.current_item = Some(i.span); let asyncness = sig.header.asyncness; let body_id = @@ -867,7 +874,7 @@ (generics, hir::ImplItemKind::Fn(sig, body_id)) } - AssocItemKind::TyAlias(box TyAliasKind(_, generics, _, ty)) => { + AssocItemKind::TyAlias(box TyAlias { generics, ty, .. }) => { let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); let kind = match ty { None => { @@ -918,7 +925,7 @@ kind: match &i.kind { AssocItemKind::Const(..) => hir::AssocItemKind::Const, AssocItemKind::TyAlias(..) => hir::AssocItemKind::Type, - AssocItemKind::Fn(box FnKind(_, sig, ..)) => { + AssocItemKind::Fn(box Fn { sig, .. }) => { hir::AssocItemKind::Fn { has_self: sig.decl.has_self() } } AssocItemKind::MacCall(..) => unimplemented!(), @@ -971,7 +978,8 @@ ) -> hir::BodyId { let body = hir::Body { generator_kind: self.generator_kind, params, value }; let id = body.id(); - self.bodies.insert(id, body); + debug_assert_eq!(id.hir_id.owner, self.current_hir_id_owner); + self.bodies.push((id.hir_id.local_id, self.arena.alloc(body))); id } @@ -1124,7 +1132,7 @@ // // If this is the simple case, this parameter will end up being the same as the // original parameter, but with a different pattern id. - let stmt_attrs = this.attrs.get(¶meter.hir_id).copied(); + let stmt_attrs = this.attrs.get(¶meter.hir_id.local_id).copied(); let (new_parameter_pat, new_parameter_id) = this.pat_ident(desugared_span, ident); let new_parameter = hir::Param { hir_id: parameter.hir_id,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 245199e..2b3a538 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -33,16 +33,19 @@ #![feature(crate_visibility_modifier)] #![feature(box_patterns)] #![feature(iter_zip)] +#![feature(never_type)] #![recursion_limit = "256"] -use rustc_ast::node_id::NodeMap; use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree}; use rustc_ast::visit; use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::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_hir as hir; @@ -52,18 +55,18 @@ use rustc_hir::intravisit; use rustc_hir::{ConstArg, GenericArg, InferKind, ParamName}; use rustc_index::vec::{Idx, IndexVec}; +use rustc_query_system::ich::StableHashingContext; use rustc_session::lint::builtin::BARE_TRAIT_OBJECTS; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::utils::{FlattenNonterminals, NtToTokenstream}; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::ExpnId; -use rustc_span::source_map::{respan, CachingSourceMapView, DesugaringKind}; +use rustc_span::source_map::{respan, DesugaringKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use smallvec::SmallVec; -use std::collections::BTreeMap; use tracing::{debug, trace}; macro_rules! arena_vec { @@ -76,11 +79,12 @@ mod asm; mod block; mod expr; +mod index; mod item; mod pat; mod path; -rustc_hir::arena_types!(rustc_arena::declare_arena, 'tcx); +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. @@ -97,13 +101,14 @@ arena: &'hir Arena<'hir>, /// The items being lowered are collected here. - owners: IndexVec<LocalDefId, Option<hir::OwnerNode<'hir>>>, - bodies: BTreeMap<hir::BodyId, hir::Body<'hir>>, + owners: IndexVec<LocalDefId, Option<hir::OwnerInfo<'hir>>>, + /// Bodies inside the owner being lowered. + bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>, + /// Attributes inside the owner being lowered. + attrs: SortedMap<hir::ItemLocalId, &'hir [Attribute]>, generator_kind: Option<hir::GeneratorKind>, - attrs: BTreeMap<hir::HirId, &'hir [Attribute]>, - /// When inside an `async` context, this is the `HirId` of the /// `task_context` local bound to the resume argument of the generator. task_context: Option<hir::HirId>, @@ -152,6 +157,9 @@ item_local_id_counter: hir::ItemLocalId, node_id_to_hir_id: IndexVec<NodeId, Option<hir::HirId>>, + /// NodeIds that are lowered inside the current HIR owner. + local_node_ids: Vec<NodeId>, + allow_try_trait: Option<Lrc<[Symbol]>>, allow_gen_future: Option<Lrc<[Symbol]>>, } @@ -178,11 +186,13 @@ /// This should only return `None` during testing. fn definitions(&mut self) -> &mut Definitions; + fn create_stable_hashing_context(&self) -> StableHashingContext<'_>; + fn lint_buffer(&mut self) -> &mut LintBuffer; fn next_node_id(&mut self) -> NodeId; - fn take_trait_map(&mut self) -> NodeMap<Vec<hir::TraitCandidate>>; + fn take_trait_map(&mut self, node: NodeId) -> Option<Vec<hir::TraitCandidate>>; fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId>; @@ -200,37 +210,6 @@ ) -> LocalDefId; } -struct LoweringHasher<'a> { - source_map: CachingSourceMapView<'a>, - resolver: &'a dyn ResolverAstLowering, -} - -impl<'a> rustc_span::HashStableContext for LoweringHasher<'a> { - #[inline] - fn hash_spans(&self) -> bool { - true - } - - #[inline] - fn def_span(&self, id: LocalDefId) -> Span { - self.resolver.def_span(id) - } - - #[inline] - fn def_path_hash(&self, def_id: DefId) -> DefPathHash { - self.resolver.def_path_hash(def_id) - } - - #[inline] - fn span_data_to_lines_and_cols( - &mut self, - span: &rustc_span::SpanData, - ) -> Option<(Lrc<rustc_span::SourceFile>, usize, rustc_span::BytePos, usize, rustc_span::BytePos)> - { - self.source_map.span_data_to_lines_and_cols(span) - } -} - /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree, /// and if so, what meaning it has. #[derive(Debug)] @@ -314,14 +293,15 @@ ) -> &'hir hir::Crate<'hir> { let _prof_timer = sess.prof.verbose_generic_activity("hir_lowering"); + let owners = IndexVec::from_fn_n(|_| None, resolver.definitions().def_index_count()); LoweringContext { sess, resolver, nt_to_tokenstream, arena, - owners: IndexVec::default(), - bodies: BTreeMap::new(), - attrs: BTreeMap::default(), + owners, + bodies: Vec::new(), + attrs: SortedMap::new(), catch_scope: None, loop_scope: None, is_in_loop_condition: false, @@ -331,6 +311,7 @@ current_hir_id_owner: CRATE_DEF_ID, item_local_id_counter: hir::ItemLocalId::new(0), node_id_to_hir_id: IndexVec::new(), + local_node_ids: Vec::new(), generator_kind: None, task_context: None, current_item: None, @@ -420,13 +401,7 @@ hir::OwnerNode::Crate(lctx.arena.alloc(module)) }); - let mut trait_map: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); - for (k, v) in self.resolver.take_trait_map().into_iter() { - if let Some(Some(hir_id)) = self.node_id_to_hir_id.get(k) { - let map = trait_map.entry(hir_id.owner).or_default(); - map.insert(hir_id.local_id, v.into_boxed_slice()); - } - } + let hir_hash = self.compute_hir_hash(); let mut def_id_to_hir_id = IndexVec::default(); @@ -441,24 +416,29 @@ self.resolver.definitions().init_def_id_to_hir_id_mapping(def_id_to_hir_id); - #[cfg(debug_assertions)] - for (&id, attrs) in self.attrs.iter() { - // Verify that we do not store empty slices in the map. - if attrs.is_empty() { - panic!("Stored empty attributes for {:?}", id); - } - } - - let krate = - hir::Crate { owners: self.owners, bodies: self.bodies, trait_map, attrs: self.attrs }; + let krate = hir::Crate { owners: self.owners, hir_hash }; self.arena.alloc(krate) } - fn create_stable_hashing_context(&self) -> LoweringHasher<'_> { - LoweringHasher { - source_map: CachingSourceMapView::new(self.sess.source_map()), - resolver: self.resolver, - } + /// 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(&mut self) -> Fingerprint { + let definitions = self.resolver.definitions(); + let mut hir_body_nodes: Vec<_> = self + .owners + .iter_enumerated() + .filter_map(|(def_id, info)| { + let info = info.as_ref()?; + let def_path_hash = definitions.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 = self.resolver.create_stable_hashing_context(); + hir_body_nodes.hash_stable(&mut hcx, &mut stable_hasher); + stable_hasher.finish() } fn with_hir_id_owner( @@ -468,25 +448,93 @@ ) -> LocalDefId { let def_id = self.resolver.local_def_id(owner); - // Always allocate the first `HirId` for the owner itself. - let _old = self.node_id_to_hir_id.insert(owner, hir::HirId::make_owner(def_id)); - debug_assert_eq!(_old, None); - + let current_attrs = std::mem::take(&mut self.attrs); + let current_bodies = std::mem::take(&mut self.bodies); + let current_node_ids = std::mem::take(&mut self.local_node_ids); let current_owner = std::mem::replace(&mut self.current_hir_id_owner, def_id); let current_local_counter = std::mem::replace(&mut self.item_local_id_counter, hir::ItemLocalId::new(1)); - let item = f(self); + // Always allocate the first `HirId` for the owner itself. + let _old = self.node_id_to_hir_id.insert(owner, hir::HirId::make_owner(def_id)); + debug_assert_eq!(_old, None); + self.local_node_ids.push(owner); + let item = f(self); + debug_assert_eq!(def_id, item.def_id()); + let info = self.make_owner_info(item); + + self.attrs = current_attrs; + self.bodies = current_bodies; + self.local_node_ids = current_node_ids; self.current_hir_id_owner = current_owner; self.item_local_id_counter = current_local_counter; - let _old = self.owners.insert(def_id, item); + let _old = self.owners.insert(def_id, info); debug_assert!(_old.is_none()); def_id } + fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> hir::OwnerInfo<'hir> { + let attrs = std::mem::take(&mut self.attrs); + let mut bodies = std::mem::take(&mut self.bodies); + let local_node_ids = std::mem::take(&mut self.local_node_ids); + let trait_map = local_node_ids + .into_iter() + .filter_map(|node_id| { + let hir_id = self.node_id_to_hir_id[node_id]?; + let traits = self.resolver.take_trait_map(node_id)?; + Some((hir_id.local_id, traits.into_boxed_slice())) + }) + .collect(); + + #[cfg(debug_assertions)] + for (id, attrs) in attrs.iter() { + // Verify that we do not store empty slices in the map. + if attrs.is_empty() { + panic!("Stored empty attributes for {:?}", id); + } + } + + 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.resolver.definitions(), node, &bodies); + let nodes = hir::OwnerNodes { hash_including_bodies, hash_without_bodies, nodes, bodies }; + let attrs = { + let mut hcx = self.resolver.create_stable_hashing_context(); + let mut stable_hasher = StableHasher::new(); + attrs.hash_stable(&mut hcx, &mut stable_hasher); + let hash = stable_hasher.finish(); + hir::AttributeMap { map: attrs, hash } + }; + + hir::OwnerInfo { nodes, parenting, attrs, trait_map } + } + + /// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate + /// queries which depend on the full HIR tree and those which only depend on the item signature. + fn hash_owner( + &mut self, + node: hir::OwnerNode<'hir>, + bodies: &SortedMap<hir::ItemLocalId, &'hir hir::Body<'hir>>, + ) -> (Fingerprint, Fingerprint) { + let mut hcx = self.resolver.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) + } + /// This method allocates a new `HirId` for the given `NodeId` and stores it in /// the `LoweringContext`'s `NodeId => HirId` map. /// Take care not to call this method if the resulting `HirId` is then not @@ -501,6 +549,7 @@ let owner = self.current_hir_id_owner; let local_id = self.item_local_id_counter; self.item_local_id_counter.increment_by(1); + self.local_node_ids.push(ast_node_id); hir::HirId { owner, local_id } }) } @@ -547,7 +596,7 @@ allow_internal_unstable, reason, self.sess.edition(), - self.create_stable_hashing_context(), + self.resolver.create_stable_hashing_context(), ) } @@ -791,9 +840,10 @@ if attrs.is_empty() { None } else { + debug_assert_eq!(id.owner, self.current_hir_id_owner); let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a))); debug_assert!(!ret.is_empty()); - self.attrs.insert(id, ret); + self.attrs.insert(id.local_id, ret); Some(ret) } } @@ -819,9 +869,11 @@ } fn alias_attrs(&mut self, id: hir::HirId, target_id: hir::HirId) { - if let Some(&a) = self.attrs.get(&target_id) { + debug_assert_eq!(id.owner, self.current_hir_id_owner); + debug_assert_eq!(target_id.owner, self.current_hir_id_owner); + if let Some(&a) = self.attrs.get(&target_id.local_id) { debug_assert!(!a.is_empty()); - self.attrs.insert(id, a); + self.attrs.insert(id.local_id, a); } } @@ -1286,10 +1338,7 @@ pure_wrt_drop: false, bounds: hir_bounds, span: self.lower_span(span), - kind: hir::GenericParamKind::Type { - default: None, - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - }, + kind: hir::GenericParamKind::Type { default: None, synthetic: true }, }); hir::TyKind::Path(hir::QPath::Resolved( @@ -1435,7 +1484,7 @@ trace!("registering opaque type with id {:#?}", opaque_ty_id); let opaque_ty_item = hir::Item { def_id: opaque_ty_id, - ident: Ident::invalid(), + ident: Ident::empty(), kind: opaque_ty_item_kind, vis: respan(self.lower_span(span.shrink_to_lo()), hir::VisibilityKind::Inherited), span: self.lower_span(opaque_ty_span), @@ -1902,12 +1951,7 @@ default: default.as_ref().map(|x| { self.lower_ty(x, ImplTraitContext::Disallowed(ImplTraitPosition::Other)) }), - synthetic: param - .attrs - .iter() - .filter(|attr| attr.has_name(sym::rustc_synthetic)) - .map(|_| hir::SyntheticTyParamKind::FromAttr) - .next(), + synthetic: false, }; (hir::ParamName::Plain(self.lower_ident(param.ident)), kind) @@ -2066,7 +2110,7 @@ let hir_id = self.next_id(); if let Some(a) = attrs { debug_assert!(!a.is_empty()); - self.attrs.insert(hir_id, a); + self.attrs.insert(hir_id.local_id, a); } let local = hir::Local { hir_id, init, pat, source, span: self.lower_span(span), ty: None }; self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local)))
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 968e9fa..1822ba6 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -113,7 +113,7 @@ if sess.opts.unstable_features.is_nightly_build() { sess.struct_span_err(expr.span, "`let` expressions are not supported here") .note("only supported directly in conditions of `if`- and `while`-expressions") - .note("as well as when nested within `&&` and parenthesis in those conditions") + .note("as well as when nested within `&&` and parentheses in those conditions") .emit(); } else { sess.struct_span_err(expr.span, "expected expression, found statement (`let`)") @@ -1064,7 +1064,7 @@ } match item.kind { - ItemKind::Impl(box ImplKind { + ItemKind::Impl(box Impl { unsafety, polarity, defaultness: _, @@ -1111,7 +1111,7 @@ }); return; // Avoid visiting again. } - ItemKind::Impl(box ImplKind { + ItemKind::Impl(box Impl { unsafety, polarity, defaultness, @@ -1152,8 +1152,8 @@ .emit(); } } - ItemKind::Fn(box FnKind(def, ref sig, ref generics, ref body)) => { - self.check_defaultness(item.span, def); + ItemKind::Fn(box Fn { defaultness, ref sig, ref generics, ref body }) => { + self.check_defaultness(item.span, defaultness); if body.is_none() { let msg = "free function without a body"; @@ -1195,19 +1195,13 @@ } } } - ItemKind::Trait(box TraitKind( - is_auto, - _, - ref generics, - ref bounds, - ref trait_items, - )) => { + ItemKind::Trait(box Trait { is_auto, ref generics, ref bounds, ref items, .. }) => { if is_auto == IsAuto::Yes { // Auto traits cannot have generics, super traits nor contain items. self.deny_generic_params(generics, item.ident.span); self.deny_super_traits(bounds, item.ident.span); self.deny_where_clause(&generics.where_clause, item.ident.span); - self.deny_items(trait_items, item.ident.span); + self.deny_items(items, item.ident.span); } self.no_questions_in_bounds(bounds, "supertraits", true); @@ -1217,7 +1211,7 @@ self.visit_ident(item.ident); self.visit_generics(generics); self.with_banned_tilde_const(|this| walk_list!(this, visit_param_bound, bounds)); - walk_list!(self, visit_assoc_item, trait_items, AssocCtxt::Trait); + walk_list!(self, visit_assoc_item, items, AssocCtxt::Trait); walk_list!(self, visit_attribute, &item.attrs); return; } @@ -1278,9 +1272,9 @@ let msg = "free static item without body"; self.error_item_without_body(item.span, "static", msg, " = <expr>;"); } - ItemKind::TyAlias(box TyAliasKind(def, _, ref bounds, ref body)) => { - self.check_defaultness(item.span, def); - if body.is_none() { + ItemKind::TyAlias(box TyAlias { defaultness, ref bounds, ref ty, .. }) => { + self.check_defaultness(item.span, defaultness); + if ty.is_none() { let msg = "free type alias without body"; self.error_item_without_body(item.span, "type", msg, " = <type>;"); } @@ -1294,15 +1288,15 @@ fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { match &fi.kind { - ForeignItemKind::Fn(box FnKind(def, sig, _, body)) => { - self.check_defaultness(fi.span, *def); + ForeignItemKind::Fn(box Fn { defaultness, sig, body, .. }) => { + self.check_defaultness(fi.span, *defaultness); self.check_foreign_fn_bodyless(fi.ident, body.as_deref()); self.check_foreign_fn_headerless(fi.ident, fi.span, sig.header); self.check_foreign_item_ascii_only(fi.ident); } - ForeignItemKind::TyAlias(box TyAliasKind(def, generics, bounds, body)) => { - self.check_defaultness(fi.span, *def); - self.check_foreign_kind_bodyless(fi.ident, "type", body.as_ref().map(|b| b.span)); + ForeignItemKind::TyAlias(box TyAlias { defaultness, generics, bounds, ty, .. }) => { + self.check_defaultness(fi.span, *defaultness); + self.check_foreign_kind_bodyless(fi.ident, "type", ty.as_ref().map(|b| b.span)); self.check_type_no_bounds(bounds, "`extern` blocks"); self.check_foreign_ty_genericless(generics); self.check_foreign_item_ascii_only(fi.ident); @@ -1587,11 +1581,11 @@ AssocItemKind::Const(_, _, body) => { self.check_impl_item_provided(item.span, body, "constant", " = <expr>;"); } - AssocItemKind::Fn(box FnKind(_, _, _, body)) => { + AssocItemKind::Fn(box Fn { body, .. }) => { self.check_impl_item_provided(item.span, body, "function", " { <body> }"); } - AssocItemKind::TyAlias(box TyAliasKind(_, _, bounds, body)) => { - self.check_impl_item_provided(item.span, body, "type", " = <type>;"); + AssocItemKind::TyAlias(box TyAlias { bounds, ty, .. }) => { + self.check_impl_item_provided(item.span, ty, "type", " = <type>;"); self.check_type_no_bounds(bounds, "`impl`s"); } _ => {} @@ -1600,7 +1594,7 @@ if ctxt == AssocCtxt::Trait || self.in_trait_impl { self.invalid_visibility(&item.vis, None); - if let AssocItemKind::Fn(box FnKind(_, sig, _, _)) = &item.kind { + if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { self.check_trait_fn_not_const(sig.header.constness); self.check_trait_fn_not_async(item.span, sig.header.asyncness); } @@ -1611,7 +1605,7 @@ } match item.kind { - AssocItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref ty)) + AssocItemKind::TyAlias(box TyAlias { ref generics, ref bounds, ref ty, .. }) if ctxt == AssocCtxt::Trait => { self.visit_vis(&item.vis); @@ -1623,7 +1617,7 @@ }); walk_list!(self, visit_ty, ty); } - AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, ref body)) + AssocItemKind::Fn(box Fn { ref sig, ref generics, ref body, .. }) if self.in_const_trait_impl || ctxt == AssocCtxt::Trait || matches!(sig.header.constness, Const::Yes(_)) =>
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 91b4597..ad539f2 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -3,7 +3,7 @@ use rustc_ast::{AssocTyConstraint, AssocTyConstraintKind, NodeId}; use rustc_ast::{PatKind, RangeEnd, VariantData}; use rustc_errors::struct_span_err; -use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP}; +use rustc_feature::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_feature::{Features, GateIssue}; use rustc_session::parse::{feature_err, feature_err_issue}; use rustc_session::Session; @@ -301,11 +301,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_attribute(&mut self, attr: &ast::Attribute) { - let attr_info = - attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a); + let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); // Check feature gates for built-in attributes. - if let Some((.., AttributeGate::Gated(_, name, descr, has_feature))) = attr_info { - gate_feature_fn!(self, has_feature, attr.span, name, descr); + if let Some(BuiltinAttribute { + gate: AttributeGate::Gated(_, name, descr, has_feature), + .. + }) = attr_info + { + gate_feature_fn!(self, has_feature, attr.span, *name, descr); } // Check unstable flavors of the `#[doc]` attribute. if attr.has_name(sym::doc) { @@ -322,8 +325,12 @@ cfg_hide => doc_cfg_hide masked => doc_masked notable_trait => doc_notable_trait - keyword => doc_keyword ); + + if nested_meta.has_name(sym::keyword) { + let msg = "`#[doc(keyword)]` is meant for internal use only"; + gate_feature_post!(self, rustdoc_internals, attr.span, msg); + } } } @@ -423,9 +430,7 @@ } } - ast::ItemKind::Impl(box ast::ImplKind { - polarity, defaultness, ref of_trait, .. - }) => { + ast::ItemKind::Impl(box ast::Impl { polarity, defaultness, ref of_trait, .. }) => { if let ast::ImplPolarity::Negative(span) = polarity { gate_feature_post!( &self, @@ -441,7 +446,7 @@ } } - ast::ItemKind::Trait(box ast::TraitKind(ast::IsAuto::Yes, ..)) => { + ast::ItemKind::Trait(box ast::Trait { is_auto: ast::IsAuto::Yes, .. }) => { gate_feature_post!( &self, auto_traits, @@ -459,7 +464,7 @@ gate_feature_post!(&self, decl_macro, i.span, msg); } - ast::ItemKind::TyAlias(box ast::TyAliasKind(_, _, _, Some(ref ty))) => { + ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ref ty), .. }) => { self.check_impl_trait(&ty) } @@ -541,15 +546,13 @@ ast::ExprKind::TryBlock(_) => { gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental"); } - ast::ExprKind::Block(_, opt_label) => { - if let Some(label) = opt_label { - gate_feature_post!( - &self, - label_break_value, - label.ident.span, - "labels on blocks are unstable" - ); - } + ast::ExprKind::Block(_, Some(label)) => { + gate_feature_post!( + &self, + label_break_value, + label.ident.span, + "labels on blocks are unstable" + ); } _ => {} } @@ -634,7 +637,7 @@ fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) { let is_fn = match i.kind { ast::AssocItemKind::Fn(_) => true, - ast::AssocItemKind::TyAlias(box ast::TyAliasKind(_, ref generics, _, ref ty)) => { + ast::AssocItemKind::TyAlias(box ast::TyAlias { ref generics, ref ty, .. }) => { if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) { gate_feature_post!( &self, @@ -720,6 +723,7 @@ gate_all!(const_trait_impl, "const trait impls are experimental"); gate_all!(half_open_range_patterns, "half-open range patterns are unstable"); gate_all!(inline_const, "inline-const is experimental"); + gate_all!(inline_const_pat, "inline-const in pattern position is experimental"); gate_all!( const_generics_defaults, "default values for const generic parameters are experimental"
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index c248820..f1f2387 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1044,15 +1044,27 @@ self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::ForeignItemKind::Fn(box ast::FnKind(def, sig, gen, body)) => { - self.print_fn_full(sig, ident, gen, vis, *def, body.as_deref(), attrs); + ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { + self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); } ast::ForeignItemKind::Static(ty, mutbl, body) => { let def = ast::Defaultness::Final; self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def); } - ast::ForeignItemKind::TyAlias(box ast::TyAliasKind(def, generics, bounds, ty)) => { - self.print_associated_type(ident, generics, bounds, ty.as_deref(), vis, *def); + ast::ForeignItemKind::TyAlias(box ast::TyAlias { + defaultness, + generics, + bounds, + ty, + }) => { + self.print_associated_type( + ident, + generics, + bounds, + ty.as_deref(), + vis, + *defaultness, + ); } ast::ForeignItemKind::MacCall(m) => { self.print_mac(m); @@ -1156,9 +1168,17 @@ ast::ItemKind::Const(def, ref ty, ref body) => { self.print_item_const(item.ident, None, ty, body.as_deref(), &item.vis, def); } - ast::ItemKind::Fn(box ast::FnKind(def, ref sig, ref gen, ref body)) => { + ast::ItemKind::Fn(box ast::Fn { defaultness, ref sig, ref generics, ref body }) => { let body = body.as_deref(); - self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs); + self.print_fn_full( + sig, + item.ident, + generics, + &item.vis, + defaultness, + body, + &item.attrs, + ); } ast::ItemKind::Mod(unsafety, ref mod_kind) => { self.head(self.to_string(|s| { @@ -1203,9 +1223,21 @@ self.print_inline_asm(asm); self.end(); } - ast::ItemKind::TyAlias(box ast::TyAliasKind(def, ref generics, ref bounds, ref ty)) => { + ast::ItemKind::TyAlias(box ast::TyAlias { + defaultness, + ref generics, + ref bounds, + ref ty, + }) => { let ty = ty.as_deref(); - self.print_associated_type(item.ident, generics, bounds, ty, &item.vis, def); + self.print_associated_type( + item.ident, + generics, + bounds, + ty, + &item.vis, + defaultness, + ); } ast::ItemKind::Enum(ref enum_definition, ref params) => { self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis); @@ -1218,7 +1250,7 @@ self.head(visibility_qualified(&item.vis, "union")); self.print_struct(struct_def, generics, item.ident, item.span, true); } - ast::ItemKind::Impl(box ast::ImplKind { + ast::ItemKind::Impl(box ast::Impl { unsafety, polarity, defaultness, @@ -1261,13 +1293,14 @@ } self.bclose(item.span); } - ast::ItemKind::Trait(box ast::TraitKind( + ast::ItemKind::Trait(box ast::Trait { is_auto, unsafety, ref generics, ref bounds, - ref trait_items, - )) => { + ref items, + .. + }) => { self.head(""); self.print_visibility(&item.vis); self.print_unsafety(unsafety); @@ -1290,7 +1323,7 @@ self.s.word(" "); self.bopen(); self.print_inner_attributes(&item.attrs); - for trait_item in trait_items { + for trait_item in items { self.print_assoc_item(trait_item); } self.bclose(item.span); @@ -1405,11 +1438,7 @@ } } - crate fn print_record_struct_body( - &mut self, - fields: &Vec<ast::FieldDef>, - span: rustc_span::Span, - ) { + crate fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) { self.nbsp(); self.bopen(); self.hardbreak_if_not_bol(); @@ -1483,14 +1512,21 @@ self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::AssocItemKind::Fn(box ast::FnKind(def, sig, gen, body)) => { - self.print_fn_full(sig, ident, gen, vis, *def, body.as_deref(), attrs); + ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { + self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); } ast::AssocItemKind::Const(def, ty, body) => { self.print_item_const(ident, None, ty, body.as_deref(), vis, *def); } - ast::AssocItemKind::TyAlias(box ast::TyAliasKind(def, generics, bounds, ty)) => { - self.print_associated_type(ident, generics, bounds, ty.as_deref(), vis, *def); + ast::AssocItemKind::TyAlias(box ast::TyAlias { defaultness, generics, bounds, ty }) => { + self.print_associated_type( + ident, + generics, + bounds, + ty.as_deref(), + vis, + *defaultness, + ); } ast::AssocItemKind::MacCall(m) => { self.print_mac(m); @@ -1675,7 +1711,7 @@ self.print_expr_cond_paren(expr, Self::cond_needs_par(expr)) } - // Does `expr` need parenthesis when printed in a condition position? + // Does `expr` need parentheses when printed in a condition position? // // These cases need parens due to the parse error observed in #26461: `if return {}` // parses as the erroneous construct `if (return {})`, not `if (return) {}`. @@ -2199,8 +2235,8 @@ let mut args = vec![AsmArg::Template(InlineAsmTemplatePiece::to_string(&asm.template))]; args.extend(asm.operands.iter().map(|(o, _)| AsmArg::Operand(o))); - if let Some((abi, _)) = asm.clobber_abi { - args.push(AsmArg::ClobberAbi(abi)); + for (abi, _) in &asm.clobber_abis { + args.push(AsmArg::ClobberAbi(*abi)); } if !asm.options.is_empty() { args.push(AsmArg::Options(asm.options));
diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index ee2ce1d..952e18c 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs
@@ -12,22 +12,22 @@ use std::fmt; use std::ops::Index; -crate struct BorrowSet<'tcx> { +pub struct BorrowSet<'tcx> { /// The fundamental map relating bitvector indexes to the borrows /// in the MIR. Each borrow is also uniquely identified in the MIR /// by the `Location` of the assignment statement in which it /// appears on the right hand side. Thus the location is the map /// key, and its position in the map corresponds to `BorrowIndex`. - crate location_map: FxIndexMap<Location, BorrowData<'tcx>>, + pub location_map: FxIndexMap<Location, BorrowData<'tcx>>, /// Locations which activate borrows. /// NOTE: a given location may activate more than one borrow in the future /// when more general two-phase borrow support is introduced, but for now we /// only need to store one borrow index. - crate activation_map: FxHashMap<Location, Vec<BorrowIndex>>, + pub activation_map: FxHashMap<Location, Vec<BorrowIndex>>, /// Map from local to all the borrows on that local. - crate local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>, + pub local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>, crate locals_state_at_exit: LocalsStateAtExit, } @@ -43,27 +43,27 @@ /// Location where a two-phase borrow is activated, if a borrow /// is in fact a two-phase borrow. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -crate enum TwoPhaseActivation { +pub enum TwoPhaseActivation { NotTwoPhase, NotActivated, ActivatedAt(Location), } #[derive(Debug, Clone)] -crate struct BorrowData<'tcx> { +pub struct BorrowData<'tcx> { /// Location where the borrow reservation starts. /// In many cases, this will be equal to the activation location but not always. - crate reserve_location: Location, + pub reserve_location: Location, /// Location where the borrow is activated. - crate activation_location: TwoPhaseActivation, + pub activation_location: TwoPhaseActivation, /// What kind of borrow this is - crate kind: mir::BorrowKind, + pub kind: mir::BorrowKind, /// The region for which this borrow is live - crate region: RegionVid, + pub region: RegionVid, /// Place from which we are borrowing - crate borrowed_place: mir::Place<'tcx>, + pub borrowed_place: mir::Place<'tcx>, /// Place to which the borrow was stored - crate assigned_place: mir::Place<'tcx>, + pub assigned_place: mir::Place<'tcx>, } impl<'tcx> fmt::Display for BorrowData<'tcx> { @@ -78,7 +78,7 @@ } } -crate enum LocalsStateAtExit { +pub enum LocalsStateAtExit { AllAreInvalidated, SomeAreInvalidated { has_storage_dead_or_moved: BitSet<Local> }, } @@ -315,9 +315,7 @@ // TEMP = &foo // // so extract `temp`. - let temp = if let Some(temp) = assigned_place.as_local() { - temp - } else { + let Some(temp) = assigned_place.as_local() else { span_bug!( self.body.source_info(start_location).span, "expected 2-phase borrow to assign to a local, not `{:?}`",
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 15309cc..1bc9f8c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -339,7 +339,7 @@ // We generally shouldn't have errors here because the query was // already run, but there's no point using `delay_span_bug` // when we're going to emit an error here anyway. - let _errors = fulfill_cx.select_all_or_error(infcx).err().unwrap_or_else(Vec::new); + let _errors = fulfill_cx.select_all_or_error(infcx); let (sub_region, cause) = infcx.with_region_constraints(|region_constraints| { debug!("{:#?}", region_constraints);
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 3739889..46a3c0f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -11,7 +11,6 @@ }; use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; -use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::sym; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; @@ -247,6 +246,36 @@ place_name, partially_str, loop_message ), ); + let sess = self.infcx.tcx.sess; + let ty = used_place.ty(self.body, self.infcx.tcx).ty; + // If we have a `&mut` ref, we need to reborrow. + if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { + // If we are in a loop this will be suggested later. + if !is_loop_move { + err.span_suggestion_verbose( + move_span.shrink_to_lo(), + &format!( + "consider creating a fresh reborrow of {} here", + self.describe_place(moved_place.as_ref()) + .map(|n| format!("`{}`", n)) + .unwrap_or_else( + || "the mutable reference".to_string() + ), + ), + "&mut *".to_string(), + Applicability::MachineApplicable, + ); + } + } else if let Ok(snippet) = + sess.source_map().span_to_snippet(move_span) + { + err.span_suggestion( + move_span, + "consider borrowing to avoid moving into the for loop", + format!("&{}", snippet), + Applicability::MaybeIncorrect, + ); + } } else { err.span_label( fn_call_span, @@ -315,35 +344,6 @@ in_pattern = true; } } - - if let Some(DesugaringKind::ForLoop(_)) = move_span.desugaring_kind() { - let sess = self.infcx.tcx.sess; - let ty = used_place.ty(self.body, self.infcx.tcx).ty; - // If we have a `&mut` ref, we need to reborrow. - if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { - // If we are in a loop this will be suggested later. - if !is_loop_move { - err.span_suggestion_verbose( - move_span.shrink_to_lo(), - &format!( - "consider creating a fresh reborrow of {} here", - self.describe_place(moved_place.as_ref()) - .map(|n| format!("`{}`", n)) - .unwrap_or_else(|| "the mutable reference".to_string()), - ), - "&mut *".to_string(), - Applicability::MachineApplicable, - ); - } - } else if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) { - err.span_suggestion( - move_span, - "consider borrowing to avoid moving into the for loop", - format!("&{}", snippet), - Applicability::MaybeIncorrect, - ); - } - } } use_spans.var_span_label_path_only( @@ -408,7 +408,7 @@ let param = generics.type_param(¶m_ty, tcx); if let Some(generics) = tcx .hir() - .get_generics(tcx.closure_base_def_id(self.mir_def_id().to_def_id())) + .get_generics(tcx.typeck_root_def_id(self.mir_def_id().to_def_id())) { suggest_constraining_type_param( tcx,
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index d5de080..db8268c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -13,7 +13,7 @@ use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::{self, RegionVid, TyCtxt}; use rustc_span::symbol::Symbol; -use rustc_span::Span; +use rustc_span::{sym, DesugaringKind, Span}; use crate::region_infer::BlameConstraint; use crate::{ @@ -53,10 +53,7 @@ impl BorrowExplanation { pub(crate) fn is_explained(&self) -> bool { - match self { - BorrowExplanation::Unexplained => false, - _ => true, - } + !matches!(self, BorrowExplanation::Unexplained) } pub(crate) fn add_explanation_to_diagnostic<'tcx>( &self, @@ -138,7 +135,16 @@ should_note_order, } => { let local_decl = &body.local_decls[dropped_local]; - let (dtor_desc, type_desc) = match local_decl.ty.kind() { + let mut ty = local_decl.ty; + if local_decl.source_info.span.desugaring_kind() == Some(DesugaringKind::ForLoop) { + if let ty::Adt(adt, substs) = local_decl.ty.kind() { + if tcx.is_diagnostic_item(sym::Option, adt.did) { + // in for loop desugaring, only look at the `Some(..)` inner type + ty = substs.type_at(0); + } + } + } + let (dtor_desc, type_desc) = match ty.kind() { // If type is an ADT that implements Drop, then // simplify output by reporting just the ADT name. ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() => {
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index a4df277..79623e2 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -13,11 +13,7 @@ use rustc_middle::ty::print::Print; use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; -use rustc_span::{ - hygiene::{DesugaringKind, ForLoopLoc}, - symbol::sym, - Span, -}; +use rustc_span::{hygiene::DesugaringKind, symbol::sym, Span}; use rustc_target::abi::VariantIdx; use super::borrow_set::BorrowData; @@ -955,10 +951,8 @@ let kind = kind.unwrap_or_else(|| { // This isn't a 'special' use of `self` debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span); - let implicit_into_iter = matches!( - fn_call_span.desugaring_kind(), - Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) - ); + let implicit_into_iter = Some(method_did) == tcx.lang_items().into_iter_fn() + && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop); let parent_self_ty = parent .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl) .and_then(|did| match tcx.type_of(did).kind() {
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 855e685..692c20d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -5,11 +5,10 @@ use rustc_mir_dataflow::move_paths::{ IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex, }; -use rustc_span::source_map::DesugaringKind; use rustc_span::{sym, Span, DUMMY_SP}; use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; -use crate::diagnostics::UseSpans; +use crate::diagnostics::{FnSelfUseKind, UseSpans}; use crate::prefixes::PrefixSet; use crate::MirBorrowckCtxt; @@ -400,19 +399,21 @@ | ty::Opaque(def_id, _) => def_id, _ => return err, }; - let is_option = self.infcx.tcx.is_diagnostic_item(sym::Option, def_id); - let is_result = self.infcx.tcx.is_diagnostic_item(sym::Result, def_id); - if (is_option || is_result) && use_spans.map_or(true, |v| !v.for_closure()) { + let diag_name = self.infcx.tcx.get_diagnostic_name(def_id); + if matches!(diag_name, Some(sym::Option | sym::Result)) + && use_spans.map_or(true, |v| !v.for_closure()) + { err.span_suggestion_verbose( span.shrink_to_hi(), - &format!( - "consider borrowing the `{}`'s content", - if is_option { "Option" } else { "Result" } - ), + &format!("consider borrowing the `{}`'s content", diag_name.unwrap()), ".as_ref()".to_string(), Applicability::MaybeIncorrect, ); - } else if matches!(span.desugaring_kind(), Some(DesugaringKind::ForLoop(_))) { + } else if let Some(UseSpans::FnSelfUse { + kind: FnSelfUseKind::Normal { implicit_into_iter: true, .. }, + .. + }) = use_spans + { let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) { Some(def_id) => self.infcx.tcx.infer_ctxt().enter(|infcx| { type_known_to_meet_bound_modulo_regions(
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 246d2e3..a0f8aab 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -45,12 +45,12 @@ let item_msg; let reason; let mut opt_source = None; - let access_place_desc = self.describe_place(access_place.as_ref()); + let access_place_desc = self.describe_any_place(access_place.as_ref()); debug!("report_mutability_error: access_place_desc={:?}", access_place_desc); match the_place_err { PlaceRef { local, projection: [] } => { - item_msg = format!("`{}`", access_place_desc.unwrap()); + item_msg = access_place_desc; if access_place.as_local().is_some() { reason = ", as it is not declared as mutable".to_string(); } else { @@ -83,7 +83,7 @@ // If we deref an immutable ref then the suggestion here doesn't help. return; } else { - item_msg = format!("`{}`", access_place_desc.unwrap()); + item_msg = access_place_desc; if self.is_upvar_field_projection(access_place.as_ref()).is_some() { reason = ", as it is not declared as mutable".to_string(); } else { @@ -96,17 +96,17 @@ PlaceRef { local, projection: [ProjectionElem::Deref] } if self.body.local_decls[local].is_ref_for_guard() => { - item_msg = format!("`{}`", access_place_desc.unwrap()); + item_msg = access_place_desc; reason = ", as it is immutable for the pattern guard".to_string(); } PlaceRef { local, projection: [ProjectionElem::Deref] } if self.body.local_decls[local].is_ref_to_static() => { if access_place.projection.len() == 1 { - item_msg = format!("immutable static item `{}`", access_place_desc.unwrap()); + item_msg = format!("immutable static item {}", access_place_desc); reason = String::new(); } else { - item_msg = format!("`{}`", access_place_desc.unwrap()); + item_msg = access_place_desc; let local_info = &self.body.local_decls[local].local_info; if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info { let static_name = &self.infcx.tcx.item_name(def_id); @@ -121,7 +121,7 @@ && proj_base.is_empty() && !self.upvars.is_empty() { - item_msg = format!("`{}`", access_place_desc.unwrap()); + item_msg = access_place_desc; debug_assert!( self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_region_ptr() ); @@ -147,7 +147,7 @@ }); let pointer_type = source.describe_for_immutable_place(self.infcx.tcx); opt_source = Some(source); - if let Some(desc) = access_place_desc { + if let Some(desc) = self.describe_place(access_place.as_ref()) { item_msg = format!("`{}`", desc); reason = match error_access { AccessKind::Mutate => format!(", which is behind {}", pointer_type), @@ -445,15 +445,27 @@ }, ))) => { // check if the RHS is from desugaring - let locations = self.body.find_assignments(local); - let opt_assignment_rhs_span = locations - .first() - .map(|&location| self.body.source_info(location).span); - let opt_desugaring_kind = - opt_assignment_rhs_span.and_then(|span| span.desugaring_kind()); - match opt_desugaring_kind { + let opt_assignment_rhs_span = + self.body.find_assignments(local).first().map(|&location| { + if let Some(mir::Statement { + source_info: _, + kind: + mir::StatementKind::Assign(box ( + _, + mir::Rvalue::Use(mir::Operand::Copy(place)), + )), + }) = self.body[location.block] + .statements + .get(location.statement_index) + { + self.body.local_decls[place.local].source_info.span + } else { + self.body.source_info(location).span + } + }); + match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) { // on for loops, RHS points to the iterator part - Some(DesugaringKind::ForLoop(_)) => { + Some(DesugaringKind::ForLoop) => { self.suggest_similar_mut_method_for_for_loop(&mut err); Some(( false,
diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index b15e55c..723b57e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
@@ -90,9 +90,7 @@ let mut unified_already = FxHashSet::default(); for (fr, outlived) in &self.constraints_to_add { - let fr_name = if let Some(fr_name) = self.region_vid_to_name(mbcx, *fr) { - fr_name - } else { + let Some(fr_name) = self.region_vid_to_name(mbcx, *fr) else { continue; };
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index e626015..76d3a83 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs
@@ -2,11 +2,11 @@ #![feature(bool_to_option)] #![feature(box_patterns)] -#![cfg_attr(bootstrap, feature(const_panic))] #![feature(crate_visibility_modifier)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(in_band_lifetimes)] #![feature(iter_zip)] +#![feature(let_else)] #![feature(min_specialization)] #![feature(stmt_expr_attributes)] #![feature(trusted_step)] @@ -63,7 +63,7 @@ use self::path_utils::*; -mod borrow_set; +pub mod borrow_set; mod borrowck_errors; mod constraint_generation; mod constraints;
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index e5924f9..6ffab16 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs
@@ -376,7 +376,7 @@ errors_buffer: &mut Vec<Diagnostic>, ) { let tcx = infcx.tcx; - let base_def_id = tcx.closure_base_def_id(body.source.def_id()); + let base_def_id = tcx.typeck_root_def_id(body.source.def_id()); if !tcx.has_attr(base_def_id, sym::rustc_regions) { return; }
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 21c26af..b39a28f 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -569,7 +569,7 @@ // to store those. Otherwise, we'll pass in `None` to the // functions below, which will trigger them to report errors // eagerly. - let mut outlives_requirements = infcx.tcx.is_closure(mir_def_id).then(Vec::new); + let mut outlives_requirements = infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new); self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer); @@ -689,6 +689,16 @@ // them down. let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec(); + // Convert to the SCC representative: sometimes we have inference + // variables in the member constraint that wind up equated with + // universal regions. The scc representative is the minimal numbered + // one from the corresponding scc so it will be the universal region + // if one exists. + for c_r in &mut choice_regions { + let scc = self.constraint_sccs.scc(*c_r); + *c_r = self.scc_representatives[scc]; + } + // The 'member region' in a member constraint is part of the // hidden type, which must be in the root universe. Therefore, // it cannot have any placeholders in its value. @@ -2100,14 +2110,14 @@ _ => constraint_sup_scc != target_scc, } } else { - match categorized_path[*i].category { + !matches!( + categorized_path[*i].category, ConstraintCategory::OpaqueType - | ConstraintCategory::Boring - | ConstraintCategory::BoringNoLocation - | ConstraintCategory::Internal - | ConstraintCategory::Predicate(_) => false, - _ => true, - } + | ConstraintCategory::Boring + | ConstraintCategory::BoringNoLocation + | ConstraintCategory::Internal + | ConstraintCategory::Predicate(_) + ) } }; @@ -2219,7 +2229,7 @@ tcx, closure_substs, self.num_external_vids, - tcx.closure_base_def_id(closure_def_id), + tcx.typeck_root_def_id(closure_def_id), ); debug!("apply_requirements: closure_mapping={:?}", closure_mapping);
diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 8819039..100ac57 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs
@@ -60,6 +60,11 @@ PointIndex::new(start_index) } + /// Return the PointIndex for the block start of this index. + crate fn to_block_start(&self, index: PointIndex) -> PointIndex { + PointIndex::new(self.statements_before_block[self.basic_blocks[index]]) + } + /// Converts a `PointIndex` back to a location. O(1). crate fn to_location(&self, index: PointIndex) -> Location { assert!(index.index() < self.num_points); @@ -76,29 +81,6 @@ crate fn point_in_range(&self, index: PointIndex) -> bool { index.index() < self.num_points } - - /// Pushes all predecessors of `index` onto `stack`. - crate fn push_predecessors( - &self, - body: &Body<'_>, - index: PointIndex, - stack: &mut Vec<PointIndex>, - ) { - let Location { block, statement_index } = self.to_location(index); - if statement_index == 0 { - // If this is a basic block head, then the predecessors are - // the terminators of other basic blocks - stack.extend( - body.predecessors()[block] - .iter() - .map(|&pred_bb| body.terminator_loc(pred_bb)) - .map(|pred_loc| self.point_from_location(pred_loc)), - ); - } else { - // Otherwise, the pred is just the previous statement - stack.push(PointIndex::new(index.index() - 1)); - } - } } rustc_index::newtype_index! {
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 7d4df59..0fa72ed 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -94,6 +94,7 @@ Some(ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, }))), locations, category,
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 84fa9bb..8d97c3c 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -1,20 +1,18 @@ use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::TransitiveRelation; use rustc_infer::infer::canonical::QueryRegionConstraints; -use rustc_infer::infer::free_regions::FreeRegionRelations; use rustc_infer::infer::outlives; use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; -use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; +use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; use type_op::TypeOpOutput; use crate::{ - nll::ToRegionVid, type_check::constraint_conversion, type_check::{Locations, MirTypeckRegionConstraints}, universal_regions::UniversalRegions, @@ -381,21 +379,3 @@ } } } - -/// This trait is used by the `impl-trait` constraint code to abstract -/// over the `FreeRegionMap` from lexical regions and -/// `UniversalRegions` (from NLL)`. -impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegionRelations<'tcx> { - fn sub_free_regions( - &self, - _tcx: TyCtxt<'tcx>, - shorter: ty::Region<'tcx>, - longer: ty::Region<'tcx>, - ) -> bool { - let shorter = shorter.to_region_vid(); - assert!(self.universal_regions.is_universal_region(shorter)); - let longer = longer.to_region_vid(); - assert!(self.universal_regions.is_universal_region(longer)); - self.outlives(longer, shorter) - } -}
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 2433269..92d2d04 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -7,13 +7,16 @@ //! `RETURN_PLACE` the MIR arguments) are always fully normalized (and //! contain revealed `impl Trait` values). +use crate::type_check::constraint_conversion::ConstraintConversion; use rustc_index::vec::Idx; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::mir::*; -use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::Ty; use rustc_span::Span; -use rustc_trait_selection::traits::query::normalize::AtExt; +use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; +use rustc_trait_selection::traits::query::Fallible; +use type_op::TypeOpOutput; use crate::universal_regions::UniversalRegions; @@ -30,6 +33,9 @@ let (&normalized_output_ty, normalized_input_tys) = normalized_inputs_and_output.split_last().unwrap(); + debug!(?normalized_output_ty); + debug!(?normalized_input_tys); + let mir_def_id = body.source.def_id().expect_local(); // If the user explicitly annotated the input types, extract @@ -75,10 +81,12 @@ .delay_span_bug(body.span, "found more normalized_input_ty than local_decls"); break; } + // In MIR, argument N is stored in local N+1. let local = Local::new(argument_index + 1); let mir_input_ty = body.local_decls[local].ty; + let mir_input_span = body.local_decls[local].source_info.span; self.equate_normalized_input_or_output( normalized_input_ty, @@ -100,6 +108,7 @@ // If the user explicitly annotated the input types, enforce those. let user_provided_input_ty = self.normalize(user_provided_input_ty, Locations::All(mir_input_span)); + self.equate_normalized_input_or_output( user_provided_input_ty, mir_input_ty, @@ -167,30 +176,14 @@ // `rustc_traits::normalize_after_erasing_regions`. Ideally, we'd // like to normalize *before* inserting into `local_decls`, but // doing so ends up causing some other trouble. - let b = match self - .infcx - .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) - .normalize(b) - { - Ok(n) => { - debug!("equate_inputs_and_outputs: {:?}", n); - if n.obligations.iter().all(|o| { - matches!( - o.predicate.kind().skip_binder(), - ty::PredicateKind::RegionOutlives(_) - | ty::PredicateKind::TypeOutlives(_) - ) - }) { - n.value - } else { - b - } - } + let b = match self.normalize_and_add_constraints(b) { + Ok(n) => n, Err(_) => { debug!("equate_inputs_and_outputs: NoSolution"); b } }; + // Note: if we have to introduce new placeholders during normalization above, then we won't have // added those universes to the universe info, which we would want in `relate_tys`. if let Err(terr) = @@ -207,4 +200,27 @@ } } } + + pub(crate) fn normalize_and_add_constraints(&mut self, t: Ty<'tcx>) -> Fallible<Ty<'tcx>> { + let TypeOpOutput { output: norm_ty, constraints, .. } = + self.param_env.and(type_op::normalize::Normalize::new(t)).fully_perform(self.infcx)?; + + debug!("{:?} normalized to {:?}", t, norm_ty); + + for data in constraints.into_iter().collect::<Vec<_>>() { + ConstraintConversion::new( + self.infcx, + &self.borrowck_context.universal_regions, + &self.region_bound_pairs, + Some(self.implicit_region_bound), + self.param_env, + Locations::All(DUMMY_SP), + ConstraintCategory::Internal, + &mut self.borrowck_context.constraints, + ) + .convert_all(&*data); + } + + Ok(norm_ty) + } }
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 1671c7c..73c2840 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -205,12 +205,42 @@ self.stack.extend(self.cx.local_use_map.uses(local)); while let Some(p) = self.stack.pop() { - if self.defs.contains(p) { - continue; - } + // We are live in this block from the closest to us of: + // + // * Inclusively, the block start + // * Exclusively, the previous definition (if it's in this block) + // * Exclusively, the previous live_at setting (an optimization) + let block_start = self.cx.elements.to_block_start(p); + let previous_defs = self.defs.last_set_in(block_start..=p); + let previous_live_at = self.use_live_at.last_set_in(block_start..=p); - if self.use_live_at.insert(p) { - self.cx.elements.push_predecessors(self.cx.body, p, &mut self.stack) + let exclusive_start = match (previous_defs, previous_live_at) { + (Some(a), Some(b)) => Some(std::cmp::max(a, b)), + (Some(a), None) | (None, Some(a)) => Some(a), + (None, None) => None, + }; + + if let Some(exclusive) = exclusive_start { + self.use_live_at.insert_range(exclusive + 1..=p); + + // If we have a bound after the start of the block, we should + // not add the predecessors for this block. + continue; + } else { + // Add all the elements of this block. + self.use_live_at.insert_range(block_start..=p); + + // Then add the predecessors for this block, which are the + // terminators of predecessor basic blocks. Push those onto the + // stack so that the next iteration(s) will process them. + + let block = self.cx.elements.to_location(block_start).block; + self.stack.extend( + self.cx.body.predecessors()[block] + .iter() + .map(|&pred_bb| self.cx.body.terminator_loc(pred_bb)) + .map(|pred_loc| self.cx.elements.point_from_location(pred_loc)), + ); } } }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 7bf1198..da26d9c7 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -10,6 +10,7 @@ use rustc_data_structures::vec_map::VecMap; use rustc_errors::struct_span_err; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::lang_items::LangItem; use rustc_index::vec::{Idx, IndexVec}; @@ -36,7 +37,6 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::VariantIdx; use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::opaque_types::{GenerateMemberConstraints, InferCtxtExt}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::query::type_op; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; @@ -185,7 +185,6 @@ ®ion_bound_pairs, implicit_region_bound, &mut borrowck_context, - &universal_region_relations, |mut cx| { cx.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output); liveness::generate(&mut cx, body, elements, flow_inits, move_data, location_table); @@ -253,15 +252,7 @@ } #[instrument( - skip( - infcx, - body, - promoted, - region_bound_pairs, - borrowck_context, - universal_region_relations, - extra - ), + skip(infcx, body, promoted, region_bound_pairs, borrowck_context, extra), level = "debug" )] fn type_check_internal<'a, 'tcx, R>( @@ -272,7 +263,6 @@ region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, - universal_region_relations: &'a UniversalRegionRelations<'tcx>, extra: impl FnOnce(TypeChecker<'a, 'tcx>) -> R, ) -> R { let mut checker = TypeChecker::new( @@ -282,7 +272,6 @@ region_bound_pairs, implicit_region_bound, borrowck_context, - universal_region_relations, ); let errors_reported = { let mut verifier = TypeVerifier::new(&mut checker, body, promoted); @@ -667,7 +656,7 @@ // If the region is live at at least one location in the promoted MIR, // then add a liveness constraint to the main MIR for this region // at the location provided as an argument to this method - if let Some(_) = liveness_constraints.get_elements(region).next() { + if liveness_constraints.get_elements(region).next().is_some() { self.cx .borrowck_context .constraints @@ -901,15 +890,14 @@ implicit_region_bound: ty::Region<'tcx>, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, - universal_region_relations: &'a UniversalRegionRelations<'tcx>, } struct BorrowCheckContext<'a, 'tcx> { - universal_regions: &'a UniversalRegions<'tcx>, + pub(crate) universal_regions: &'a UniversalRegions<'tcx>, location_table: &'a LocationTable, all_facts: &'a mut Option<AllFacts>, borrow_set: &'a BorrowSet<'tcx>, - constraints: &'a mut MirTypeckRegionConstraints<'tcx>, + pub(crate) constraints: &'a mut MirTypeckRegionConstraints<'tcx>, upvars: &'a [Upvar<'tcx>], } @@ -1050,7 +1038,6 @@ region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, - universal_region_relations: &'a UniversalRegionRelations<'tcx>, ) -> Self { let mut checker = Self { infcx, @@ -1062,7 +1049,6 @@ implicit_region_bound, borrowck_context, reported_errors: Default::default(), - universal_region_relations, }; checker.check_user_type_annotations(); checker @@ -1171,6 +1157,7 @@ self.relate_types(sup, ty::Variance::Contravariant, sub, locations, category) } + #[instrument(skip(self, category), level = "debug")] fn eq_types( &mut self, expected: Ty<'tcx>, @@ -1322,8 +1309,6 @@ ), )?; - let universal_region_relations = self.universal_region_relations; - // Finally, if we instantiated the anon types successfully, we // have to solve any bounds (e.g., `-> impl Iterator` needs to // prove that `T: Iterator` where `T` is the type we @@ -1335,12 +1320,7 @@ ConstraintCategory::OpaqueType, CustomTypeOp::new( |infcx| { - infcx.constrain_opaque_type( - opaque_type_key, - &opaque_decl, - GenerateMemberConstraints::IfNoStaticBound, - universal_region_relations, - ); + infcx.constrain_opaque_type(opaque_type_key, &opaque_decl); Ok(InferOk { value: (), obligations: vec![] }) }, || "opaque_type_map".to_string(), @@ -1365,13 +1345,9 @@ // though. let category = match place.as_local() { Some(RETURN_PLACE) => { - if let BorrowCheckContext { - universal_regions: - UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. }, - .. - } = self.borrowck_context - { - if tcx.is_static(*def_id) { + let defining_ty = &self.borrowck_context.universal_regions.defining_ty; + if defining_ty.is_const() { + if tcx.is_static(defining_ty.def_id()) { ConstraintCategory::UseAsStatic } else { ConstraintCategory::UseAsConst @@ -1549,6 +1525,8 @@ } } TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { + self.check_operand(discr, term_location); + let discr_ty = discr.ty(body, tcx); if let Err(terr) = self.sub_types( discr_ty, @@ -1571,6 +1549,11 @@ // FIXME: check the values } TerminatorKind::Call { ref func, ref args, ref destination, from_hir_call, .. } => { + self.check_operand(func, term_location); + for arg in args { + self.check_operand(arg, term_location); + } + let func_ty = func.ty(body, tcx); debug!("check_terminator: call, func_ty={:?}", func_ty); let sig = match func_ty.kind() { @@ -1615,6 +1598,8 @@ self.check_call_inputs(body, term, &sig, args, term_location, from_hir_call); } TerminatorKind::Assert { ref cond, ref msg, .. } => { + self.check_operand(cond, term_location); + let cond_ty = cond.ty(body, tcx); if cond_ty != tcx.types.bool { span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); @@ -1630,6 +1615,8 @@ } } TerminatorKind::Yield { ref value, .. } => { + self.check_operand(value, term_location); + let value_ty = value.ty(body, tcx); match body.yield_ty() { None => span_mirbug!(self, term, "yield in non-generator"), @@ -1672,7 +1659,12 @@ Some(RETURN_PLACE) => { if let BorrowCheckContext { universal_regions: - UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. }, + UniversalRegions { + defining_ty: + DefiningTy::Const(def_id, _) + | DefiningTy::InlineConst(def_id, _), + .. + }, .. } = self.borrowck_context { @@ -1953,15 +1945,51 @@ } } + fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) { + if let Operand::Constant(constant) = op { + let maybe_uneval = match constant.literal { + ConstantKind::Ty(ct) => match ct.val { + ty::ConstKind::Unevaluated(uv) => Some(uv), + _ => None, + }, + _ => None, + }; + if let Some(uv) = maybe_uneval { + if uv.promoted.is_none() { + let tcx = self.tcx(); + let def_id = uv.def.def_id_for_type_of(); + if tcx.def_kind(def_id) == DefKind::InlineConst { + let predicates = self.prove_closure_bounds( + tcx, + def_id.expect_local(), + uv.substs(tcx), + location, + ); + self.normalize_and_prove_instantiated_predicates( + def_id, + predicates, + location.to_locations(), + ); + } + } + } + } + } + fn check_rvalue(&mut self, body: &Body<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { let tcx = self.tcx(); match rvalue { Rvalue::Aggregate(ak, ops) => { + for op in ops { + self.check_operand(op, location); + } self.check_aggregate_rvalue(&body, rvalue, ak, ops, location) } Rvalue::Repeat(operand, len) => { + self.check_operand(operand, location); + // If the length cannot be evaluated we must assume that the length can be larger // than 1. // If the length is larger than 1, the repeat expression will need to copy the @@ -2012,7 +2040,22 @@ } } - Rvalue::NullaryOp(_, ty) | Rvalue::ShallowInitBox(_, ty) => { + Rvalue::NullaryOp(_, ty) => { + let trait_ref = ty::TraitRef { + def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), + substs: tcx.mk_substs_trait(ty, &[]), + }; + + self.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::SizedBound, + ); + } + + Rvalue::ShallowInitBox(operand, ty) => { + self.check_operand(operand, location); + let trait_ref = ty::TraitRef { def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), substs: tcx.mk_substs_trait(ty, &[]), @@ -2026,6 +2069,8 @@ } Rvalue::Cast(cast_kind, op, ty) => { + self.check_operand(op, location); + match cast_kind { CastKind::Pointer(PointerCast::ReifyFnPointer) => { let fn_sig = op.ty(body, tcx).fn_sig(tcx); @@ -2272,6 +2317,9 @@ BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, box (left, right), ) => { + self.check_operand(left, location); + self.check_operand(right, location); + let ty_left = left.ty(body, tcx); match ty_left.kind() { // Types with regions are comparable if they have a common super-type. @@ -2322,13 +2370,19 @@ } } + Rvalue::Use(operand) | Rvalue::UnaryOp(_, operand) => { + self.check_operand(operand, location); + } + + Rvalue::BinaryOp(_, box (left, right)) + | Rvalue::CheckedBinaryOp(_, box (left, right)) => { + self.check_operand(left, location); + self.check_operand(right, location); + } + Rvalue::AddressOf(..) | Rvalue::ThreadLocalRef(..) - | Rvalue::Use(..) | Rvalue::Len(..) - | Rvalue::BinaryOp(..) - | Rvalue::CheckedBinaryOp(..) - | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) => {} } }
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index bebd193..b986df4 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -23,7 +23,7 @@ use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; +use rustc_middle::ty::{self, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt}; use std::iter; use crate::nll::ToRegionVid; @@ -108,6 +108,10 @@ /// is that it has no inputs and a single return value, which is /// the value of the constant. Const(DefId, SubstsRef<'tcx>), + + /// The MIR represents an inline const. The signature has no inputs and a + /// single return value found via `InlineConstSubsts::ty`. + InlineConst(DefId, SubstsRef<'tcx>), } impl<'tcx> DefiningTy<'tcx> { @@ -121,7 +125,7 @@ DefiningTy::Generator(_, substs, _) => { Either::Right(Either::Left(substs.as_generator().upvar_tys())) } - DefiningTy::FnDef(..) | DefiningTy::Const(..) => { + DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => { Either::Right(Either::Right(iter::empty())) } } @@ -133,22 +137,16 @@ pub fn implicit_inputs(self) -> usize { match self { DefiningTy::Closure(..) | DefiningTy::Generator(..) => 1, - DefiningTy::FnDef(..) | DefiningTy::Const(..) => 0, + DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => 0, } } pub fn is_fn_def(&self) -> bool { - match *self { - DefiningTy::FnDef(..) => true, - _ => false, - } + matches!(*self, DefiningTy::FnDef(..)) } pub fn is_const(&self) -> bool { - match *self { - DefiningTy::Const(..) => true, - _ => false, - } + matches!(*self, DefiningTy::Const(..) | DefiningTy::InlineConst(..)) } pub fn def_id(&self) -> DefId { @@ -156,7 +154,8 @@ DefiningTy::Closure(def_id, ..) | DefiningTy::Generator(def_id, ..) | DefiningTy::FnDef(def_id, ..) - | DefiningTy::Const(def_id, ..) => def_id, + | DefiningTy::Const(def_id, ..) + | DefiningTy::InlineConst(def_id, ..) => def_id, } } } @@ -248,7 +247,7 @@ tcx: TyCtxt<'tcx>, closure_substs: SubstsRef<'tcx>, expected_num_vars: usize, - closure_base_def_id: DefId, + typeck_root_def_id: DefId, ) -> IndexVec<RegionVid, ty::Region<'tcx>> { let mut region_mapping = IndexVec::with_capacity(expected_num_vars); region_mapping.push(tcx.lifetimes.re_static); @@ -256,7 +255,7 @@ region_mapping.push(fr); }); - for_each_late_bound_region_defined_on(tcx, closure_base_def_id, |r| { + for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| { region_mapping.push(r); }); @@ -350,8 +349,8 @@ // tests, and the resulting print-outs include def-ids // and other things that are not stable across tests! // So we just include the region-vid. Annoying. - let closure_base_def_id = tcx.closure_base_def_id(def_id); - for_each_late_bound_region_defined_on(tcx, closure_base_def_id, |r| { + let typeck_root_def_id = tcx.typeck_root_def_id(def_id); + for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| { err.note(&format!("late-bound region is {:?}", self.to_region_vid(r),)); }); } @@ -365,8 +364,8 @@ // FIXME: As above, we'd like to print out the region // `r` but doing so is not stable across architectures // and so forth. - let closure_base_def_id = tcx.closure_base_def_id(def_id); - for_each_late_bound_region_defined_on(tcx, closure_base_def_id, |r| { + let typeck_root_def_id = tcx.typeck_root_def_id(def_id); + for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| { err.note(&format!("late-bound region is {:?}", self.to_region_vid(r),)); }); } @@ -382,6 +381,12 @@ tcx.def_path_str_with_substs(def_id, substs), )); } + DefiningTy::InlineConst(def_id, substs) => { + err.note(&format!( + "defining inline constant type: {}", + tcx.def_path_str_with_substs(def_id, substs), + )); + } } } } @@ -417,7 +422,7 @@ let mut indices = self.compute_indices(fr_static, defining_ty); debug!("build: indices={:?}", indices); - let closure_base_def_id = self.infcx.tcx.closure_base_def_id(self.mir_def.did.to_def_id()); + let typeck_root_def_id = self.infcx.tcx.typeck_root_def_id(self.mir_def.did.to_def_id()); // If this is a closure or generator, then the late-bound regions from the enclosing // function are actually external regions to us. For example, here, 'a is not local @@ -425,7 +430,7 @@ // fn foo<'a>() { // let c = || { let x: &'a u32 = ...; } // } - if self.mir_def.did.to_def_id() != closure_base_def_id { + if self.mir_def.did.to_def_id() != typeck_root_def_id { self.infcx .replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices) } @@ -443,7 +448,7 @@ ); // Converse of above, if this is a function then the late-bound regions declared on its // signature are local to the fn. - if self.mir_def.did.to_def_id() == closure_base_def_id { + if self.mir_def.did.to_def_id() == typeck_root_def_id { self.infcx .replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices); } @@ -508,12 +513,12 @@ /// see `DefiningTy` for details. fn defining_ty(&self) -> DefiningTy<'tcx> { let tcx = self.infcx.tcx; - let closure_base_def_id = tcx.closure_base_def_id(self.mir_def.did.to_def_id()); + let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.did.to_def_id()); match tcx.hir().body_owner_kind(self.mir_hir_id) { BodyOwnerKind::Closure | BodyOwnerKind::Fn => { - let defining_ty = if self.mir_def.did.to_def_id() == closure_base_def_id { - tcx.type_of(closure_base_def_id) + let defining_ty = if self.mir_def.did.to_def_id() == typeck_root_def_id { + tcx.type_of(typeck_root_def_id) } else { let tables = tcx.typeck(self.mir_def.did); tables.node_type(self.mir_hir_id) @@ -540,11 +545,21 @@ } BodyOwnerKind::Const | BodyOwnerKind::Static(..) => { - assert_eq!(self.mir_def.did.to_def_id(), closure_base_def_id); - let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id); - let substs = - self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs); - DefiningTy::Const(self.mir_def.did.to_def_id(), substs) + let identity_substs = InternalSubsts::identity_for_item(tcx, typeck_root_def_id); + if self.mir_def.did.to_def_id() == typeck_root_def_id { + let substs = + self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs); + DefiningTy::Const(self.mir_def.did.to_def_id(), substs) + } else { + let ty = tcx.typeck(self.mir_def.did).node_type(self.mir_hir_id); + let substs = InlineConstSubsts::new( + tcx, + InlineConstSubstsParts { parent_substs: identity_substs, ty }, + ) + .substs; + let substs = self.infcx.replace_free_regions_with_nll_infer_vars(FR, substs); + DefiningTy::InlineConst(self.mir_def.did.to_def_id(), substs) + } } } } @@ -559,17 +574,19 @@ defining_ty: DefiningTy<'tcx>, ) -> UniversalRegionIndices<'tcx> { let tcx = self.infcx.tcx; - let closure_base_def_id = tcx.closure_base_def_id(self.mir_def.did.to_def_id()); - let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id); + let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.did.to_def_id()); + let identity_substs = InternalSubsts::identity_for_item(tcx, typeck_root_def_id); let fr_substs = match defining_ty { - DefiningTy::Closure(_, ref substs) | DefiningTy::Generator(_, ref substs, _) => { + DefiningTy::Closure(_, ref substs) + | DefiningTy::Generator(_, ref substs, _) + | DefiningTy::InlineConst(_, ref substs) => { // In the case of closures, we rely on the fact that // the first N elements in the ClosureSubsts are - // inherited from the `closure_base_def_id`. + // inherited from the `typeck_root_def_id`. // Therefore, when we zip together (below) with // `identity_substs`, we will get only those regions // that correspond to early-bound regions declared on - // the `closure_base_def_id`. + // the `typeck_root_def_id`. assert!(substs.len() >= identity_substs.len()); assert_eq!(substs.regions().count(), identity_substs.regions().count()); substs @@ -654,6 +671,12 @@ let ty = indices.fold_to_region_vids(tcx, ty); ty::Binder::dummy(tcx.intern_type_list(&[ty])) } + + DefiningTy::InlineConst(def_id, substs) => { + assert_eq!(self.mir_def.did.to_def_id(), def_id); + let ty = substs.as_inline_const().ty(); + ty::Binder::dummy(tcx.intern_type_list(&[ty])) + } } } } @@ -742,8 +765,8 @@ indices: &mut UniversalRegionIndices<'tcx>, ) { debug!("replace_late_bound_regions_with_nll_infer_vars(mir_def_id={:?})", mir_def_id); - let closure_base_def_id = self.tcx.closure_base_def_id(mir_def_id.to_def_id()); - for_each_late_bound_region_defined_on(self.tcx, closure_base_def_id, |r| { + let typeck_root_def_id = self.tcx.typeck_root_def_id(mir_def_id.to_def_id()); + for_each_late_bound_region_defined_on(self.tcx, typeck_root_def_id, |r| { debug!("replace_late_bound_regions_with_nll_infer_vars: r={:?}", r); if !indices.indices.contains_key(&r) { let region_vid = self.next_nll_region_var(FR);
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index c032364c..41662f4 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -19,7 +19,7 @@ operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxHashMap<Symbol, usize>, reg_args: FxHashSet<usize>, - clobber_abi: Option<(Symbol, Span)>, + clobber_abis: Vec<(Symbol, Span)>, options: ast::InlineAsmOptions, options_spans: Vec<Span>, } @@ -64,7 +64,7 @@ operands: vec![], named_args: FxHashMap::default(), reg_args: FxHashSet::default(), - clobber_abi: None, + clobber_abis: Vec::new(), options: ast::InlineAsmOptions::empty(), options_spans: vec![], }; @@ -210,9 +210,9 @@ .span_labels(args.options_spans.clone(), "previous options") .span_label(span, "argument") .emit(); - } else if let Some((_, abi_span)) = args.clobber_abi { + } else if let Some((_, abi_span)) = args.clobber_abis.last() { ecx.struct_span_err(span, "arguments are not allowed after clobber_abi") - .span_label(abi_span, "clobber_abi") + .span_label(*abi_span, "clobber_abi") .span_label(span, "argument") .emit(); } @@ -322,10 +322,13 @@ // Bail out now since this is likely to confuse MIR return Err(err); } - if let Some((_, abi_span)) = args.clobber_abi { + + if args.clobber_abis.len() > 0 { if is_global_asm { - let err = - ecx.struct_span_err(abi_span, "`clobber_abi` cannot be used with `global_asm!`"); + let err = ecx.struct_span_err( + args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(), + "`clobber_abi` cannot be used with `global_asm!`", + ); // Bail out now since this is likely to confuse later stages return Err(err); @@ -335,7 +338,10 @@ regclass_outputs.clone(), "asm with `clobber_abi` must specify explicit registers for outputs", ) - .span_label(abi_span, "clobber_abi") + .span_labels( + args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(), + "clobber_abi", + ) .span_labels(regclass_outputs, "generic outputs") .emit(); } @@ -439,37 +445,61 @@ p.expect(&token::OpenDelim(token::DelimToken::Paren))?; - let clobber_abi = match p.parse_str_lit() { - Ok(str_lit) => str_lit.symbol_unescaped, - Err(opt_lit) => { - let span = opt_lit.map_or(p.token.span, |lit| lit.span); - let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal"); - err.span_label(span, "not a string literal"); - return Err(err); - } - }; - - p.expect(&token::CloseDelim(token::DelimToken::Paren))?; - - let new_span = span_start.to(p.prev_token.span); - - if let Some((_, prev_span)) = args.clobber_abi { - let mut err = p - .sess - .span_diagnostic - .struct_span_err(new_span, "clobber_abi specified multiple times"); - err.span_label(prev_span, "clobber_abi previously specified here"); + if p.eat(&token::CloseDelim(token::DelimToken::Paren)) { + let err = p.sess.span_diagnostic.struct_span_err( + p.token.span, + "at least one abi must be provided as an argument to `clobber_abi`", + ); return Err(err); - } else if !args.options_spans.is_empty() { + } + + let mut new_abis = Vec::new(); + loop { + match p.parse_str_lit() { + Ok(str_lit) => { + new_abis.push((str_lit.symbol_unescaped, str_lit.span)); + } + Err(opt_lit) => { + // If the non-string literal is a closing paren then it's the end of the list and is fine + if p.eat(&token::CloseDelim(token::DelimToken::Paren)) { + break; + } + let span = opt_lit.map_or(p.token.span, |lit| lit.span); + let mut err = + p.sess.span_diagnostic.struct_span_err(span, "expected string literal"); + err.span_label(span, "not a string literal"); + return Err(err); + } + }; + + // Allow trailing commas + if p.eat(&token::CloseDelim(token::DelimToken::Paren)) { + break; + } + p.expect(&token::Comma)?; + } + + let full_span = span_start.to(p.prev_token.span); + + if !args.options_spans.is_empty() { let mut err = p .sess .span_diagnostic - .struct_span_err(new_span, "clobber_abi is not allowed after options"); + .struct_span_err(full_span, "clobber_abi is not allowed after options"); err.span_labels(args.options_spans.clone(), "options"); return Err(err); } - args.clobber_abi = Some((clobber_abi, new_span)); + match &new_abis[..] { + // should have errored above during parsing + [] => unreachable!(), + [(abi, _span)] => args.clobber_abis.push((*abi, full_span)), + [abis @ ..] => { + for (abi, span) in abis { + args.clobber_abis.push((*abi, *span)); + } + } + } Ok(()) } @@ -547,7 +577,7 @@ if let Some(snippet) = &template_snippet { if let Some(pos) = snippet.find(needle) { let end = pos - + &snippet[pos..] + + snippet[pos..] .find(|c| matches!(c, '\n' | ';' | '\\' | '"')) .unwrap_or(snippet[pos..].len() - 1); let inner = InnerSpan::new(pos, end); @@ -770,7 +800,7 @@ template, template_strs: template_strs.into_boxed_slice(), operands: args.operands, - clobber_abi: args.clobber_abi, + clobber_abis: args.clobber_abis, options: args.options, line_spans, }) @@ -812,10 +842,10 @@ Ok(args) => { if let Some(inline_asm) = expand_preparsed_asm(ecx, args) { MacEager::items(smallvec![P(ast::Item { - ident: Ident::invalid(), + ident: Ident::empty(), attrs: Vec::new(), id: ast::DUMMY_NODE_ID, - kind: ast::ItemKind::GlobalAsm(inline_asm), + kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), vis: ast::Visibility { span: sp.shrink_to_lo(), kind: ast::VisibilityKind::Inherited,
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 241c90c..31a35b9 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -85,7 +85,7 @@ fn dummy_annotatable() -> Annotatable { Annotatable::GenericParam(ast::GenericParam { id: ast::DUMMY_NODE_ID, - ident: Ident::invalid(), + ident: Ident::empty(), attrs: Default::default(), bounds: Default::default(), is_placeholder: false,
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index cd78c01..994a74a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -557,12 +557,12 @@ tokens: None, }, attrs: Vec::new(), - kind: ast::AssocItemKind::TyAlias(Box::new(ast::TyAliasKind( - ast::Defaultness::Final, - Generics::default(), - Vec::new(), - Some(type_def.to_ty(cx, self.span, type_ident, generics)), - ))), + kind: ast::AssocItemKind::TyAlias(Box::new(ast::TyAlias { + defaultness: ast::Defaultness::Final, + generics: Generics::default(), + bounds: Vec::new(), + ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)), + })), tokens: None, }) }); @@ -724,9 +724,9 @@ cx.item( self.span, - Ident::invalid(), + Ident::empty(), a, - ast::ItemKind::Impl(Box::new(ast::ImplKind { + ast::ItemKind::Impl(Box::new(ast::Impl { unsafety, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, @@ -955,7 +955,7 @@ decl: fn_decl, span: trait_.span, }; - let def = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Final; // Create the method. P(ast::AssocItem { @@ -968,12 +968,12 @@ tokens: None, }, ident: method_ident, - kind: ast::AssocItemKind::Fn(Box::new(ast::FnKind( - def, + kind: ast::AssocItemKind::Fn(Box::new(ast::Fn { + defaultness, sig, - fn_generics, - Some(body_block), - ))), + generics: fn_generics, + body: Some(body_block), + })), tokens: None, }) }
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index bcf9571..367a5aa 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -2,7 +2,7 @@ use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::{ImplKind, ItemKind, MetaItem}; +use rustc_ast::{Impl, ItemKind, MetaItem}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; @@ -178,9 +178,9 @@ let newitem = cx.item( span, - Ident::invalid(), + Ident::empty(), attrs, - ItemKind::Impl(Box::new(ImplKind { + ItemKind::Impl(Box::new(Impl { unsafety: ast::Unsafe::No, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final,
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index f0056cb..097eadd 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -527,17 +527,9 @@ self.verify_arg_type(Exact(idx), ty) } None => { - let capture_feature_enabled = self - .ecx - .ecfg - .features - .map_or(false, |features| features.format_args_capture); - // For the moment capturing variables from format strings expanded from macros is // disabled (see RFC #2795) - let can_capture = capture_feature_enabled && self.is_literal; - - if can_capture { + if self.is_literal { // Treat this name as a variable to capture from the surrounding scope let idx = self.args.len(); self.arg_types.push(Vec::new()); @@ -559,23 +551,15 @@ }; let mut err = self.ecx.struct_span_err(sp, &msg[..]); - if capture_feature_enabled && !self.is_literal { - err.note(&format!( - "did you intend to capture a variable `{}` from \ - the surrounding scope?", - name - )); - err.note( - "to avoid ambiguity, `format_args!` cannot capture variables \ - when the format string is expanded from a macro", - ); - } else if self.ecx.parse_sess().unstable_features.is_nightly_build() { - err.help(&format!( - "if you intended to capture `{}` from the surrounding scope, add \ - `#![feature(format_args_capture)]` to the crate attributes", - name - )); - } + err.note(&format!( + "did you intend to capture a variable `{}` from \ + the surrounding scope?", + name + )); + err.note( + "to avoid ambiguity, `format_args!` cannot capture variables \ + when the format string is expanded from a macro", + ); err.emit(); } @@ -760,16 +744,11 @@ /// Actually builds the expression which the format_args! block will be /// expanded to. fn into_expr(self) -> P<ast::Expr> { - let mut locals = - Vec::with_capacity((0..self.args.len()).map(|i| self.arg_unique_types[i].len()).sum()); - let mut counts = Vec::with_capacity(self.count_args.len()); - let mut pats = Vec::with_capacity(self.args.len()); + let mut args = Vec::with_capacity( + self.arg_unique_types.iter().map(|v| v.len()).sum::<usize>() + self.count_args.len(), + ); let mut heads = Vec::with_capacity(self.args.len()); - let names_pos: Vec<_> = (0..self.args.len()) - .map(|i| Ident::from_str_and_span(&format!("arg{}", i), self.macsp)) - .collect(); - // First, build up the static array which will become our precompiled // format "string" let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces); @@ -787,11 +766,8 @@ // of each variable because we don't want to move out of the arguments // passed to this function. for (i, e) in self.args.into_iter().enumerate() { - let name = names_pos[i]; - let span = self.ecx.with_def_site_ctxt(e.span); - pats.push(self.ecx.pat_ident(span, name)); for arg_ty in self.arg_unique_types[i].iter() { - locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, name)); + args.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, i)); } heads.push(self.ecx.expr_addr_of(e.span, e)); } @@ -800,15 +776,11 @@ Exact(i) => i, _ => panic!("should never happen"), }; - let name = names_pos[index]; let span = spans_pos[index]; - counts.push(Context::format_arg(self.ecx, self.macsp, span, &Count, name)); + args.push(Context::format_arg(self.ecx, self.macsp, span, &Count, index)); } - // Now create a vector containing all the arguments - let args = locals.into_iter().chain(counts.into_iter()); - - let args_array = self.ecx.expr_vec(self.macsp, args.collect()); + let args_array = self.ecx.expr_vec(self.macsp, args); // Constructs an AST equivalent to: // @@ -838,7 +810,7 @@ // But the nested match expression is proved to perform not as well // as series of let's; the first approach does. let args_match = { - let pat = self.ecx.pat_tuple(self.macsp, pats); + let pat = self.ecx.pat_ident(self.macsp, Ident::new(sym::_args, self.macsp)); let arm = self.ecx.arm(self.macsp, pat, args_array); let head = self.ecx.expr(self.macsp, ast::ExprKind::Tup(heads)); self.ecx.expr_match(self.macsp, head, vec![arm]) @@ -877,10 +849,11 @@ macsp: Span, mut sp: Span, ty: &ArgumentType, - arg: Ident, + arg_index: usize, ) -> P<ast::Expr> { sp = ecx.with_def_site_ctxt(sp); - let arg = ecx.expr_ident(sp, arg); + let arg = ecx.expr_ident(sp, Ident::new(sym::_args, sp)); + let arg = ecx.expr(sp, ast::ExprKind::Field(arg, Ident::new(sym::integer(arg_index), sp))); let trait_ = match *ty { Placeholder(trait_) if trait_ == "<invalid>" => return DummyResult::raw_expr(sp, true), Placeholder(trait_) => trait_,
diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 3f71ee6..a433876 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs
@@ -5,7 +5,7 @@ }; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Attribute, Expr, FnHeader, FnSig, Generics, Param, StmtKind}; -use rustc_ast::{FnKind, ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe}; +use rustc_ast::{Fn, ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -84,13 +84,13 @@ let decl = self.cx.fn_decl(abi_args, ast::FnRetTy::Ty(output_ty)); let header = FnHeader { unsafety: Unsafe::Yes(self.span), ..FnHeader::default() }; let sig = FnSig { decl, header, span: self.span }; - let block = Some(self.cx.block_expr(output_expr)); - let kind = ItemKind::Fn(Box::new(FnKind( - ast::Defaultness::Final, + let body = Some(self.cx.block_expr(output_expr)); + let kind = ItemKind::Fn(Box::new(Fn { + defaultness: ast::Defaultness::Final, sig, - Generics::default(), - block, - ))); + generics: Generics::default(), + body, + })); let item = self.cx.item( self.span, Ident::from_str_and_span(&self.kind.fn_name(method.name), self.span),
diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index e0d5726..e106f60 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
@@ -77,7 +77,7 @@ let use_item = cx.item( span, - Ident::invalid(), + Ident::empty(), vec![cx.attribute(cx.meta_word(span, sym::prelude_import))], ast::ItemKind::Use(ast::UseTree { prefix: cx.path(span, import_path),
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index bbca070..d262992 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -429,7 +429,7 @@ fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool { let has_should_panic_attr = cx.sess.contains_name(&i.attrs, sym::should_panic); let sd = &cx.sess.parse_sess.span_diagnostic; - if let ast::ItemKind::Fn(box ast::FnKind(_, ref sig, ref generics, _)) = i.kind { + if let ast::ItemKind::Fn(box ast::Fn { ref sig, ref generics, .. }) = i.kind { if let ast::Unsafe::Yes(span) = sig.header.unsafety { sd.struct_span_err(i.span, "unsafe functions cannot be used for tests") .span_label(span, "`unsafe` because of this") @@ -478,7 +478,7 @@ } fn has_bench_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool { - let has_sig = if let ast::ItemKind::Fn(box ast::FnKind(_, ref sig, _, _)) = i.kind { + let has_sig = if let ast::ItemKind::Fn(box ast::Fn { ref sig, .. }) = i.kind { // N.B., inadequate check, but we're running // well before resolve, can't get too deep. sig.decl.inputs.len() == 1
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index d791677..64ccd43 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -313,13 +313,13 @@ let decl = ecx.fn_decl(vec![], ast::FnRetTy::Ty(main_ret_ty)); let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp }; - let def = ast::Defaultness::Final; - let main = ast::ItemKind::Fn(Box::new(ast::FnKind( - def, + let defaultness = ast::Defaultness::Final; + let main = ast::ItemKind::Fn(Box::new(ast::Fn { + defaultness, sig, - ast::Generics::default(), - Some(main_body), - ))); + generics: ast::Generics::default(), + body: Some(main_body), + })); // Honor the reexport_test_harness_main attribute let main_id = match cx.reexport_test_harness_main {
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 32cc50ee..0a8d612 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -224,7 +224,7 @@ tcx, (backend_config.clone(), cgu.name()), module_codegen, - rustc_middle::dep_graph::hash_result, + Some(rustc_middle::dep_graph::hash_result), ); if let Some((id, product)) = work_product {
diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index ce428c5..2bbb199 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs
@@ -14,7 +14,7 @@ // TODO(antoyo) } - fn get_param(&self, index: usize) -> Self::Value { + fn get_param(&mut self, index: usize) -> Self::Value { self.cx.current_func.borrow().expect("current func") .get_param(index as i32) .to_rvalue()
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 3b77097..7c3ed3c 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs
@@ -118,7 +118,7 @@ true } - fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) { + fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span], _instance: Instance<'_>) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); let is_x86 = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64); let att_dialect = is_x86 && options.contains(InlineAsmOptions::ATT_SYNTAX);
diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 9f96096..a3b8d32 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs
@@ -59,7 +59,13 @@ let start_time = Instant::now(); let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); - let (module, _) = tcx.dep_graph.with_task(dep_node, tcx, cgu_name, module_codegen, dep_graph::hash_result); + let (module, _) = tcx.dep_graph.with_task( + dep_node, + tcx, + cgu_name, + module_codegen, + Some(dep_graph::hash_result), + ); let time_to_codegen = start_time.elapsed(); drop(prof_timer);
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index ac90841..fff2aa6 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -915,6 +915,16 @@ // TODO(antoyo) } + fn type_metadata(&mut self, _function: RValue<'gcc>, _typeid: String) { + // Unsupported. + } + + fn typeid_metadata(&mut self, _typeid: String) -> RValue<'gcc> { + // Unsupported. + self.context.new_rvalue_from_int(self.int_type, 0) + } + + fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> { self.store_with_flags(val, ptr, align, MemFlags::empty()) }
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 375d422..f3a2382 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
@@ -316,7 +316,7 @@ extended_asm.add_input_operand(None, "r", result.llval); extended_asm.add_clobber("memory"); extended_asm.set_volatile_flag(true); - + // We have copied the value to `result` already. return; } @@ -363,8 +363,9 @@ cond } - fn sideeffect(&mut self) { - // TODO(antoyo) + fn type_test(&mut self, _pointer: Self::Value, _typeid: Self::Value) -> Self::Value { + // Unsupported. + self.context.new_rvalue_from_int(self.int_type, 0) } fn va_start(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> {
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index a6a553b..5f3f533 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -11,7 +11,7 @@ bitflags = "1.0" cstr = "0.2" libc = "0.2" -measureme = "9.1.0" +measureme = "10.0.0" snap = "1" tracing = "0.1" rustc_middle = { path = "../rustc_middle" }
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index dca9c1f..07adfff 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -1,7 +1,6 @@ use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm::{self, AttributePlace}; -use crate::llvm_util; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; @@ -53,15 +52,10 @@ } fn should_use_mutable_noalias(cx: &CodegenCx<'_, '_>) -> bool { - // LLVM prior to version 12 has known miscompiles in the presence of - // noalias attributes (see #54878). Only enable mutable noalias by - // default for versions we believe to be safe. - cx.tcx - .sess - .opts - .debugging_opts - .mutable_noalias - .unwrap_or_else(|| llvm_util::get_version() >= (12, 0, 0)) + // LLVM prior to version 12 had known miscompiles in the presence of + // noalias attributes (see #54878), but we don't support earlier + // versions at all anymore. We now enable mutable noalias by default. + cx.tcx.sess.opts.debugging_opts.mutable_noalias.unwrap_or(true) } impl ArgAttributesExt for ArgAttributes { @@ -613,7 +607,7 @@ fn_abi.apply_attrs_callsite(self, callsite) } - fn get_param(&self, index: usize) -> Self::Value { + fn get_param(&mut self, index: usize) -> Self::Value { llvm::get_param(self.llfn(), index as c_uint) } }
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 341a888..02096f4 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::{bug, span_bug}; +use rustc_middle::{bug, span_bug, ty::Instance}; use rustc_span::{Pos, Span, Symbol}; use rustc_target::abi::*; use rustc_target::asm::*; @@ -120,6 +120,7 @@ operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, line_spans: &[Span], + instance: Instance<'_>, ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); @@ -135,7 +136,10 @@ let is_target_supported = |reg_class: InlineAsmRegClass| { for &(_, feature) in reg_class.supported_types(asm_arch) { if let Some(feature) = feature { - if self.tcx.sess.target_features.contains(&Symbol::intern(feature)) + let codegen_fn_attrs = self.tcx.codegen_fn_attrs(instance.def_id()); + let feature_name = Symbol::intern(feature); + if self.tcx.sess.target_features.contains(&feature_name) + || codegen_fn_attrs.target_features.contains(&feature_name) { return true; } @@ -316,7 +320,7 @@ InlineAsmArch::Mips | InlineAsmArch::Mips64 => {} InlineAsmArch::S390x => {} InlineAsmArch::SpirV => {} - InlineAsmArch::Wasm32 => {} + InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {} InlineAsmArch::Bpf => {} } }
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 659cf9e..8e6329a 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -12,7 +12,7 @@ use rustc_session::config::OptLevel; use rustc_session::Session; use rustc_target::spec::abi::Abi; -use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType}; +use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector}; use crate::attributes; use crate::llvm::AttributePlace::Function; @@ -161,6 +161,17 @@ } } +fn set_stackprotector(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { + let sspattr = match cx.sess().stack_protector() { + StackProtector::None => return, + StackProtector::All => Attribute::StackProtectReq, + StackProtector::Strong => Attribute::StackProtectStrong, + StackProtector::Basic => Attribute::StackProtect, + }; + + sspattr.apply_llfn(Function, llfn) +} + pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { let target_cpu = SmallCStr::new(llvm_util::target_cpu(cx.tcx.sess)); llvm::AddFunctionAttrStringValue( @@ -271,6 +282,7 @@ set_frame_pointer_type(cx, llfn); set_instrument_function(cx, llfn); set_probestack(cx, llfn); + set_stackprotector(cx, llfn); if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { Attribute::Cold.apply_llfn(Function, llfn);
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b6d682f..6d2ad70 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -17,6 +17,7 @@ }; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::small_c_str::SmallCStr; use rustc_errors::{FatalError, Handler, Level}; use rustc_fs_util::{link_or_copy, path_to_c_string}; @@ -53,6 +54,7 @@ output: &Path, dwo_output: Option<&Path>, file_type: llvm::FileType, + self_profiler_ref: &SelfProfilerRef, ) -> Result<(), FatalError> { unsafe { let output_c = path_to_c_string(output); @@ -76,6 +78,19 @@ file_type, ) }; + + // Record artifact sizes for self-profiling + if result == llvm::LLVMRustResult::Success { + let artifact_kind = match file_type { + llvm::FileType::ObjectFile => "object_file", + llvm::FileType::AssemblyFile => "assembly_file", + }; + record_artifact_size(self_profiler_ref, artifact_kind, output); + if let Some(dwo_file) = dwo_output { + record_artifact_size(self_profiler_ref, "dwo_file", dwo_file); + } + } + result.into_result().map_err(|()| { let msg = format!("could not write output to {}", output.display()); llvm_err(handler, &msg) @@ -161,6 +176,7 @@ let ffunction_sections = sess.opts.debugging_opts.function_sections.unwrap_or(sess.target.function_sections); let fdata_sections = ffunction_sections; + let funique_section_names = !sess.opts.debugging_opts.no_unique_section_names; let code_model = to_llvm_code_model(sess.code_model()); @@ -205,6 +221,7 @@ use_softfp, ffunction_sections, fdata_sections, + funique_section_names, trap_unreachable, singlethread, asm_comments, @@ -284,7 +301,7 @@ cookie = 0; } let level = match level { - llvm::DiagnosticLevel::Error => Level::Error, + llvm::DiagnosticLevel::Error => Level::Error { lint: false }, llvm::DiagnosticLevel::Warning => Level::Warning, llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note, }; @@ -746,6 +763,14 @@ let thin = ThinBuffer::new(llmod); let data = thin.data(); + if let Some(bitcode_filename) = bc_out.file_name() { + cgcx.prof.artifact_size( + "llvm_bitcode", + bitcode_filename.to_string_lossy(), + data.len() as u64, + ); + } + if config.emit_bc || config.emit_obj == EmitObj::Bitcode { let _timer = cgcx.prof.generic_activity_with_arg( "LLVM_module_codegen_emit_bitcode", @@ -806,6 +831,11 @@ } let result = llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback); + + if result == llvm::LLVMRustResult::Success { + record_artifact_size(&cgcx.prof, "llvm_ir", &out); + } + result.into_result().map_err(|()| { let msg = format!("failed to write LLVM IR to {}", out.display()); llvm_err(diag_handler, &msg) @@ -836,6 +866,7 @@ &path, None, llvm::FileType::AssemblyFile, + &cgcx.prof, ) })?; } @@ -869,6 +900,7 @@ &obj_out, dwo_out, llvm::FileType::ObjectFile, + &cgcx.prof, ) })?; } @@ -1125,3 +1157,19 @@ symbol_name.starts_with(b"__llvm_profile_") } } + +fn record_artifact_size( + self_profiler_ref: &SelfProfilerRef, + artifact_kind: &'static str, + path: &Path, +) { + // Don't stat the file if we are not going to record its size. + if !self_profiler_ref.enabled() { + return; + } + + if let Some(artifact_name) = path.file_name() { + let file_size = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0); + self_profiler_ref.artifact_size(artifact_kind, artifact_name.to_string_lossy(), file_size); + } +}
diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 3026c2f..8766cae 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs
@@ -113,8 +113,13 @@ let start_time = Instant::now(); let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); - let (module, _) = - tcx.dep_graph.with_task(dep_node, tcx, cgu_name, module_codegen, dep_graph::hash_result); + let (module, _) = tcx.dep_graph.with_task( + dep_node, + tcx, + cgu_name, + module_codegen, + Some(dep_graph::hash_result), + ); let time_to_codegen = start_time.elapsed(); // We assume that the cost to run LLVM on a CGU is proportional to
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index d5deacf..ff88302 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -604,6 +604,32 @@ } } + fn type_metadata(&mut self, function: &'ll Value, typeid: String) { + let typeid_metadata = self.typeid_metadata(typeid); + let v = [self.const_usize(0), typeid_metadata]; + unsafe { + llvm::LLVMGlobalSetMetadata( + function, + llvm::MD_type as c_uint, + llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( + self.cx.llcx, + v.as_ptr(), + v.len() as c_uint, + )), + ) + } + } + + fn typeid_metadata(&mut self, typeid: String) -> Self::Value { + unsafe { + llvm::LLVMMDStringInContext( + self.cx.llcx, + typeid.as_ptr() as *const c_char, + typeid.as_bytes().len() as c_uint, + ) + } + } + fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value { self.store_with_flags(val, ptr, align, MemFlags::empty()) } @@ -705,7 +731,7 @@ } fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - if llvm_util::get_version() >= (12, 0, 0) && !self.fptoint_sat_broken_in_llvm() { + if !self.fptoint_sat_broken_in_llvm() { let src_ty = self.cx.val_ty(val); let float_width = self.cx.float_width(src_ty); let int_width = self.cx.int_width(dest_ty); @@ -717,7 +743,7 @@ } fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - if llvm_util::get_version() >= (12, 0, 0) && !self.fptoint_sat_broken_in_llvm() { + if !self.fptoint_sat_broken_in_llvm() { let src_ty = self.cx.val_ty(val); let float_width = self.cx.float_width(src_ty); let int_width = self.cx.int_width(dest_ty); @@ -743,7 +769,7 @@ // we like. To ensure that LLVM picks the right instruction we choose // the raw wasm intrinsic functions which avoid LLVM inserting all the // other control flow automatically. - if self.sess().target.arch == "wasm32" { + if self.sess().target.is_like_wasm { let src_ty = self.cx.val_ty(val); if self.cx.type_kind(src_ty) != TypeKind::Vector { let float_width = self.cx.float_width(src_ty); @@ -765,7 +791,7 @@ fn fptosi(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { // see `fptoui` above for why wasm is different here - if self.sess().target.arch == "wasm32" { + if self.sess().target.is_like_wasm { let src_ty = self.cx.val_ty(val); if self.cx.type_kind(src_ty) != TypeKind::Vector { let float_width = self.cx.float_width(src_ty);
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 1afa6f0..b154ced 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -490,7 +490,7 @@ // Wasm statics with custom link sections get special treatment as they // go into custom sections of the wasm executable. - if self.tcx.sess.opts.target_triple.triple().starts_with("wasm32") { + if self.tcx.sess.target.is_like_wasm { if let Some(section) = attrs.link_section { let section = llvm::LLVMMDStringInContext( self.llcx,
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 257a0ac..613a8df 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -134,9 +134,6 @@ let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); let mut target_data_layout = sess.target.data_layout.clone(); - if llvm_util::get_version() < (12, 0, 0) && sess.target.arch == "powerpc64" { - target_data_layout = target_data_layout.replace("-v256:256:256-v512:512:512", ""); - } if llvm_util::get_version() < (13, 0, 0) { if sess.target.arch == "powerpc64" { target_data_layout = target_data_layout.replace("-S128", ""); @@ -221,6 +218,15 @@ llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1); } + if sess.is_sanitizer_cfi_enabled() { + // FIXME(rcvalle): Add support for non canonical jump tables. + let canonical_jump_tables = "CFI Canonical Jump Tables\0".as_ptr().cast(); + // FIXME(rcvalle): Add it with Override behavior flag--LLVMRustAddModuleFlag adds it with + // Warning behavior flag. Add support for specifying the behavior flag to + // LLVMRustAddModuleFlag. + llvm::LLVMRustAddModuleFlag(llmod, canonical_jump_tables, 1); + } + // Control Flow Guard is currently only supported by the MSVC linker on Windows. if sess.target.is_like_msvc { match sess.opts.cg.control_flow_guard { @@ -591,7 +597,6 @@ ifn!("llvm.trap", fn() -> void); ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> i8p); - ifn!("llvm.sideeffect", fn() -> void); ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); @@ -779,6 +784,8 @@ ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void); } + ifn!("llvm.type.test", fn(i8p, self.type_metadata()) -> i1); + if self.sess().opts.debuginfo != DebugInfo::None { ifn!("llvm.dbg.declare", fn(self.type_metadata(), self.type_metadata()) -> void); ifn!("llvm.dbg.value", fn(self.type_metadata(), t_i64, self.type_metadata()) -> void);
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 1f1bd73..2a6bf7d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -322,7 +322,7 @@ type_names::push_item_name(self.tcx(), def_id, false, &mut name); // Find the enclosing function, in case this is a closure. - let enclosing_fn_def_id = self.tcx().closure_base_def_id(def_id); + let enclosing_fn_def_id = self.tcx().typeck_root_def_id(def_id); // Get_template_parameters() will append a `<...>` clause to the function // name if necessary.
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index c43141c..a7e34b0 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -19,7 +19,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::{sym, symbol::kw, Span, Symbol}; -use rustc_target::abi::{self, HasDataLayout, Primitive}; +use rustc_target::abi::{self, Align, HasDataLayout, Primitive}; use rustc_target::spec::{HasTargetSpec, PanicStrategy}; use std::cmp::Ordering; @@ -392,13 +392,12 @@ self.call_intrinsic("llvm.expect.i1", &[cond, self.const_bool(expected)]) } - fn sideeffect(&mut self) { - // This kind of check would make a ton of sense in the caller, but currently the only - // caller of this function is in `rustc_codegen_ssa`, which is agnostic to whether LLVM - // codegen backend being used, and so is unable to check the LLVM version. - if unsafe { llvm::LLVMRustVersionMajor() } < 12 { - self.call_intrinsic("llvm.sideeffect", &[]); - } + fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value { + // Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time + // optimization pass replaces calls to this intrinsic with code to test type membership. + let i8p_ty = self.type_i8p(); + let bitcast = self.bitcast(pointer, i8p_ty); + self.call_intrinsic("llvm.type.test", &[bitcast, typeid]) } fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value { @@ -849,28 +848,39 @@ let arg_tys = sig.inputs(); if name == sym::simd_select_bitmask { - let in_ty = arg_tys[0]; - let m_len = match in_ty.kind() { - // Note that this `.unwrap()` crashes for isize/usize, that's sort - // of intentional as there's not currently a use case for that. - ty::Int(i) => i.bit_width().unwrap(), - ty::Uint(i) => i.bit_width().unwrap(), - _ => return_error!("`{}` is not an integral type", in_ty), - }; require_simd!(arg_tys[1], "argument"); - let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); - require!( - // Allow masks for vectors with fewer than 8 elements to be - // represented with a u8 or i8. - m_len == v_len || (m_len == 8 && v_len < 8), - "mismatched lengths: mask length `{}` != other vector length `{}`", - m_len, - v_len - ); + let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); + + let expected_int_bits = (len.max(8) - 1).next_power_of_two(); + let expected_bytes = len / 8 + ((len % 8 > 0) as u64); + + let mask_ty = arg_tys[0]; + let mask = match mask_ty.kind() { + ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(), + ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(), + ty::Array(elem, len) + if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) + && len.try_eval_usize(bx.tcx, ty::ParamEnv::reveal_all()) + == Some(expected_bytes) => + { + let place = PlaceRef::alloca(bx, args[0].layout); + args[0].val.store(bx, place); + let int_ty = bx.type_ix(expected_bytes * 8); + let ptr = bx.pointercast(place.llval, bx.cx.type_ptr_to(int_ty)); + bx.load(int_ty, ptr, Align::ONE) + } + _ => return_error!( + "invalid bitmask `{}`, expected `u{}` or `[u8; {}]`", + mask_ty, + expected_int_bits, + expected_bytes + ), + }; + let i1 = bx.type_i1(); - let im = bx.type_ix(v_len); - let i1xn = bx.type_vector(i1, v_len); - let m_im = bx.trunc(args[0].immediate(), im); + let im = bx.type_ix(len); + let i1xn = bx.type_vector(i1, len); + let m_im = bx.trunc(mask, im); let m_i1s = bx.bitcast(m_im, i1xn); return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); } @@ -1048,16 +1058,16 @@ if name == sym::simd_bitmask { // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a - // vector mask and returns an unsigned integer containing the most - // significant bit (MSB) of each lane. - - // If the vector has less than 8 lanes, a u8 is returned with zeroed - // trailing bits. + // vector mask and returns the most significant bit (MSB) of each lane in the form + // of either: + // * an unsigned integer + // * an array of `u8` + // If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits. + // + // The bit order of the result depends on the byte endianness, LSB-first for little + // endian and MSB-first for big endian. let expected_int_bits = in_len.max(8); - match ret_ty.kind() { - ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => (), - _ => return_error!("bitmask `{}`, expected `u{}`", ret_ty, expected_int_bits), - } + let expected_bytes = expected_int_bits / 8 + ((expected_int_bits % 8 > 0) as u64); // Integer vector <i{in_bitwidth} x in_len>: let (i_xn, in_elem_bitwidth) = match in_elem.kind() { @@ -1087,8 +1097,34 @@ let i1xn = bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len)); // Bitcast <i1 x N> to iN: let i_ = bx.bitcast(i1xn, bx.type_ix(in_len)); - // Zero-extend iN to the bitmask type: - return Ok(bx.zext(i_, bx.type_ix(expected_int_bits))); + + match ret_ty.kind() { + ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => { + // Zero-extend iN to the bitmask type: + return Ok(bx.zext(i_, bx.type_ix(expected_int_bits))); + } + ty::Array(elem, len) + if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) + && len.try_eval_usize(bx.tcx, ty::ParamEnv::reveal_all()) + == Some(expected_bytes) => + { + // Zero-extend iN to the array lengh: + let ze = bx.zext(i_, bx.type_ix(expected_bytes * 8)); + + // Convert the integer to a byte array + let ptr = bx.alloca(bx.type_ix(expected_bytes * 8), Align::ONE); + bx.store(ze, ptr, Align::ONE); + let array_ty = bx.type_array(bx.type_i8(), expected_bytes); + let ptr = bx.pointercast(ptr, bx.cx.type_ptr_to(array_ty)); + return Ok(bx.load(array_ty, ptr, Align::ONE)); + } + _ => return_error!( + "cannot return `{}`, expected `u{}` or `[u8; {}]`", + ret_ty, + expected_int_bits, + expected_bytes + ), + } } fn simd_simple_float_intrinsic(
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 8f4d79e..c66d7d8 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -76,6 +76,27 @@ #[derive(Clone)] pub struct LlvmCodegenBackend(()); +struct TimeTraceProfiler { + enabled: bool, +} + +impl TimeTraceProfiler { + fn new(enabled: bool) -> Self { + if enabled { + unsafe { llvm::LLVMTimeTraceProfilerInitialize() } + } + TimeTraceProfiler { enabled } + } +} + +impl Drop for TimeTraceProfiler { + fn drop(&mut self) { + if self.enabled { + unsafe { llvm::LLVMTimeTraceProfilerFinishThread() } + } + } +} + impl ExtraBackendMethods for LlvmCodegenBackend { fn new_metadata(&self, tcx: TyCtxt<'_>, mod_name: &str) -> ModuleLlvm { ModuleLlvm::new_metadata(tcx, mod_name) @@ -119,6 +140,34 @@ fn tune_cpu<'b>(&self, sess: &'b Session) -> Option<&'b str> { llvm_util::tune_cpu(sess) } + + fn spawn_thread<F, T>(time_trace: bool, f: F) -> std::thread::JoinHandle<T> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + std::thread::spawn(move || { + let _profiler = TimeTraceProfiler::new(time_trace); + f() + }) + } + + fn spawn_named_thread<F, T>( + time_trace: bool, + name: String, + f: F, + ) -> std::io::Result<std::thread::JoinHandle<T>> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + std::thread::Builder::new().name(name).spawn(move || { + let _profiler = TimeTraceProfiler::new(time_trace); + f() + }) + } } impl WriteBackendMethods for LlvmCodegenBackend { @@ -239,6 +288,31 @@ } println!(); } + PrintRequest::StackProtectorStrategies => { + println!( + r#"Available stack protector strategies: + all + Generate stack canaries in all functions. + + strong + Generate stack canaries in a function if it either: + - has a local variable of `[T; N]` type, regardless of `T` and `N` + - takes the address of a local variable. + + (Note that a local variable being borrowed is not equivalent to its + address being taken: e.g. some borrows may be removed by optimization, + while by-value argument passing may be implemented with reference to a + local stack variable in the ABI.) + + basic + Generate stack canaries in functions with: + - local variables of `[T; N]` type, where `T` is byte-sized and `N` > 8. + + none + Do not generate stack canaries. +"# + ); + } req => llvm_util::print(req, sess), } }
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 4c9ae4f..1d255c0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -166,6 +166,9 @@ InaccessibleMemOnly = 27, SanitizeHWAddress = 28, WillReturn = 29, + StackProtectReq = 30, + StackProtectStrong = 31, + StackProtect = 32, } /// LLVMIntPredicate @@ -416,6 +419,7 @@ MD_nontemporal = 9, MD_mem_parallel_loop_access = 10, MD_nonnull = 11, + MD_type = 19, } /// LLVMRustAsmDialect @@ -1002,6 +1006,8 @@ pub fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t); pub fn LLVMReplaceAllUsesWith(OldVal: &'a Value, NewVal: &'a Value); pub fn LLVMSetMetadata(Val: &'a Value, KindID: c_uint, Node: &'a Value); + pub fn LLVMGlobalSetMetadata(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata); + pub fn LLVMValueAsMetadata(Node: &'a Value) -> &Metadata; // Operations on constants of any type pub fn LLVMConstNull(Ty: &Type) -> &Value; @@ -1734,6 +1740,8 @@ pub fn LLVMTimeTraceProfilerInitialize(); + pub fn LLVMTimeTraceProfilerFinishThread(); + pub fn LLVMTimeTraceProfilerFinish(FileName: *const c_char); pub fn LLVMAddAnalysisPasses(T: &'a TargetMachine, PM: &PassManager<'a>); @@ -1770,7 +1778,7 @@ pub fn LLVMDisposeMessage(message: *mut c_char); - pub fn LLVMStartMultithreaded() -> Bool; + pub fn LLVMIsMultithreaded() -> Bool; /// Returns a string describing the last error caused by an LLVMRust* call. pub fn LLVMRustGetLastError() -> *const c_char; @@ -2187,6 +2195,7 @@ UseSoftFP: bool, FunctionSections: bool, DataSections: bool, + UniqueSectionNames: bool, TrapUnreachable: bool, Singlethread: bool, AsmComments: bool,
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index c2136f1..3393c9b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -17,35 +17,25 @@ use std::ptr; use std::slice; use std::str; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Once; -static POISONED: AtomicBool = AtomicBool::new(false); static INIT: Once = Once::new(); pub(crate) fn init(sess: &Session) { unsafe { // Before we touch LLVM, make sure that multithreading is enabled. + if llvm::LLVMIsMultithreaded() != 1 { + bug!("LLVM compiled without support for threads"); + } INIT.call_once(|| { - if llvm::LLVMStartMultithreaded() != 1 { - // use an extra bool to make sure that all future usage of LLVM - // cannot proceed despite the Once not running more than once. - POISONED.store(true, Ordering::SeqCst); - } - configure_llvm(sess); }); - - if POISONED.load(Ordering::SeqCst) { - bug!("couldn't enable multi-threaded LLVM"); - } } } fn require_inited() { - INIT.call_once(|| bug!("llvm is not initialized")); - if POISONED.load(Ordering::SeqCst) { - bug!("couldn't enable multi-threaded LLVM"); + if !INIT.is_completed() { + bug!("LLVM is not initialized"); } } @@ -85,7 +75,9 @@ if sess.print_llvm_passes() { add("-debug-pass=Structure", false); } - if !sess.opts.debugging_opts.no_generate_arange_section { + if sess.target.generate_arange_section + && !sess.opts.debugging_opts.no_generate_arange_section + { add("-generate-arange-section", false); } @@ -95,8 +87,7 @@ // Ref: // - https://github.com/rust-lang/rust/issues/85351 // - https://reviews.llvm.org/D103167 - let llvm_version = llvm_util::get_version(); - if llvm_version >= (11, 0, 0) && llvm_version < (13, 0, 0) { + if llvm_util::get_version() < (13, 0, 0) { add("-enable-machine-outliner=never", false); } @@ -124,11 +115,6 @@ } if sess.opts.debugging_opts.llvm_time_trace { - // time-trace is not thread safe and running it in parallel will cause seg faults. - if !sess.opts.debugging_opts.no_parallel_llvm { - bug!("`-Z llvm-time-trace` requires `-Z no-parallel-llvm") - } - llvm::LLVMTimeTraceProfilerInitialize(); } @@ -191,6 +177,7 @@ ("aarch64", "dpb2") => vec!["ccdp"], ("aarch64", "frintts") => vec!["fptoint"], ("aarch64", "fcma") => vec!["complxnum"], + ("aarch64", "pmuv3") => vec!["perfmon"], (_, s) => vec![s], } } @@ -416,14 +403,6 @@ // -Ctarget-features features.extend(sess.opts.cg.target_feature.split(',').flat_map(&filter)); - // FIXME: Move outline-atomics to target definition when earliest supported LLVM is 12. - if get_version() >= (12, 0, 0) - && sess.target.llvm_target.contains("aarch64-unknown-linux") - && sess.target.llvm_target != "aarch64-unknown-linux-musl" - { - features.push("+outline-atomics".to_string()); - } - features }
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index cf1c605..638b2a7 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -121,6 +121,19 @@ if sess.opts.json_artifact_notifications { sess.parse_sess.span_diagnostic.emit_artifact_notification(&out_filename, "link"); } + + if sess.prof.enabled() { + if let Some(artifact_name) = out_filename.file_name() { + // Record size for self-profiling + let file_size = std::fs::metadata(&out_filename).map(|m| m.len()).unwrap_or(0); + + sess.prof.artifact_size( + "linked_artifact", + artifact_name.to_string_lossy(), + file_size, + ); + } + } } } @@ -174,9 +187,8 @@ _ => {} } } - let fmts = match fmts { - Some(f) => f, - None => return Err("could not find formats for rlibs".to_string()), + let Some(fmts) = fmts else { + return Err("could not find formats for rlibs".to_string()); }; for &cnum in crates { match fmts.get(cnum.as_usize() - 1) { @@ -1022,8 +1034,10 @@ SplitDebuginfo::Packed => link_dwarf_object(sess, &out_filename), } + let strip = strip_value(sess); + if sess.target.is_like_osx { - match sess.opts.debugging_opts.strip { + match strip { Strip::Debuginfo => strip_symbols_in_osx(sess, &out_filename, Some("-S")), Strip::Symbols => strip_symbols_in_osx(sess, &out_filename, None), Strip::None => {} @@ -1031,6 +1045,14 @@ } } +// Temporarily support both -Z strip and -C strip +fn strip_value(sess: &Session) -> Strip { + match (sess.opts.debugging_opts.strip, sess.opts.cg.strip) { + (s, Strip::None) => s, + (_, s) => s, + } +} + fn strip_symbols_in_osx<'a>(sess: &'a Session, out_filename: &Path, option: Option<&str>) { let mut cmd = Command::new("strip"); if let Some(option) = option { @@ -1096,10 +1118,10 @@ } fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { - fn find_sanitizer_runtime(sess: &Session, filename: &String) -> PathBuf { + fn find_sanitizer_runtime(sess: &Session, filename: &str) -> PathBuf { let session_tlib = filesearch::make_target_lib_path(&sess.sysroot, sess.opts.target_triple.triple()); - let path = session_tlib.join(&filename); + let path = session_tlib.join(filename); if path.exists() { return session_tlib; } else { @@ -2002,7 +2024,7 @@ cmd.optimize(); // Pass debuginfo and strip flags down to the linker. - cmd.debuginfo(sess.opts.debugging_opts.strip); + cmd.debuginfo(strip_value(sess)); // We want to prevent the compiler from accidentally leaking in any system libraries, // so by default we tell linkers not to link to any default libraries.
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 429dc45..15d16e7 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -219,19 +219,36 @@ } impl<'a> GccLinker<'a> { - /// Argument that must be passed *directly* to the linker + /// Passes an argument directly to the linker. /// - /// These arguments need to be prepended with `-Wl`, when a GCC-style linker is used. - fn linker_arg<S>(&mut self, arg: S) -> &mut Self - where - S: AsRef<OsStr>, - { - if !self.is_ld { - let mut os = OsString::from("-Wl,"); - os.push(arg.as_ref()); - self.cmd.arg(os); + /// When the linker is not ld-like such as when using a compiler as a linker, the argument is + /// prepended by `-Wl,`. + fn linker_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { + self.linker_args(&[arg]); + self + } + + /// Passes a series of arguments directly to the linker. + /// + /// When the linker is ld-like, the arguments are simply appended to the command. When the + /// linker is not ld-like such as when using a compiler as a linker, the arguments are joined by + /// commas to form an argument that is then prepended with `-Wl`. In this situation, only a + /// single argument is appended to the command to ensure that the order of the arguments is + /// preserved by the compiler. + fn linker_args(&mut self, args: &[impl AsRef<OsStr>]) -> &mut Self { + if self.is_ld { + args.into_iter().for_each(|a| { + self.cmd.arg(a); + }); } else { - self.cmd.arg(arg); + if !args.is_empty() { + let mut s = OsString::from("-Wl"); + for a in args { + s.push(","); + s.push(a); + } + self.cmd.arg(s); + } } self } @@ -289,14 +306,19 @@ if let Some(path) = &self.sess.opts.debugging_opts.profile_sample_use { self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display())); }; - self.linker_arg(&format!("-plugin-opt={}", opt_level)); - self.linker_arg(&format!("-plugin-opt=mcpu={}", self.target_cpu)); + self.linker_args(&[ + &format!("-plugin-opt={}", opt_level), + &format!("-plugin-opt=mcpu={}", self.target_cpu), + ]); } fn build_dylib(&mut self, out_filename: &Path) { // On mac we need to tell the linker to let this library be rpathed if self.sess.target.is_like_osx { - self.cmd.arg("-dynamiclib"); + if !self.is_ld { + self.cmd.arg("-dynamiclib"); + } + self.linker_arg("-dylib"); // Note that the `osx_rpath_install_name` option here is a hack @@ -304,10 +326,9 @@ // principled solution at some point to force the compiler to pass // the right `-Wl,-install_name` with an `@rpath` in it. if self.sess.opts.cg.rpath || self.sess.opts.debugging_opts.osx_rpath_install_name { - self.linker_arg("-install_name"); - let mut v = OsString::from("@rpath/"); - v.push(out_filename.file_name().unwrap()); - self.linker_arg(&v); + let mut rpath = OsString::from("@rpath/"); + rpath.push(out_filename.file_name().unwrap()); + self.linker_args(&[OsString::from("-install_name"), rpath]); } } else { self.cmd.arg("-shared"); @@ -381,8 +402,7 @@ self.build_dylib(out_filename); } LinkOutputKind::WasiReactorExe => { - self.linker_arg("--entry"); - self.linker_arg("_initialize"); + self.linker_args(&["--entry", "_initialize"]); } } // VxWorks compiler driver introduced `--static-crt` flag specifically for rustc, @@ -454,8 +474,7 @@ self.cmd.arg(path); } fn full_relro(&mut self) { - self.linker_arg("-zrelro"); - self.linker_arg("-znow"); + self.linker_args(&["-zrelro", "-znow"]); } fn partial_relro(&mut self) { self.linker_arg("-zrelro"); @@ -639,7 +658,6 @@ } let is_windows = self.sess.target.is_like_windows; - let mut arg = OsString::new(); let path = tmpdir.join(if is_windows { "list.def" } else { "list" }); debug!("EXPORTED SYMBOLS:"); @@ -691,27 +709,18 @@ } if self.sess.target.is_like_osx { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("-exported_symbols_list,"); + self.linker_args(&[OsString::from("-exported_symbols_list"), path.into()]); } else if self.sess.target.is_like_solaris { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("-M,"); + self.linker_args(&[OsString::from("-M"), path.into()]); } else { - if !self.is_ld { - arg.push("-Wl,") - } - // Both LD and LLD accept export list in *.def file form, there are no flags required - if !is_windows { - arg.push("--version-script=") + if is_windows { + self.linker_arg(path); + } else { + let mut arg = OsString::from("--version-script="); + arg.push(path); + self.linker_arg(arg); } } - - arg.push(&path); - self.cmd.arg(arg); } fn subsystem(&mut self, subsystem: &str) { @@ -769,8 +778,7 @@ self.linker_arg("--as-needed"); } else if self.sess.target.is_like_solaris { // -z ignore is the Solaris equivalent to the GNU ld --as-needed option - self.linker_arg("-z"); - self.linker_arg("ignore"); + self.linker_args(&["-z", "ignore"]); } } }
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 4a7090b3..f80f996 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -11,7 +11,7 @@ use rustc_middle::middle::exported_symbols::{ metadata_symbol_name, ExportedSymbol, SymbolExportLevel, }; -use rustc_middle::ty::query::Providers; +use rustc_middle::ty::query::{ExternProviders, Providers}; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::Instance; use rustc_middle::ty::{SymbolName, TyCtxt}; @@ -363,7 +363,7 @@ providers.wasm_import_module_map = wasm_import_module_map; } -pub fn provide_extern(providers: &mut Providers) { +pub fn provide_extern(providers: &mut ExternProviders) { providers.is_reachable_non_generic = is_reachable_non_generic_provider_extern; providers.upstream_monomorphizations_for = upstream_monomorphizations_for_provider; }
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index da34612..85d51ea 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -310,6 +310,7 @@ pub no_landing_pads: bool, pub save_temps: bool, pub fewer_names: bool, + pub time_trace: bool, pub exported_symbols: Option<Arc<ExportedSymbols>>, pub opts: Arc<config::Options>, pub crate_types: Vec<CrateType>, @@ -1039,6 +1040,7 @@ no_landing_pads: sess.panic_strategy() == PanicStrategy::Abort, fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps, + time_trace: sess.opts.debugging_opts.llvm_time_trace, opts: Arc::new(sess.opts.clone()), prof: sess.prof.clone(), exported_symbols, @@ -1198,7 +1200,7 @@ // Each LLVM module is automatically sent back to the coordinator for LTO if // necessary. There's already optimizations in place to avoid sending work // back to the coordinator if LTO isn't requested. - return thread::spawn(move || { + return B::spawn_thread(cgcx.time_trace, move || { let mut worker_id_counter = 0; let mut free_worker_ids = Vec::new(); let mut get_worker_id = |free_worker_ids: &mut Vec<usize>| { @@ -1615,59 +1617,57 @@ pub struct WorkerFatalError; fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>) { - let builder = thread::Builder::new().name(work.short_description()); - builder - .spawn(move || { - // Set up a destructor which will fire off a message that we're done as - // we exit. - struct Bomb<B: ExtraBackendMethods> { - coordinator_send: Sender<Box<dyn Any + Send>>, - result: Option<Result<WorkItemResult<B>, FatalError>>, - worker_id: usize, + B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { + // Set up a destructor which will fire off a message that we're done as + // we exit. + struct Bomb<B: ExtraBackendMethods> { + coordinator_send: Sender<Box<dyn Any + Send>>, + result: Option<Result<WorkItemResult<B>, FatalError>>, + worker_id: usize, + } + impl<B: ExtraBackendMethods> Drop for Bomb<B> { + fn drop(&mut self) { + let worker_id = self.worker_id; + let msg = match self.result.take() { + Some(Ok(WorkItemResult::Compiled(m))) => { + Message::Done::<B> { result: Ok(m), worker_id } + } + Some(Ok(WorkItemResult::NeedsLink(m))) => { + Message::NeedsLink::<B> { module: m, worker_id } + } + Some(Ok(WorkItemResult::NeedsFatLTO(m))) => { + Message::NeedsFatLTO::<B> { result: m, worker_id } + } + Some(Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))) => { + Message::NeedsThinLTO::<B> { name, thin_buffer, worker_id } + } + Some(Err(FatalError)) => { + Message::Done::<B> { result: Err(Some(WorkerFatalError)), worker_id } + } + None => Message::Done::<B> { result: Err(None), worker_id }, + }; + drop(self.coordinator_send.send(Box::new(msg))); } - impl<B: ExtraBackendMethods> Drop for Bomb<B> { - fn drop(&mut self) { - let worker_id = self.worker_id; - let msg = match self.result.take() { - Some(Ok(WorkItemResult::Compiled(m))) => { - Message::Done::<B> { result: Ok(m), worker_id } - } - Some(Ok(WorkItemResult::NeedsLink(m))) => { - Message::NeedsLink::<B> { module: m, worker_id } - } - Some(Ok(WorkItemResult::NeedsFatLTO(m))) => { - Message::NeedsFatLTO::<B> { result: m, worker_id } - } - Some(Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))) => { - Message::NeedsThinLTO::<B> { name, thin_buffer, worker_id } - } - Some(Err(FatalError)) => { - Message::Done::<B> { result: Err(Some(WorkerFatalError)), worker_id } - } - None => Message::Done::<B> { result: Err(None), worker_id }, - }; - drop(self.coordinator_send.send(Box::new(msg))); - } - } + } - let mut bomb = Bomb::<B> { - coordinator_send: cgcx.coordinator_send.clone(), - result: None, - worker_id: cgcx.worker, - }; + let mut bomb = Bomb::<B> { + coordinator_send: cgcx.coordinator_send.clone(), + result: None, + worker_id: cgcx.worker, + }; - // Execute the work itself, and if it finishes successfully then flag - // ourselves as a success as well. - // - // Note that we ignore any `FatalError` coming out of `execute_work_item`, - // as a diagnostic was already sent off to the main thread - just - // surface that there was an error in this worker. - bomb.result = { - let _prof_timer = work.start_profiling(&cgcx); - Some(execute_work_item(&cgcx, work)) - }; - }) - .expect("failed to spawn thread"); + // Execute the work itself, and if it finishes successfully then flag + // ourselves as a success as well. + // + // Note that we ignore any `FatalError` coming out of `execute_work_item`, + // as a diagnostic was already sent off to the main thread - just + // surface that there was an error in this worker. + bomb.result = { + let _prof_timer = work.start_profiling(&cgcx); + Some(execute_work_item(&cgcx, work)) + }; + }) + .expect("failed to spawn thread"); } enum SharedEmitterMessage { @@ -1757,7 +1757,7 @@ let msg = msg.strip_prefix("error: ").unwrap_or(&msg); let mut err = match level { - Level::Error => sess.struct_err(&msg), + Level::Error { lint: false } => sess.struct_err(&msg), Level::Warning => sess.struct_warn(&msg), Level::Note => sess.struct_note_without_error(&msg), _ => bug!("Invalid inline asm diagnostic level"),
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index accb54e..ab119ae 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -124,10 +124,7 @@ // info for MSVC debugger. However, wrapping these types' names in a synthetic type // causes the .natvis engine for WinDbg to fail to display their data, so we opt these // types out to aid debugging in MSVC. - let is_slice_or_str = match *inner_type.kind() { - ty::Slice(_) | ty::Str => true, - _ => false, - }; + let is_slice_or_str = matches!(*inner_type.kind(), ty::Slice(_) | ty::Str); if !cpp_like_names { output.push('&');
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index b759e3a..4c87d4d 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -3,6 +3,7 @@ #![feature(box_patterns)] #![feature(try_blocks)] #![feature(in_band_lifetimes)] +#![feature(let_else)] #![feature(once_cell)] #![feature(nll)] #![feature(associated_type_bounds)] @@ -26,7 +27,7 @@ use rustc_hir::LangItem; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::dependency_format::Dependencies; -use rustc_middle::ty::query::Providers; +use rustc_middle::ty::query::{ExternProviders, Providers}; use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc_session::cstore::{self, CrateSource}; use rustc_session::utils::NativeLibKind; @@ -168,7 +169,7 @@ crate::target_features::provide(providers); } -pub fn provide_extern(providers: &mut Providers) { +pub fn provide_extern(providers: &mut ExternProviders) { crate::back::symbol_export::provide_extern(providers); }
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index b0a5631..c8f388b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -19,6 +19,7 @@ use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; use rustc_span::source_map::Span; use rustc_span::{sym, Symbol}; +use rustc_symbol_mangling::typeid_for_fnabi; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; use rustc_target::abi::{self, HasDataLayout, WrappingRange}; use rustc_target::spec::abi::Abi; @@ -818,12 +819,43 @@ self.codegen_argument(&mut bx, location, &mut llargs, last_arg); } - let fn_ptr = match (llfn, instance) { - (Some(llfn), _) => llfn, - (None, Some(instance)) => bx.get_fn_addr(instance), + let (is_indirect_call, fn_ptr) = match (llfn, instance) { + (Some(llfn), _) => (true, llfn), + (None, Some(instance)) => (false, bx.get_fn_addr(instance)), _ => span_bug!(span, "no llfn for call"), }; + // For backends that support CFI using type membership (i.e., testing whether a given + // pointer is associated with a type identifier). + if bx.tcx().sess.is_sanitizer_cfi_enabled() && is_indirect_call { + // Emit type metadata and checks. + // FIXME(rcvalle): Add support for generalized identifiers. + // FIXME(rcvalle): Create distinct unnamed MDNodes for internal identifiers. + let typeid = typeid_for_fnabi(bx.tcx(), fn_abi); + let typeid_metadata = bx.typeid_metadata(typeid); + + // Test whether the function pointer is associated with the type identifier. + let cond = bx.type_test(fn_ptr, typeid_metadata); + let mut bx_pass = bx.build_sibling_block("type_test.pass"); + let mut bx_fail = bx.build_sibling_block("type_test.fail"); + bx.cond_br(cond, bx_pass.llbb(), bx_fail.llbb()); + + helper.do_call( + self, + &mut bx_pass, + fn_abi, + fn_ptr, + &llargs, + destination.as_ref().map(|&(_, target)| (ret_dest, target)), + cleanup, + ); + + bx_fail.abort(); + bx_fail.unreachable(); + + return; + } + helper.do_call( self, &mut bx, @@ -845,6 +877,7 @@ options: ast::InlineAsmOptions, line_spans: &[Span], destination: Option<mir::BasicBlock>, + instance: Instance<'_>, ) { let span = terminator.source_info.span; @@ -898,7 +931,7 @@ }) .collect(); - bx.codegen_inline_asm(template, &operands, options, line_spans); + bx.codegen_inline_asm(template, &operands, options, line_spans, instance); if let Some(target) = destination { helper.funclet_br(self, &mut bx, target); @@ -947,17 +980,6 @@ } mir::TerminatorKind::Goto { target } => { - if bb == target { - // This is an unconditional branch back to this same basic block. That means we - // have something like a `loop {}` statement. LLVM versions before 12.0 - // miscompile this because they assume forward progress. For older versions - // try to handle just this specific case which comes up commonly in practice - // (e.g., in embedded code). - // - // NB: the `sideeffect` currently checks for the LLVM version used internally. - bx.sideeffect(); - } - helper.funclet_br(self, &mut bx, target); } @@ -1029,6 +1051,7 @@ options, line_spans, destination, + self.instance, ); } }
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 476ddbd..1ef863e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -4,6 +4,7 @@ use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; +use rustc_symbol_mangling::typeid_for_fnabi; use rustc_target::abi::call::{FnAbi, PassMode}; use std::iter; @@ -244,6 +245,13 @@ for (bb, _) in traversal::reverse_postorder(&mir) { fx.codegen_block(bb); } + + // For backends that support CFI using type membership (i.e., testing whether a given pointer + // is associated with a type identifier). + if cx.tcx().sess.is_sanitizer_cfi_enabled() { + let typeid = typeid_for_fnabi(cx.tcx(), fn_abi); + bx.type_metadata(llfn, typeid); + } } /// Produces, for each argument, a `Value` pointing at the
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index ce6cec6..bea55bb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -343,9 +343,7 @@ .unwrap_or_else(|| bug!("indirect_dest has non-pointer type: {:?}", indirect_dest)) .ty; - let (llptr, llextra) = if let OperandValue::Ref(llptr, Some(llextra), _) = self { - (llptr, llextra) - } else { + let OperandValue::Ref(llptr, Some(llextra), _) = self else { bug!("store_unsized called with a sized value") };
diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index fe7f628..2c96987 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs
@@ -125,7 +125,7 @@ let count = self.codegen_operand(&mut bx, count).immediate(); let pointee_layout = dst_val .layout - .pointee_info_at(&mut bx, rustc_target::abi::Size::ZERO) + .pointee_info_at(&bx, rustc_target::abi::Size::ZERO) .expect("Expected pointer"); let bytes = bx.mul(count, bx.const_usize(pointee_layout.size.bytes()));
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 8d7e961..b4420df 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -19,6 +19,8 @@ ("crypto", Some(sym::arm_target_feature)), ("aes", Some(sym::arm_target_feature)), ("sha2", Some(sym::arm_target_feature)), + ("i8mm", Some(sym::arm_target_feature)), + ("dotprod", Some(sym::arm_target_feature)), ("v5te", Some(sym::arm_target_feature)), ("v6", Some(sym::arm_target_feature)), ("v6k", Some(sym::arm_target_feature)), @@ -35,7 +37,6 @@ ("thumb-mode", Some(sym::arm_target_feature)), ]; -// Commented features are not available in LLVM 10.0, or have since been renamed const AARCH64_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ // FEAT_AdvSimd ("neon", Some(sym::aarch64_target_feature)), @@ -66,13 +67,13 @@ // FEAT_DIT ("dit", Some(sym::aarch64_target_feature)), // FEAT_FLAGM - // ("flagm", Some(sym::aarch64_target_feature)), + ("flagm", Some(sym::aarch64_target_feature)), // FEAT_SSBS ("ssbs", Some(sym::aarch64_target_feature)), // FEAT_SB ("sb", Some(sym::aarch64_target_feature)), // FEAT_PAUTH - // ("pauth", Some(sym::aarch64_target_feature)), + ("pauth", Some(sym::aarch64_target_feature)), // FEAT_DPB ("dpb", Some(sym::aarch64_target_feature)), // FEAT_DPB2 @@ -90,13 +91,13 @@ // FEAT_FRINTTS ("frintts", Some(sym::aarch64_target_feature)), // FEAT_I8MM - // ("i8mm", Some(sym::aarch64_target_feature)), + ("i8mm", Some(sym::aarch64_target_feature)), // FEAT_F32MM - // ("f32mm", Some(sym::aarch64_target_feature)), + ("f32mm", Some(sym::aarch64_target_feature)), // FEAT_F64MM - // ("f64mm", Some(sym::aarch64_target_feature)), + ("f64mm", Some(sym::aarch64_target_feature)), // FEAT_BF16 - // ("bf16", Some(sym::aarch64_target_feature)), + ("bf16", Some(sym::aarch64_target_feature)), // FEAT_RAND ("rand", Some(sym::aarch64_target_feature)), // FEAT_BTI @@ -115,13 +116,23 @@ ("sha3", Some(sym::aarch64_target_feature)), // FEAT_SM3 & FEAT_SM4 ("sm4", Some(sym::aarch64_target_feature)), + // FEAT_PAN + ("pan", Some(sym::aarch64_target_feature)), + // FEAT_LOR + ("lor", Some(sym::aarch64_target_feature)), + // FEAT_VHE + ("vh", Some(sym::aarch64_target_feature)), + // FEAT_PMUv3 + ("pmuv3", Some(sym::aarch64_target_feature)), + // FEAT_SPE + ("spe", Some(sym::aarch64_target_feature)), ("v8.1a", Some(sym::aarch64_target_feature)), ("v8.2a", Some(sym::aarch64_target_feature)), ("v8.3a", Some(sym::aarch64_target_feature)), ("v8.4a", Some(sym::aarch64_target_feature)), ("v8.5a", Some(sym::aarch64_target_feature)), - // ("v8.6a", Some(sym::aarch64_target_feature)), - // ("v8.7a", Some(sym::aarch64_target_feature)), + ("v8.6a", Some(sym::aarch64_target_feature)), + ("v8.7a", Some(sym::aarch64_target_feature)), ]; const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
diff --git a/compiler/rustc_codegen_ssa/src/traits/abi.rs b/compiler/rustc_codegen_ssa/src/traits/abi.rs index dd84958..a00d78d 100644 --- a/compiler/rustc_codegen_ssa/src/traits/abi.rs +++ b/compiler/rustc_codegen_ssa/src/traits/abi.rs
@@ -4,5 +4,5 @@ pub trait AbiBuilderMethods<'tcx>: BackendTypes { fn apply_attrs_callsite(&mut self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, callsite: Self::Value); - fn get_param(&self, index: usize) -> Self::Value; + fn get_param(&mut self, index: usize) -> Self::Value; }
diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 86f2781..31f539e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs
@@ -58,6 +58,7 @@ operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, line_spans: &[Span], + instance: Instance<'_>, ); }
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 8129a14..9c8bc3b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -9,7 +9,7 @@ use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf, TyAndLayout}; -use rustc_middle::ty::query::Providers; +use rustc_middle::ty::query::{ExternProviders, Providers}; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_session::{ config::{self, OutputFilenames, PrintRequest}, @@ -80,7 +80,7 @@ } fn provide(&self, _providers: &mut Providers) {} - fn provide_extern(&self, _providers: &mut Providers) {} + fn provide_extern(&self, _providers: &mut ExternProviders) {} fn codegen_crate<'tcx>( &self, tcx: TyCtxt<'tcx>, @@ -142,4 +142,26 @@ ) -> TargetMachineFactoryFn<Self>; fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str; fn tune_cpu<'b>(&self, sess: &'b Session) -> Option<&'b str>; + + fn spawn_thread<F, T>(_time_trace: bool, f: F) -> std::thread::JoinHandle<T> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + std::thread::spawn(f) + } + + fn spawn_named_thread<F, T>( + _time_trace: bool, + name: String, + f: F, + ) -> std::io::Result<std::thread::JoinHandle<T>> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + std::thread::Builder::new().name(name).spawn(f) + } }
diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index e7da96f..158e658 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs
@@ -158,6 +158,8 @@ fn range_metadata(&mut self, load: Self::Value, range: WrappingRange); fn nonnull_metadata(&mut self, load: Self::Value); + fn type_metadata(&mut self, function: Self::Function, typeid: String); + fn typeid_metadata(&mut self, typeid: String) -> Self::Value; fn store(&mut self, val: Self::Value, ptr: Self::Value, align: Align) -> Self::Value; fn store_with_flags(
diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 777436a..02be6cd 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs
@@ -20,10 +20,8 @@ fn abort(&mut self); fn assume(&mut self, val: Self::Value); fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value; - /// Emits a forced side effect. - /// - /// Currently has any effect only when LLVM versions prior to 12.0 are used as the backend. - fn sideeffect(&mut self); + /// Trait method used to test whether a given pointer is associated with a type identifier. + fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value; /// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in /// Rust defined C-variadic functions. fn va_start(&mut self, val: Self::Value) -> Self::Value;
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 5da1681..8729802 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -25,10 +25,7 @@ impl MachineStopType for ConstEvalErrKind { fn is_hard_err(&self) -> bool { - match self { - Self::Panic { .. } => true, - _ => false, - } + matches!(self, Self::Panic { .. }) } }
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 57af0ff..6d3a89c 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -42,6 +42,7 @@ | DefKind::Static | DefKind::ConstParam | DefKind::AnonConst + | DefKind::InlineConst | DefKind::AssocConst ), "Unexpected DefKind: {:?}",
diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index df4cc29..821b048 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
@@ -1,6 +1,5 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; @@ -44,18 +43,16 @@ } else { false } - } else if let Some(fn_like) = FnLikeNode::from_node(node) { - if fn_like.constness() == hir::Constness::Const { + } else if let Some(fn_kind) = node.fn_kind() { + if fn_kind.constness() == hir::Constness::Const { return true; } // If the function itself is not annotated with `const`, it may still be a `const fn` // if it resides in a const trait impl. is_parent_const_impl_raw(tcx, hir_id) - } else if let hir::Node::Ctor(_) = node { - true } else { - false + matches!(node, hir::Node::Ctor(_)) } }
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 202c9ca..dacd8f7 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -30,34 +30,25 @@ &mut self, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - is_const_fn: bool, ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> { - // The list of functions we handle here must be in sync with - // `is_lang_special_const_fn` in `transform/check_consts/mod.rs`. + // All `#[rustc_do_not_const_check]` functions should be hooked here. let def_id = instance.def_id(); - if is_const_fn { - if Some(def_id) == self.tcx.lang_items().const_eval_select() { - // redirect to const_eval_select_ct - if let Some(const_eval_select) = self.tcx.lang_items().const_eval_select_ct() { - return Ok(Some( - ty::Instance::resolve( - *self.tcx, - ty::ParamEnv::reveal_all(), - const_eval_select, - instance.substs, - ) - .unwrap() - .unwrap(), - )); - } + if Some(def_id) == self.tcx.lang_items().const_eval_select() { + // redirect to const_eval_select_ct + if let Some(const_eval_select) = self.tcx.lang_items().const_eval_select_ct() { + return Ok(Some( + ty::Instance::resolve( + *self.tcx, + ty::ParamEnv::reveal_all(), + const_eval_select, + instance.substs, + ) + .unwrap() + .unwrap(), + )); } - return Ok(None); - } - - if Some(def_id) == self.tcx.lang_items().panic_fn() - || Some(def_id) == self.tcx.lang_items().panic_str() - || Some(def_id) == self.tcx.lang_items().panic_display() + } else if Some(def_id) == self.tcx.lang_items().panic_display() || Some(def_id) == self.tcx.lang_items().begin_panic_fn() { // &str or &&str @@ -72,9 +63,7 @@ let span = self.find_closest_untracked_caller_location(); let (file, line, col) = self.location_triple_for_span(span); return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); - } else if Some(def_id) == self.tcx.lang_items().panic_fmt() - || Some(def_id) == self.tcx.lang_items().begin_panic_fmt() - { + } else if Some(def_id) == self.tcx.lang_items().panic_fmt() { // For panic_fmt, call const_panic_fmt instead. if let Some(const_panic_fmt) = self.tcx.lang_items().const_panic_fmt() { return Ok(Some( @@ -276,31 +265,22 @@ // Only check non-glue functions if let ty::InstanceDef::Item(def) = instance.def { - let mut is_const_fn = true; - // Execution might have wandered off into other crates, so we cannot do a stability- // sensitive check here. But we can at least rule out functions that are not const // at all. if !ecx.tcx.is_const_fn_raw(def.did) { // allow calling functions marked with #[default_method_body_is_const]. if !ecx.tcx.has_attr(def.did, sym::default_method_body_is_const) { - is_const_fn = false; + // We certainly do *not* want to actually call the fn + // though, so be sure we return here. + throw_unsup_format!("calling non-const function `{}`", instance) } } - // Some functions we support even if they are non-const -- but avoid testing - // that for const fn! - // `const_eval_select` is a const fn because it must use const trait bounds. - if let Some(new_instance) = ecx.hook_special_const_fn(instance, args, is_const_fn)? { + if let Some(new_instance) = ecx.hook_special_const_fn(instance, args)? { // We call another const fn instead. return Self::find_mir_or_eval_fn(ecx, new_instance, _abi, args, _ret, _unwind); } - - if !is_const_fn { - // We certainly do *not* want to actually call the fn - // though, so be sure we return here. - throw_unsup_format!("calling non-const function `{}`", instance) - } } // This is a const fn. Call it. Ok(Some(ecx.load_mir(instance.def, None)?))
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 698742f..44da27a 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -413,48 +413,33 @@ sym::simd_insert => { let index = u64::from(self.read_scalar(&args[1])?.to_u32()?); let elem = &args[2]; - let input = &args[0]; - let (len, e_ty) = input.layout.ty.simd_size_and_type(*self.tcx); + let (input, input_len) = self.operand_to_simd(&args[0])?; + let (dest, dest_len) = self.place_to_simd(dest)?; + assert_eq!(input_len, dest_len, "Return vector length must match input length"); assert!( - index < len, - "Index `{}` must be in bounds of vector type `{}`: `[0, {})`", + index < dest_len, + "Index `{}` must be in bounds of vector with length {}`", index, - e_ty, - len - ); - assert_eq!( - input.layout, dest.layout, - "Return type `{}` must match vector type `{}`", - dest.layout.ty, input.layout.ty - ); - assert_eq!( - elem.layout.ty, e_ty, - "Scalar element type `{}` must match vector element type `{}`", - elem.layout.ty, e_ty + dest_len ); - for i in 0..len { - let place = self.place_index(dest, i)?; - let value = if i == index { *elem } else { self.operand_index(input, i)? }; - self.copy_op(&value, &place)?; + for i in 0..dest_len { + let place = self.mplace_index(&dest, i)?; + let value = + if i == index { *elem } else { self.mplace_index(&input, i)?.into() }; + self.copy_op(&value, &place.into())?; } } sym::simd_extract => { let index = u64::from(self.read_scalar(&args[1])?.to_u32()?); - let (len, e_ty) = args[0].layout.ty.simd_size_and_type(*self.tcx); + let (input, input_len) = self.operand_to_simd(&args[0])?; assert!( - index < len, - "index `{}` is out-of-bounds of vector type `{}` with length `{}`", + index < input_len, + "index `{}` must be in bounds of vector with length `{}`", index, - e_ty, - len + input_len ); - assert_eq!( - e_ty, dest.layout.ty, - "Return type `{}` must match vector element type `{}`", - dest.layout.ty, e_ty - ); - self.copy_op(&self.operand_index(&args[0], index)?, dest)?; + self.copy_op(&self.mplace_index(&input, index)?.into(), dest)?; } sym::likely | sym::unlikely | sym::black_box => { // These just return their argument
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs index d4cbba1..b5e97ec8 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
@@ -80,10 +80,17 @@ line: u32, col: u32, ) -> MPlaceTy<'tcx, M::PointerTag> { - let file = - self.allocate_str(&filename.as_str(), MemoryKind::CallerLocation, Mutability::Not); - let line = Scalar::from_u32(line); - let col = Scalar::from_u32(col); + let loc_details = &self.tcx.sess.opts.debugging_opts.location_detail; + let file = if loc_details.file { + self.allocate_str(&filename.as_str(), MemoryKind::CallerLocation, Mutability::Not) + } else { + // FIXME: This creates a new allocation each time. It might be preferable to + // perform this allocation only once, and re-use the `MPlaceTy`. + // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398 + self.allocate_str("<redacted>", MemoryKind::CallerLocation, Mutability::Not) + }; + let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) }; + let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) }; // Allocate memory for `CallerLocation` struct. let loc_ty = self
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs index a7012cd..5b4a5ac 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs
@@ -138,10 +138,8 @@ args: &[GenericArg<'tcx>], ) -> Result<Self::Path, Self::Error> { self = print_prefix(self)?; - let args = args.iter().cloned().filter(|arg| match arg.unpack() { - GenericArgKind::Lifetime(_) => false, - _ => true, - }); + let args = + args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))); if args.clone().next().is_some() { self.generic_delimiters(|cx| cx.comma_sep(args)) } else {
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 323e102..5120782 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -131,6 +131,10 @@ /// Whether to enforce the validity invariant fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + /// Whether to enforce validity (e.g., initialization and not having ptr provenance) + /// of integers and floats. + fn enforce_number_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + /// Whether function calls should be [ABI](Abi)-checked. fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { true @@ -427,6 +431,11 @@ } #[inline(always)] + fn enforce_number_validity(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { + true + } + + #[inline(always)] fn call_extra_fn( _ecx: &mut InterpCx<$mir, $tcx, Self>, fn_val: !,
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index b8b6ff9..4aa3c83 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -1057,20 +1057,19 @@ Some(dest_ptr) => dest_ptr, }; - // first copy the relocations to a temporary buffer, because - // `get_bytes_mut` will clear the relocations, which is correct, - // since we don't want to keep any relocations at the target. - // (`get_bytes_with_uninit_and_ptr` below checks that there are no - // relocations overlapping the edges; those would not be handled correctly). - let relocations = - src_alloc.prepare_relocation_copy(self, src_range, dest_offset, num_copies); - // Prepare a copy of the initialization mask. - let compressed = src_alloc.compress_uninit_range(src_range); - // This checks relocation edges on the src. + // This checks relocation edges on the src, which needs to happen before + // `prepare_relocation_copy`. let src_bytes = src_alloc .get_bytes_with_uninit_and_ptr(&tcx, src_range) .map_err(|e| e.to_interp_error(src_alloc_id))? .as_ptr(); // raw ptr, so we can also get a ptr to the destination allocation + // first copy the relocations to a temporary buffer, because + // `get_bytes_mut` will clear the relocations, which is correct, + // since we don't want to keep any relocations at the target. + let relocations = + src_alloc.prepare_relocation_copy(self, src_range, dest_offset, num_copies); + // Prepare a copy of the initialization mask. + let compressed = src_alloc.compress_uninit_range(src_range); // Destination alloc preparations and access hooks. let (dest_alloc, extra) = self.get_raw_mut(dest_alloc_id)?;
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index b6682b1..de9e94c 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -437,6 +437,18 @@ }) } + /// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements. + /// Also returns the number of elements. + pub fn operand_to_simd( + &self, + base: &OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { + // Basically we just transmute this place into an array following simd_size_and_type. + // This only works in memory, but repr(simd) types should never be immediates anyway. + assert!(base.layout.ty.is_simd()); + self.mplace_to_simd(&base.assert_mem_place()) + } + /// Read from a local. Will not actually access the local if reading from a ZST. /// Will not access memory, instead an indirect `Operand` is returned. ///
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index ac000b1..a90582f 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -130,7 +130,12 @@ let signed = left_layout.abi.is_signed(); let size = u128::from(left_layout.size.bits()); let overflow = r >= size; - let r = r % size; // mask to type size + // The shift offset is implicitly masked to the type size, to make sure this operation + // is always defined. This is the one MIR operator that does *not* directly map to a + // single LLVM operation. See + // <https://github.com/rust-lang/rust/blob/a3b9405ae7bb6ab4e8103b414e75c44598a10fd2/compiler/rustc_codegen_ssa/src/common.rs#L131-L158> + // for the corresponding truncation in our codegen backends. + let r = r % size; let r = u32::try_from(r).unwrap(); // we masked so this will always fit let result = if signed { let l = self.sign_extend(l, left_layout) as i128;
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index d425b84..d7f2853 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -200,7 +200,7 @@ } } else { // Go through the layout. There are lots of types that support a length, - // e.g., SIMD types. + // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!) match self.layout.fields { FieldsShape::Array { count, .. } => Ok(count), _ => bug!("len not supported on sized type {:?}", self.layout.ty), @@ -533,6 +533,22 @@ }) } + /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements. + /// Also returns the number of elements. + pub fn mplace_to_simd( + &self, + base: &MPlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { + // Basically we just transmute this place into an array following simd_size_and_type. + // (Transmuting is okay since this is an in-memory place. We also double-check the size + // stays the same.) + let (len, e_ty) = base.layout.ty.simd_size_and_type(*self.tcx); + let array = self.tcx.mk_array(e_ty, len); + let layout = self.layout_of(array)?; + assert_eq!(layout.size, base.layout.size); + Ok((MPlaceTy { layout, ..*base }, len)) + } + /// Gets the place of a field inside the place, and also the field's type. /// Just a convenience function, but used quite a bit. /// This is the only projection that might have a side-effect: We cannot project @@ -594,6 +610,16 @@ }) } + /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements. + /// Also returns the number of elements. + pub fn place_to_simd( + &mut self, + base: &PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { + let mplace = self.force_allocation(base)?; + self.mplace_to_simd(&mplace) + } + /// Computes a place. You should only use this if you intend to write into this /// place; for reading, a more efficient alternative is `eval_place_for_read`. pub fn eval_place(
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 8d3544d..0020857 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -345,10 +345,8 @@ // Figure out how to pass which arguments. // The Rust ABI is special: ZST get skipped. - let rust_abi = match caller_abi { - Abi::Rust | Abi::RustCall => true, - _ => false, - }; + let rust_abi = matches!(caller_abi, Abi::Rust | Abi::RustCall); + // We have two iterators: Where the arguments come from, // and where they go to.
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index fc69770..6be3e19 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -520,7 +520,7 @@ let value = self.read_scalar(value)?; // NOTE: Keep this in sync with the array optimization for int/float // types below! - if self.ctfe_mode.is_some() { + if M::enforce_number_validity(self.ecx) { // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous let is_bits = value.check_init().map_or(false, |v| v.try_to_int().is_ok()); if !is_bits { @@ -528,9 +528,6 @@ { "{}", value } expected { "initialized plain (non-pointer) bytes" } ) } - } else { - // At run-time, for now, we accept *anything* for these types, including - // uninit. We should fix that, but let's start low. } Ok(true) } @@ -855,9 +852,10 @@ } }; + let allow_uninit_and_ptr = !M::enforce_number_validity(self.ecx); match alloc.check_bytes( alloc_range(Size::ZERO, size), - /*allow_uninit_and_ptr*/ self.ctfe_mode.is_none(), + allow_uninit_and_ptr, ) { // In the happy case, we needn't check anything else. Ok(()) => {}
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 7ce40b3..f308e76 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs
@@ -13,6 +13,7 @@ #![feature(exact_size_is_empty)] #![feature(in_band_lifetimes)] #![feature(iter_zip)] +#![feature(let_else)] #![feature(map_try_insert)] #![feature(min_specialization)] #![feature(slice_ptr_get)]
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 d704c43..4e3a8b6 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -12,7 +12,6 @@ use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt}; use rustc_middle::ty::{Binder, TraitPredicate, TraitRef}; -use rustc_mir_dataflow::impls::MaybeMutBorrowedLocals; use rustc_mir_dataflow::{self, Analysis}; use rustc_span::{sym, Span, Symbol}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; @@ -22,54 +21,22 @@ use std::ops::Deref; use super::ops::{self, NonConstOp, Status}; -use super::qualifs::{self, CustomEq, HasMutInterior, NeedsNonConstDrop}; +use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; -use super::{is_lang_panic_fn, is_lang_special_const_fn, ConstCx, Qualif}; +use super::{ConstCx, Qualif}; use crate::const_eval::is_unstable_const_fn; -// We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated -// through a pointer prior to the given point. This is okay even though `MaybeMutBorrowedLocals` -// kills locals upon `StorageDead` because a local will never be used after a `StorageDead`. -type IndirectlyMutableResults<'mir, 'tcx> = - rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, MaybeMutBorrowedLocals<'mir, 'tcx>>; - type QualifResults<'mir, 'tcx, Q> = rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>; #[derive(Default)] pub struct Qualifs<'mir, 'tcx> { has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>, - needs_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>, - indirectly_mutable: Option<IndirectlyMutableResults<'mir, 'tcx>>, + needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>, + needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>, } impl Qualifs<'mir, 'tcx> { - pub fn indirectly_mutable( - &mut self, - ccx: &'mir ConstCx<'mir, 'tcx>, - local: Local, - location: Location, - ) -> bool { - let indirectly_mutable = self.indirectly_mutable.get_or_insert_with(|| { - let ConstCx { tcx, body, param_env, .. } = *ccx; - - // We can use `unsound_ignore_borrow_on_drop` here because custom drop impls are not - // allowed in a const. - // - // FIXME(ecstaticmorse): Someday we want to allow custom drop impls. How do we do this - // without breaking stable code? - MaybeMutBorrowedLocals::mut_borrows_only(tcx, &body, param_env) - .unsound_ignore_borrow_on_drop() - .into_engine(tcx, &body) - .pass_name("const_qualification") - .iterate_to_fixpoint() - .into_results_cursor(&body) - }); - - indirectly_mutable.seek_before_primary_effect(location); - indirectly_mutable.get().contains(local) - } - /// Returns `true` if `local` is `NeedsDrop` at the given `Location`. /// /// Only updates the cursor if absolutely necessary @@ -80,21 +47,48 @@ location: Location, ) -> bool { let ty = ccx.body.local_decls[local].ty; - if !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) { + if !NeedsDrop::in_any_value_of_ty(ccx, ty) { return false; } let needs_drop = self.needs_drop.get_or_insert_with(|| { let ConstCx { tcx, body, .. } = *ccx; + FlowSensitiveAnalysis::new(NeedsDrop, ccx) + .into_engine(tcx, &body) + .iterate_to_fixpoint() + .into_results_cursor(&body) + }); + + needs_drop.seek_before_primary_effect(location); + needs_drop.get().contains(local) + } + + /// Returns `true` if `local` is `NeedsNonConstDrop` at the given `Location`. + /// + /// Only updates the cursor if absolutely necessary + pub fn needs_non_const_drop( + &mut self, + ccx: &'mir ConstCx<'mir, 'tcx>, + local: Local, + location: Location, + ) -> bool { + let ty = ccx.body.local_decls[local].ty; + if !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) { + return false; + } + + let needs_non_const_drop = self.needs_non_const_drop.get_or_insert_with(|| { + let ConstCx { tcx, body, .. } = *ccx; + FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx) .into_engine(tcx, &body) .iterate_to_fixpoint() .into_results_cursor(&body) }); - needs_drop.seek_before_primary_effect(location); - needs_drop.get().contains(local) || self.indirectly_mutable(ccx, local, location) + needs_non_const_drop.seek_before_primary_effect(location); + needs_non_const_drop.get().contains(local) } /// Returns `true` if `local` is `HasMutInterior` at the given `Location`. @@ -121,7 +115,7 @@ }); has_mut_interior.seek_before_primary_effect(location); - has_mut_interior.get().contains(local) || self.indirectly_mutable(ccx, local, location) + has_mut_interior.get().contains(local) } fn in_return_place( @@ -137,10 +131,7 @@ .body .basic_blocks() .iter_enumerated() - .find(|(_, block)| match block.terminator().kind { - TerminatorKind::Return => true, - _ => false, - }) + .find(|(_, block)| matches!(block.terminator().kind, TerminatorKind::Return)) .map(|(bb, _)| bb); let return_block = match return_block { @@ -167,12 +158,13 @@ .into_results_cursor(&ccx.body); cursor.seek_after_primary_effect(return_loc); - cursor.contains(RETURN_PLACE) + cursor.get().contains(RETURN_PLACE) } }; ConstQualifs { needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc), + needs_non_const_drop: self.needs_non_const_drop(ccx, RETURN_PLACE, return_loc), has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc), custom_eq, error_occured, @@ -730,7 +722,7 @@ match elem { ProjectionElem::Deref => { let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty; - if let ty::RawPtr(_) = base_ty.kind() { + if base_ty.is_unsafe_ptr() { if proj_base.is_empty() { let decl = &self.body.local_decls[place_local]; if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info { @@ -739,7 +731,13 @@ return; } } - self.check_op(ops::RawPtrDeref); + + // `*const T` is stable, `*mut T` is not + if !base_ty.is_mutable_ptr() { + return; + } + + self.check_op(ops::RawMutPtrDeref); } if context.is_mutating_use() { @@ -825,6 +823,7 @@ Binder::dummy(TraitPredicate { trait_ref, constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, }), ); @@ -888,31 +887,27 @@ } // At this point, we are calling a function, `callee`, whose `DefId` is known... - if is_lang_special_const_fn(tcx, callee) { - // `begin_panic` and `panic_display` are generic functions that accept - // types other than str. Check to enforce that only str can be used in - // const-eval. - // const-eval of the `begin_panic` fn assumes the argument is `&str` - if Some(callee) == tcx.lang_items().begin_panic_fn() { - match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { - ty::Ref(_, ty, _) if ty.is_str() => (), - _ => self.check_op(ops::PanicNonStr), - } + // `begin_panic` and `panic_display` are generic functions that accept + // types other than str. Check to enforce that only str can be used in + // const-eval. + + // const-eval of the `begin_panic` fn assumes the argument is `&str` + if Some(callee) == tcx.lang_items().begin_panic_fn() { + match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { + ty::Ref(_, ty, _) if ty.is_str() => return, + _ => self.check_op(ops::PanicNonStr), } + } - // const-eval of the `panic_display` fn assumes the argument is `&&str` - if Some(callee) == tcx.lang_items().panic_display() { - match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { - ty::Ref(_, ty, _) if matches!(ty.kind(), ty::Ref(_, ty, _) if ty.is_str()) => - {} - _ => self.check_op(ops::PanicNonStr), + // const-eval of the `panic_display` fn assumes the argument is `&&str` + if Some(callee) == tcx.lang_items().panic_display() { + match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { + ty::Ref(_, ty, _) if matches!(ty.kind(), ty::Ref(_, ty, _) if ty.is_str()) => + { + return; } - } - - if is_lang_panic_fn(tcx, callee) { - // run stability check on non-panic special const fns. - return; + _ => self.check_op(ops::PanicNonStr), } } @@ -999,7 +994,7 @@ } // Forbid all `Drop` terminators unless the place being dropped is a local with no - // projections that cannot be `NeedsDrop`. + // projections that cannot be `NeedsNonConstDrop`. TerminatorKind::Drop { place: dropped_place, .. } | TerminatorKind::DropAndReplace { place: dropped_place, .. } => { // If we are checking live drops after drop-elaboration, don't emit duplicate @@ -1009,25 +1004,26 @@ } let mut err_span = self.span; + let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty; - let ty_needs_non_const_drop = qualifs::NeedsNonConstDrop::in_any_value_of_ty( - self.ccx, - dropped_place.ty(self.body, self.tcx).ty, - ); + let ty_needs_non_const_drop = + qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place); + + debug!(?ty_of_dropped_place, ?ty_needs_non_const_drop); if !ty_needs_non_const_drop { return; } - let needs_drop = if let Some(local) = dropped_place.as_local() { + let needs_non_const_drop = if let Some(local) = dropped_place.as_local() { // Use the span where the local was declared as the span of the drop error. err_span = self.body.local_decls[local].source_info.span; - self.qualifs.needs_drop(self.ccx, local, location) + self.qualifs.needs_non_const_drop(self.ccx, local, location) } else { true }; - if needs_drop { + if needs_non_const_drop { self.check_op_spanned( ops::LiveDrop { dropped_at: Some(terminator.source_info.span) }, err_span, @@ -1065,8 +1061,9 @@ let mut fulfillment_cx = traits::FulfillmentContext::new(); let sync_def_id = tcx.require_lang_item(LangItem::Sync, Some(body.span)); fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause); - if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) { - infcx.report_fulfillment_errors(&err, None, false); + let errors = fulfillment_cx.select_all_or_error(&infcx); + if !errors.is_empty() { + infcx.report_fulfillment_errors(&errors, None, false); } }); }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs index 0a85228..dc44409 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs
@@ -72,25 +72,6 @@ } } -/// Returns `true` if this `DefId` points to one of the official `panic` lang items. -pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { - Some(def_id) == tcx.lang_items().panic_fn() - || Some(def_id) == tcx.lang_items().panic_str() - || Some(def_id) == tcx.lang_items().panic_display() - || Some(def_id) == tcx.lang_items().begin_panic_fn() - || Some(def_id) == tcx.lang_items().panic_fmt() - || Some(def_id) == tcx.lang_items().begin_panic_fmt() -} - -/// Returns `true` if this `DefId` points to one of the lang items that will be handled differently -/// in const_eval. -pub fn is_lang_special_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { - // We can allow calls to these functions because `hook_special_const_fn` in - // `const_eval/machine.rs` ensures the calls are handled specially. - // Keep in sync with what that function handles! - is_lang_panic_fn(tcx, def_id) || Some(def_id) == tcx.lang_items().const_eval_select() -} - pub fn rustc_allow_const_fn_unstable( tcx: TyCtxt<'tcx>, def_id: DefId,
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 230d023..6391c88 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -400,18 +400,18 @@ } #[derive(Debug)] -pub struct RawPtrDeref; -impl NonConstOp for RawPtrDeref { +pub struct RawMutPtrDeref; +impl NonConstOp for RawMutPtrDeref { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { - Status::Unstable(sym::const_raw_ptr_deref) + Status::Unstable(sym::const_mut_refs) } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, - sym::const_raw_ptr_deref, + sym::const_mut_refs, span, - &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),), + &format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),), ) } }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs index 1a8c8b1..7a2be3c 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs
@@ -97,7 +97,7 @@ // `src/test/ui/consts/control-flow/drop-pass.rs`; e.g., when an `Option<Vec<T>>` is // initialized with `None` and never changed, it still emits drop glue. // Hence we additionally check the qualifs here to allow more code to pass. - if self.qualifs.needs_drop(self.ccx, dropped_place.local, location) { + if self.qualifs.needs_non_const_drop(self.ccx, dropped_place.local, location) { // Use the span where the dropped local was declared for the error. let span = self.body.local_decls[dropped_place.local].source_info.span; self.check_live_drop(span);
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 5eb7d7a..abc5a3c 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -21,7 +21,8 @@ ) -> ConstQualifs { ConstQualifs { has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty), - needs_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty), + needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty), + needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty), custom_eq: CustomEq::in_any_value_of_ty(cx, ty), error_occured, } @@ -45,6 +46,9 @@ /// Whether this `Qualif` is cleared when a local is moved from. const IS_CLEARED_ON_MOVE: bool = false; + /// Whether this `Qualif` might be evaluated after the promotion and can encounter a promoted. + const ALLOW_PROMOTED: bool = false; + /// Extracts the field of `ConstQualifs` that corresponds to this `Qualif`. fn in_qualifs(qualifs: &ConstQualifs) -> bool; @@ -98,17 +102,40 @@ } /// Constant containing an ADT that implements `Drop`. -/// This must be ruled out (a) because we cannot run `Drop` during compile-time -/// as that might not be a `const fn`, and (b) because implicit promotion would -/// remove side-effects that occur as part of dropping that value. +/// This must be ruled out because implicit promotion would remove side-effects +/// that occur as part of dropping that value. N.B., the implicit promotion has +/// to reject const Drop implementations because even if side-effects are ruled +/// out through other means, the execution of the drop could diverge. +pub struct NeedsDrop; + +impl Qualif for NeedsDrop { + const ANALYSIS_NAME: &'static str = "flow_needs_drop"; + const IS_CLEARED_ON_MOVE: bool = true; + + fn in_qualifs(qualifs: &ConstQualifs) -> bool { + qualifs.needs_drop + } + + fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.needs_drop(cx.tcx, cx.param_env) + } + + fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool { + adt.has_dtor(cx.tcx) + } +} + +/// Constant containing an ADT that implements non-const `Drop`. +/// This must be ruled out because we cannot run `Drop` during compile-time. pub struct NeedsNonConstDrop; impl Qualif for NeedsNonConstDrop { const ANALYSIS_NAME: &'static str = "flow_needs_nonconst_drop"; const IS_CLEARED_ON_MOVE: bool = true; + const ALLOW_PROMOTED: bool = true; fn in_qualifs(qualifs: &ConstQualifs) -> bool { - qualifs.needs_drop + qualifs.needs_non_const_drop } fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, mut ty: Ty<'tcx>) -> bool { @@ -122,9 +149,7 @@ Ok([..]) => {} } - let drop_trait = if let Some(did) = cx.tcx.lang_items().drop_trait() { - did - } else { + let Some(drop_trait) = cx.tcx.lang_items().drop_trait() else { // there is no way to define a type that needs non-const drop // without having the lang item present. return false; @@ -137,6 +162,7 @@ ty::Binder::dummy(ty::TraitPredicate { trait_ref, constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, }), ); @@ -144,11 +170,12 @@ let mut selcx = SelectionContext::with_constness(&infcx, hir::Constness::Const); selcx.select(&obligation) }); - match implsrc { - Ok(Some(ImplSource::ConstDrop(_))) - | Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => false, - _ => true, - } + !matches!( + implsrc, + Ok(Some( + ImplSource::ConstDrop(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) + )) + ) } fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool { @@ -232,6 +259,9 @@ if Q::in_adt_inherently(cx, def, substs) { return true; } + if def.is_union() && Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx)) { + return true; + } } // Otherwise, proceed structurally... @@ -289,9 +319,12 @@ // Check the qualifs of the value of `const` items. if let Some(ct) = constant.literal.const_for_ty() { if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted }) = ct.val { - assert!(promoted.is_none()); + // Use qualifs of the type for the promoted. Promoteds in MIR body should be possible + // only for `NeedsNonConstDrop` with precise drop checking. This is the only const + // check performed after the promotion. Verify that with an assertion. + assert!(promoted.is_none() || Q::ALLOW_PROMOTED); // Don't peek inside trait associated constants. - if cx.tcx.trait_of_item(def.did).is_none() { + if promoted.is_none() && cx.tcx.trait_of_item(def.did).is_none() { let qualifs = if let Some((did, param_did)) = def.as_const_arg() { cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did)) } else {
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs index 8e1b69a..b70b387 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
@@ -4,8 +4,12 @@ use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{self, BasicBlock, Local, Location}; +use rustc_middle::mir::{self, BasicBlock, Local, Location, Statement, StatementKind}; +use rustc_mir_dataflow::fmt::DebugWithContext; +use rustc_mir_dataflow::JoinSemiLattice; +use rustc_span::DUMMY_SP; +use std::fmt; use std::marker::PhantomData; use super::{qualifs, ConstCx, Qualif}; @@ -13,13 +17,13 @@ /// A `Visitor` that propagates qualifs between locals. This defines the transfer function of /// `FlowSensitiveAnalysis`. /// -/// This transfer does nothing when encountering an indirect assignment. Consumers should rely on -/// the `MaybeMutBorrowedLocals` dataflow pass to see if a `Local` may have become qualified via -/// an indirect assignment or function call. +/// To account for indirect assignments, data flow conservatively assumes that local becomes +/// qualified immediately after it is borrowed or its address escapes. The borrow must allow for +/// mutation, which includes shared borrows of places with interior mutability. The type of +/// borrowed place must contain the qualif. struct TransferFunction<'a, 'mir, 'tcx, Q> { ccx: &'a ConstCx<'mir, 'tcx>, - qualifs_per_local: &'a mut BitSet<Local>, - + state: &'a mut State, _qualif: PhantomData<Q>, } @@ -27,27 +31,38 @@ where Q: Qualif, { - fn new(ccx: &'a ConstCx<'mir, 'tcx>, qualifs_per_local: &'a mut BitSet<Local>) -> Self { - TransferFunction { ccx, qualifs_per_local, _qualif: PhantomData } + fn new(ccx: &'a ConstCx<'mir, 'tcx>, state: &'a mut State) -> Self { + TransferFunction { ccx, state, _qualif: PhantomData } } fn initialize_state(&mut self) { - self.qualifs_per_local.clear(); + self.state.qualif.clear(); + self.state.borrow.clear(); for arg in self.ccx.body.args_iter() { let arg_ty = self.ccx.body.local_decls[arg].ty; if Q::in_any_value_of_ty(self.ccx, arg_ty) { - self.qualifs_per_local.insert(arg); + self.state.qualif.insert(arg); } } } - fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, value: bool) { + fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, mut value: bool) { debug_assert!(!place.is_indirect()); + if !value { + for (base, _elem) in place.iter_projections() { + let base_ty = base.ty(self.ccx.body, self.ccx.tcx); + if base_ty.ty.is_union() && Q::in_any_value_of_ty(self.ccx, base_ty.ty) { + value = true; + break; + } + } + } + match (value, place.as_ref()) { (true, mir::PlaceRef { local, .. }) => { - self.qualifs_per_local.insert(local); + self.state.qualif.insert(local); } // For now, we do not clear the qualif if a local is overwritten in full by @@ -55,7 +70,7 @@ // with aggregates where we overwrite all fields with assignments, which would not // get this feature. (false, mir::PlaceRef { local: _, projection: &[] }) => { - // self.qualifs_per_local.remove(*local); + // self.state.qualif.remove(*local); } _ => {} @@ -78,6 +93,37 @@ self.assign_qualif_direct(&return_place, qualif); } } + + fn address_of_allows_mutation(&self, _mt: mir::Mutability, _place: mir::Place<'tcx>) -> bool { + // Exact set of permissions granted by AddressOf is undecided. Conservatively assume that + // it might allow mutation until resolution of #56604. + true + } + + fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool { + match kind { + mir::BorrowKind::Mut { .. } => true, + mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => { + self.shared_borrow_allows_mutation(place) + } + } + } + + /// `&` only allow mutation if the borrowed place is `!Freeze`. + /// + /// This assumes that it is UB to take the address of a struct field whose type is + /// `Freeze`, then use pointer arithmetic to derive a pointer to a *different* field of + /// that same struct whose type is `!Freeze`. If we decide that this is not UB, we will + /// have to check the type of the borrowed **local** instead of the borrowed **place** + /// below. See [rust-lang/unsafe-code-guidelines#134]. + /// + /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134 + fn shared_borrow_allows_mutation(&self, place: mir::Place<'tcx>) -> bool { + !place + .ty(self.ccx.body, self.ccx.tcx) + .ty + .is_freeze(self.ccx.tcx.at(DUMMY_SP), self.ccx.param_env) + } } impl<Q> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx, Q> @@ -95,7 +141,12 @@ // it no longer needs to be dropped. if let mir::Operand::Move(place) = operand { if let Some(local) = place.as_local() { - self.qualifs_per_local.remove(local); + // For backward compatibility with the MaybeMutBorrowedLocals used in an earlier + // implementation we retain qualif if a local had been borrowed before. This might + // not be strictly necessary since the local is no longer initialized. + if !self.state.borrow.contains(local) { + self.state.qualif.remove(local); + } } } } @@ -106,11 +157,8 @@ rvalue: &mir::Rvalue<'tcx>, location: Location, ) { - let qualif = qualifs::in_rvalue::<Q, _>( - self.ccx, - &mut |l| self.qualifs_per_local.contains(l), - rvalue, - ); + let qualif = + qualifs::in_rvalue::<Q, _>(self.ccx, &mut |l| self.state.qualif.contains(l), rvalue); if !place.is_indirect() { self.assign_qualif_direct(place, qualif); } @@ -120,6 +168,58 @@ self.super_assign(place, rvalue, location); } + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + + match rvalue { + mir::Rvalue::AddressOf(mt, borrowed_place) => { + if !borrowed_place.is_indirect() + && self.address_of_allows_mutation(*mt, *borrowed_place) + { + let place_ty = borrowed_place.ty(self.ccx.body, self.ccx.tcx).ty; + if Q::in_any_value_of_ty(self.ccx, place_ty) { + self.state.qualif.insert(borrowed_place.local); + self.state.borrow.insert(borrowed_place.local); + } + } + } + + mir::Rvalue::Ref(_, kind, borrowed_place) => { + if !borrowed_place.is_indirect() && self.ref_allows_mutation(*kind, *borrowed_place) + { + let place_ty = borrowed_place.ty(self.ccx.body, self.ccx.tcx).ty; + if Q::in_any_value_of_ty(self.ccx, place_ty) { + self.state.qualif.insert(borrowed_place.local); + self.state.borrow.insert(borrowed_place.local); + } + } + } + + mir::Rvalue::Cast(..) + | mir::Rvalue::ShallowInitBox(..) + | mir::Rvalue::Use(..) + | mir::Rvalue::ThreadLocalRef(..) + | mir::Rvalue::Repeat(..) + | mir::Rvalue::Len(..) + | mir::Rvalue::BinaryOp(..) + | mir::Rvalue::CheckedBinaryOp(..) + | mir::Rvalue::NullaryOp(..) + | mir::Rvalue::UnaryOp(..) + | mir::Rvalue::Discriminant(..) + | mir::Rvalue::Aggregate(..) => {} + } + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + match statement.kind { + StatementKind::StorageDead(local) => { + self.state.qualif.remove(local); + self.state.borrow.remove(local); + } + _ => self.super_statement(statement, location), + } + } + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { // The effect of assignment to the return place in `TerminatorKind::Call` is not applied // here; that occurs in `apply_call_return_effect`. @@ -127,7 +227,7 @@ if let mir::TerminatorKind::DropAndReplace { value, place, .. } = &terminator.kind { let qualif = qualifs::in_operand::<Q, _>( self.ccx, - &mut |l| self.qualifs_per_local.contains(l), + &mut |l| self.state.qualif.contains(l), value, ); @@ -136,6 +236,9 @@ } } + // We ignore borrow on drop because custom drop impls are not allowed in consts. + // FIXME: Reconsider if accounting for borrows in drops is necessary for const drop. + // We need to assign qualifs to the dropped location before visiting the operand that // replaces it since qualifs can be cleared on move. self.super_terminator(terminator, location); @@ -156,24 +259,89 @@ FlowSensitiveAnalysis { ccx, _qualif: PhantomData } } - fn transfer_function( - &self, - state: &'a mut BitSet<Local>, - ) -> TransferFunction<'a, 'mir, 'tcx, Q> { + fn transfer_function(&self, state: &'a mut State) -> TransferFunction<'a, 'mir, 'tcx, Q> { TransferFunction::<Q>::new(self.ccx, state) } } +#[derive(Debug, PartialEq, Eq)] +pub(super) struct State { + /// Describes whether a local contains qualif. + pub qualif: BitSet<Local>, + /// Describes whether a local's address escaped and it might become qualified as a result an + /// indirect mutation. + pub borrow: BitSet<Local>, +} + +impl Clone for State { + fn clone(&self) -> Self { + State { qualif: self.qualif.clone(), borrow: self.borrow.clone() } + } + + // Data flow engine when possible uses `clone_from` for domain values. + // Providing an implementation will avoid some intermediate memory allocations. + fn clone_from(&mut self, other: &Self) { + self.qualif.clone_from(&other.qualif); + self.borrow.clone_from(&other.borrow); + } +} + +impl State { + #[inline] + pub(super) fn contains(&self, local: Local) -> bool { + self.qualif.contains(local) + } +} + +impl<C> DebugWithContext<C> for State { + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("qualif: ")?; + self.qualif.fmt_with(ctxt, f)?; + f.write_str(" borrow: ")?; + self.borrow.fmt_with(ctxt, f)?; + Ok(()) + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self == old { + return Ok(()); + } + + if self.qualif != old.qualif { + f.write_str("qualif: ")?; + self.qualif.fmt_diff_with(&old.qualif, ctxt, f)?; + f.write_str("\n")?; + } + + if self.borrow != old.borrow { + f.write_str("borrow: ")?; + self.qualif.fmt_diff_with(&old.borrow, ctxt, f)?; + f.write_str("\n")?; + } + + Ok(()) + } +} + +impl JoinSemiLattice for State { + fn join(&mut self, other: &Self) -> bool { + self.qualif.join(&other.qualif) || self.borrow.join(&other.borrow) + } +} + impl<Q> rustc_mir_dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> where Q: Qualif, { - type Domain = BitSet<Local>; + type Domain = State; const NAME: &'static str = Q::ANALYSIS_NAME; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - BitSet::new_empty(body.local_decls.len()) + State { + qualif: BitSet::new_empty(body.local_decls.len()), + borrow: BitSet::new_empty(body.local_decls.len()), + } } fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) {
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 7cfe3d7..a92b20f 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -26,7 +26,7 @@ use std::cell::Cell; use std::{cmp, iter, mem}; -use crate::transform::check_consts::{is_lang_special_const_fn, qualifs, ConstCx}; +use crate::transform::check_consts::{qualifs, ConstCx}; use crate::transform::MirPass; /// A `MirPass` for promotion. @@ -93,17 +93,8 @@ /// returned value in a promoted MIR, unless it's a subset /// of a larger candidate. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Candidate { - /// Borrow of a constant temporary, candidate for lifetime extension. - Ref(Location), -} - -impl Candidate { - fn source_info(&self, body: &Body<'_>) -> SourceInfo { - match self { - Candidate::Ref(location) => *body.source_info(*location), - } - } +pub struct Candidate { + location: Location, } struct Collector<'a, 'tcx> { @@ -167,7 +158,7 @@ match *rvalue { Rvalue::Ref(..) => { - self.candidates.push(Candidate::Ref(location)); + self.candidates.push(Candidate { location }); } _ => {} } @@ -209,36 +200,33 @@ impl<'tcx> Validator<'_, 'tcx> { fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> { - match candidate { - Candidate::Ref(loc) => { - let statement = &self.body[loc.block].statements[loc.statement_index]; - match &statement.kind { - StatementKind::Assign(box (_, Rvalue::Ref(_, kind, place))) => { - // We can only promote interior borrows of promotable temps (non-temps - // don't get promoted anyway). - self.validate_local(place.local)?; + let loc = candidate.location; + let statement = &self.body[loc.block].statements[loc.statement_index]; + match &statement.kind { + StatementKind::Assign(box (_, Rvalue::Ref(_, kind, place))) => { + // We can only promote interior borrows of promotable temps (non-temps + // don't get promoted anyway). + self.validate_local(place.local)?; - // The reference operation itself must be promotable. - // (Needs to come after `validate_local` to avoid ICEs.) - self.validate_ref(*kind, place)?; + // The reference operation itself must be promotable. + // (Needs to come after `validate_local` to avoid ICEs.) + self.validate_ref(*kind, place)?; - // We do not check all the projections (they do not get promoted anyway), - // but we do stay away from promoting anything involving a dereference. - if place.projection.contains(&ProjectionElem::Deref) { - return Err(Unpromotable); - } - - // We cannot promote things that need dropping, since the promoted value - // would not get dropped. - if self.qualif_local::<qualifs::NeedsNonConstDrop>(place.local) { - return Err(Unpromotable); - } - - Ok(()) - } - _ => bug!(), + // We do not check all the projections (they do not get promoted anyway), + // but we do stay away from promoting anything involving a dereference. + if place.projection.contains(&ProjectionElem::Deref) { + return Err(Unpromotable); } + + // We cannot promote things that need dropping, since the promoted value + // would not get dropped. + if self.qualif_local::<qualifs::NeedsDrop>(place.local) { + return Err(Unpromotable); + } + + Ok(()) } + _ => bug!(), } } @@ -656,9 +644,7 @@ } let is_const_fn = match *fn_ty.kind() { - ty::FnDef(def_id, _) => { - self.tcx.is_const_fn_raw(def_id) || is_lang_special_const_fn(self.tcx, def_id) - } + ty::FnDef(def_id, _) => self.tcx.is_const_fn_raw(def_id), _ => false, }; if !is_const_fn { @@ -837,11 +823,7 @@ new_temp } - fn promote_candidate( - mut self, - candidate: Candidate, - next_promoted_id: usize, - ) -> Option<Body<'tcx>> { + fn promote_candidate(mut self, candidate: Candidate, next_promoted_id: usize) -> Body<'tcx> { let def = self.source.source.with_opt_param(); let mut rvalue = { let promoted = &mut self.promoted; @@ -877,58 +859,55 @@ })) }; let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut(); - match candidate { - Candidate::Ref(loc) => { - let statement = &mut blocks[loc.block].statements[loc.statement_index]; - match statement.kind { - StatementKind::Assign(box ( - _, - 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 span = statement.source_info.span; + let loc = candidate.location; + let statement = &mut blocks[loc.block].statements[loc.statement_index]; + match statement.kind { + StatementKind::Assign(box ( + _, + 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 span = statement.source_info.span; - let ref_ty = tcx.mk_ref( - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() }, - ); + let ref_ty = tcx.mk_ref( + tcx.lifetimes.re_erased, + ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() }, + ); - *region = tcx.lifetimes.re_erased; + *region = tcx.lifetimes.re_erased; - let mut projection = vec![PlaceElem::Deref]; - projection.extend(place.projection); - place.projection = tcx.intern_place_elems(&projection); + let mut projection = vec![PlaceElem::Deref]; + projection.extend(place.projection); + place.projection = tcx.intern_place_elems(&projection); - // Create a temp to hold the promoted reference. - // This is because `*r` requires `r` to be a local, - // otherwise we would use the `promoted` directly. - let mut promoted_ref = LocalDecl::new(ref_ty, span); - promoted_ref.source_info = statement.source_info; - let promoted_ref = local_decls.push(promoted_ref); - assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref); + // Create a temp to hold the promoted reference. + // This is because `*r` requires `r` to be a local, + // otherwise we would use the `promoted` directly. + let mut promoted_ref = LocalDecl::new(ref_ty, span); + promoted_ref.source_info = statement.source_info; + let promoted_ref = local_decls.push(promoted_ref); + assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref); - let promoted_ref_statement = Statement { - source_info: statement.source_info, - kind: StatementKind::Assign(Box::new(( - Place::from(promoted_ref), - Rvalue::Use(promoted_operand(ref_ty, span)), - ))), - }; - self.extra_statements.push((loc, promoted_ref_statement)); + let promoted_ref_statement = Statement { + source_info: statement.source_info, + kind: StatementKind::Assign(Box::new(( + Place::from(promoted_ref), + Rvalue::Use(promoted_operand(ref_ty, span)), + ))), + }; + self.extra_statements.push((loc, promoted_ref_statement)); - Rvalue::Ref( - tcx.lifetimes.re_erased, - borrow_kind, - Place { - local: mem::replace(&mut place.local, promoted_ref), - projection: List::empty(), - }, - ) - } - _ => bug!(), - } + Rvalue::Ref( + tcx.lifetimes.re_erased, + borrow_kind, + Place { + local: mem::replace(&mut place.local, promoted_ref), + projection: List::empty(), + }, + ) } + _ => bug!(), } }; @@ -940,7 +919,7 @@ let span = self.promoted.span; self.assign(RETURN_PLACE, rvalue, span); - Some(self.promoted) + self.promoted } } @@ -970,17 +949,13 @@ let mut extra_statements = vec![]; for candidate in candidates.into_iter().rev() { - match candidate { - Candidate::Ref(Location { block, statement_index }) => { - if let StatementKind::Assign(box (place, _)) = - &body[block].statements[statement_index].kind - { - if let Some(local) = place.as_local() { - if temps[local] == TempState::PromotedOut { - // Already promoted. - continue; - } - } + let Location { block, statement_index } = candidate.location; + if let StatementKind::Assign(box (place, _)) = &body[block].statements[statement_index].kind + { + if let Some(local) = place.as_local() { + if temps[local] == TempState::PromotedOut { + // Already promoted. + continue; } } } @@ -988,7 +963,7 @@ // Declare return place local so that `mir::Body::new` doesn't complain. let initial_locals = iter::once(LocalDecl::new(tcx.types.never, body.span)).collect(); - let mut scope = body.source_scopes[candidate.source_info(body).scope].clone(); + let mut scope = body.source_scopes[body.source_info(candidate.location).scope].clone(); scope.parent_scope = None; let promoted = Body::new( @@ -1013,11 +988,9 @@ keep_original: false, }; - //FIXME(oli-obk): having a `maybe_push()` method on `IndexVec` might be nice - if let Some(mut promoted) = promoter.promote_candidate(candidate, promotions.len()) { - promoted.source.promoted = Some(promotions.next_index()); - promotions.push(promoted); - } + let mut promoted = promoter.promote_candidate(candidate, promotions.len()); + promoted.source.promoted = Some(promotions.next_index()); + promotions.push(promoted); } // Insert each of `extra_statements` before its indicated location, which
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 4996257..e3395df 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml
@@ -23,7 +23,7 @@ smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } rustc_index = { path = "../rustc_index", package = "rustc_index" } bitflags = "1.2.1" -measureme = "9.1.0" +measureme = "10.0.0" libc = "0.2" stacker = "0.1.14" tempfile = "3.2"
diff --git a/compiler/rustc_data_structures/src/base_n.rs b/compiler/rustc_data_structures/src/base_n.rs index 81e2501..3c7bea2 100644 --- a/compiler/rustc_data_structures/src/base_n.rs +++ b/compiler/rustc_data_structures/src/base_n.rs
@@ -14,7 +14,7 @@ #[inline] pub fn push_str(mut n: u128, base: usize, output: &mut String) { - debug_assert!((2..=MAX_BASE).contains(&base)); + debug_assert!(base >= 2 && base <= MAX_BASE); let mut s = [0u8; 128]; let mut index = 0;
diff --git a/compiler/rustc_data_structures/src/graph/implementation/mod.rs b/compiler/rustc_data_structures/src/graph/implementation/mod.rs index 9ff401c..1aa7ac0 100644 --- a/compiler/rustc_data_structures/src/graph/implementation/mod.rs +++ b/compiler/rustc_data_structures/src/graph/implementation/mod.rs
@@ -206,11 +206,17 @@ AdjacentEdges { graph: self, direction, next: first_edge } } - pub fn successor_nodes(&self, source: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ { + pub fn successor_nodes<'a>( + &'a self, + source: NodeIndex, + ) -> impl Iterator<Item = NodeIndex> + 'a { self.outgoing_edges(source).targets() } - pub fn predecessor_nodes(&self, target: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ { + pub fn predecessor_nodes<'a>( + &'a self, + target: NodeIndex, + ) -> impl Iterator<Item = NodeIndex> + 'a { self.incoming_edges(target).sources() }
diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs index 1c6979d..a9db349 100644 --- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs +++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs
@@ -48,7 +48,7 @@ let node = frame.node; visited[node] = true; - for successor in frame.iter.by_ref() { + while let Some(successor) = frame.iter.next() { if !visited[successor] { stack.push(PostOrderFrame { node: successor, iter: graph.successors(successor) }); continue 'recurse; @@ -112,7 +112,7 @@ /// This is equivalent to just invoke `next` repeatedly until /// you get a `None` result. pub fn complete_search(&mut self) { - for _ in self {} + while let Some(_) = self.next() {} } /// Returns true if node has been visited thus far.
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 94e115e..77784bf 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs
@@ -11,7 +11,6 @@ #![feature(associated_type_bounds)] #![feature(auto_traits)] #![feature(bool_to_option)] -#![cfg_attr(bootstrap, feature(const_panic))] #![feature(control_flow_enum)] #![feature(core_intrinsics)] #![feature(extend_one)]
diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index caf515b..25b7a84 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs
@@ -390,7 +390,7 @@ .map(|(index, _node)| Error { error: error.clone(), backtrace: self.error_at(index) }) .collect(); - self.compress(|_| unreachable!()); + self.compress(|_| assert!(false)); errors } @@ -612,7 +612,7 @@ fn compress(&mut self, mut outcome_cb: impl FnMut(&O)) { let orig_nodes_len = self.nodes.len(); let mut node_rewrites: Vec<_> = std::mem::take(&mut self.reused_node_vec); - assert!(node_rewrites.is_empty()); + debug_assert!(node_rewrites.is_empty()); node_rewrites.extend(0..orig_nodes_len); let mut dead_nodes = 0; @@ -623,13 +623,13 @@ // self.nodes[0..index - dead_nodes] are the first remaining nodes // self.nodes[index - dead_nodes..index] are all dead // self.nodes[index..] are unchanged - for (index, node_rewrite) in node_rewrites.iter_mut().enumerate() { + for index in 0..orig_nodes_len { let node = &self.nodes[index]; match node.state.get() { NodeState::Pending | NodeState::Waiting => { if dead_nodes > 0 { self.nodes.swap(index, index - dead_nodes); - *node_rewrite -= dead_nodes; + node_rewrites[index] -= dead_nodes; } } NodeState::Done => { @@ -646,7 +646,7 @@ } // Extract the success stories. outcome_cb(&node.obligation); - *node_rewrite = orig_nodes_len; + node_rewrites[index] = orig_nodes_len; dead_nodes += 1; } NodeState::Error => { @@ -655,7 +655,7 @@ // check against. self.active_cache.remove(&node.obligation.as_cache_key()); self.insert_into_error_cache(index); - *node_rewrite = orig_nodes_len; + node_rewrites[index] = orig_nodes_len; dead_nodes += 1; } NodeState::Success => unreachable!(),
diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 0bbd0ed..c219392 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs
@@ -110,12 +110,14 @@ const FUNCTION_ARGS = 1 << 6; const LLVM = 1 << 7; const INCR_RESULT_HASHING = 1 << 8; + const ARTIFACT_SIZES = 1 << 9; const DEFAULT = Self::GENERIC_ACTIVITIES.bits | Self::QUERY_PROVIDERS.bits | Self::QUERY_BLOCKED.bits | Self::INCR_CACHE_LOADS.bits | - Self::INCR_RESULT_HASHING.bits; + Self::INCR_RESULT_HASHING.bits | + Self::ARTIFACT_SIZES.bits; const ARGS = Self::QUERY_KEYS.bits | Self::FUNCTION_ARGS.bits; } @@ -136,6 +138,7 @@ ("args", EventFilter::ARGS), ("llvm", EventFilter::LLVM), ("incr-result-hashing", EventFilter::INCR_RESULT_HASHING), + ("artifact-sizes", EventFilter::ARTIFACT_SIZES), ]; /// Something that uniquely identifies a query invocation. @@ -285,6 +288,33 @@ }) } + /// Record the size of an artifact that the compiler produces + /// + /// `artifact_kind` is the class of artifact (e.g., query_cache, object_file, etc.) + /// `artifact_name` is an identifier to the specific artifact being stored (usually a filename) + #[inline(always)] + pub fn artifact_size<A>(&self, artifact_kind: &str, artifact_name: A, size: u64) + where + A: Borrow<str> + Into<String>, + { + drop(self.exec(EventFilter::ARTIFACT_SIZES, |profiler| { + let builder = EventIdBuilder::new(&profiler.profiler); + let event_label = profiler.get_or_alloc_cached_string(artifact_kind); + let event_arg = profiler.get_or_alloc_cached_string(artifact_name); + let event_id = builder.from_label_and_arg(event_label, event_arg); + let thread_id = get_thread_id(); + + profiler.profiler.record_integer_event( + profiler.artifact_size_event_kind, + event_id, + thread_id, + size, + ); + + TimingGuard::none() + })) + } + #[inline(always)] pub fn generic_activity_with_args( &self, @@ -372,7 +402,7 @@ ) { drop(self.exec(event_filter, |profiler| { let event_id = StringId::new_virtual(query_invocation_id.0); - let thread_id = std::thread::current().id().as_u64().get() as u32; + let thread_id = get_thread_id(); profiler.profiler.record_instant_event( event_kind(profiler), @@ -425,6 +455,7 @@ incremental_result_hashing_event_kind: StringId, query_blocked_event_kind: StringId, query_cache_hit_event_kind: StringId, + artifact_size_event_kind: StringId, } impl SelfProfiler { @@ -447,6 +478,7 @@ profiler.alloc_string("IncrementalResultHashing"); let query_blocked_event_kind = profiler.alloc_string("QueryBlocked"); let query_cache_hit_event_kind = profiler.alloc_string("QueryCacheHit"); + let artifact_size_event_kind = profiler.alloc_string("ArtifactSize"); let mut event_filter_mask = EventFilter::empty(); @@ -491,6 +523,7 @@ incremental_result_hashing_event_kind, query_blocked_event_kind, query_cache_hit_event_kind, + artifact_size_event_kind, }) } @@ -561,7 +594,7 @@ event_kind: StringId, event_id: EventId, ) -> TimingGuard<'a> { - let thread_id = std::thread::current().id().as_u64().get() as u32; + let thread_id = get_thread_id(); let raw_profiler = &profiler.profiler; let timing_guard = raw_profiler.start_recording_interval_event(event_kind, event_id, thread_id); @@ -655,6 +688,10 @@ format!("{:.3}", dur.as_secs_f64()) } +fn get_thread_id() -> u32 { + std::thread::current().id().as_u64().get() as u32 +} + // Memory reporting cfg_if! { if #[cfg(windows)] {
diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index e80db08..9efea12 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs
@@ -1,3 +1,4 @@ +use crate::stable_hasher::{HashStable, StableHasher}; use std::borrow::Borrow; use std::cmp::Ordering; use std::iter::FromIterator; @@ -16,17 +17,26 @@ /// stores data in a more compact way. It also supports accessing contiguous /// ranges of elements as a slice, and slices of already sorted elements can be /// inserted efficiently. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, Encodable, Decodable)] -pub struct SortedMap<K: Ord, V> { +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] +pub struct SortedMap<K, V> { data: Vec<(K, V)>, } -impl<K: Ord, V> SortedMap<K, V> { +impl<K, V> Default for SortedMap<K, V> { #[inline] - pub fn new() -> SortedMap<K, V> { - SortedMap { data: vec![] } + fn default() -> SortedMap<K, V> { + SortedMap { data: Vec::new() } } +} +impl<K, V> SortedMap<K, V> { + #[inline] + pub const fn new() -> SortedMap<K, V> { + SortedMap { data: Vec::new() } + } +} + +impl<K: Ord, V> SortedMap<K, V> { /// Construct a `SortedMap` from a presorted set of elements. This is faster /// than creating an empty map and then inserting the elements individually. /// @@ -205,10 +215,10 @@ R: RangeBounds<K>, { let start = match range.start_bound() { - Bound::Included(k) => match self.lookup_index_for(k) { + Bound::Included(ref k) => match self.lookup_index_for(k) { Ok(index) | Err(index) => index, }, - Bound::Excluded(k) => match self.lookup_index_for(k) { + Bound::Excluded(ref k) => match self.lookup_index_for(k) { Ok(index) => index + 1, Err(index) => index, }, @@ -216,11 +226,11 @@ }; let end = match range.end_bound() { - Bound::Included(k) => match self.lookup_index_for(k) { + Bound::Included(ref k) => match self.lookup_index_for(k) { Ok(index) => index + 1, Err(index) => index, }, - Bound::Excluded(k) => match self.lookup_index_for(k) { + Bound::Excluded(ref k) => match self.lookup_index_for(k) { Ok(index) | Err(index) => index, }, Bound::Unbounded => self.data.len(), @@ -281,5 +291,12 @@ } } +impl<K: HashStable<CTX>, V: HashStable<CTX>, CTX> HashStable<CTX> for SortedMap<K, V> { + #[inline] + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + self.data.hash_stable(ctx, hasher); + } +} + #[cfg(test)] mod tests;
diff --git a/compiler/rustc_data_structures/src/sorted_map/index_map.rs b/compiler/rustc_data_structures/src/sorted_map/index_map.rs index 1395bb1..61c7239 100644 --- a/compiler/rustc_data_structures/src/sorted_map/index_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map/index_map.rs
@@ -34,39 +34,47 @@ } impl<I: Idx, K: Ord, V> SortedIndexMultiMap<I, K, V> { + #[inline] pub fn new() -> Self { SortedIndexMultiMap { items: IndexVec::new(), idx_sorted_by_item_key: Vec::new() } } + #[inline] pub fn len(&self) -> usize { self.items.len() } + #[inline] pub fn is_empty(&self) -> bool { self.items.is_empty() } /// Returns an iterator over the items in the map in insertion order. + #[inline] pub fn into_iter(self) -> impl DoubleEndedIterator<Item = (K, V)> { self.items.into_iter() } /// Returns an iterator over the items in the map in insertion order along with their indices. + #[inline] pub fn into_iter_enumerated(self) -> impl DoubleEndedIterator<Item = (I, (K, V))> { self.items.into_iter_enumerated() } /// Returns an iterator over the items in the map in insertion order. + #[inline] pub fn iter(&self) -> impl '_ + DoubleEndedIterator<Item = (&K, &V)> { self.items.iter().map(|(ref k, ref v)| (k, v)) } /// Returns an iterator over the items in the map in insertion order along with their indices. + #[inline] pub fn iter_enumerated(&self) -> impl '_ + DoubleEndedIterator<Item = (I, (&K, &V))> { self.items.iter_enumerated().map(|(i, (ref k, ref v))| (i, (k, v))) } /// Returns the item in the map with the given index. + #[inline] pub fn get(&self, idx: I) -> Option<&(K, V)> { self.items.get(idx) } @@ -75,7 +83,8 @@ /// /// If there are multiple items that are equivalent to `key`, they will be yielded in /// insertion order. - pub fn get_by_key(&self, key: K) -> impl Iterator<Item = &V> { + #[inline] + pub fn get_by_key(&'a self, key: K) -> impl 'a + Iterator<Item = &'a V> { self.get_by_key_enumerated(key).map(|(_, v)| v) } @@ -84,7 +93,8 @@ /// /// If there are multiple items that are equivalent to `key`, they will be yielded in /// insertion order. - pub fn get_by_key_enumerated(&self, key: K) -> impl Iterator<Item = (I, &V)> { + #[inline] + pub fn get_by_key_enumerated(&'a self, key: K) -> impl '_ + Iterator<Item = (I, &V)> { let lower_bound = self.idx_sorted_by_item_key.partition_point(|&i| self.items[i].0 < key); self.idx_sorted_by_item_key[lower_bound..].iter().map_while(move |&i| { let (k, v) = &self.items[i];
diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs index d4274e9..2de05cd 100644 --- a/compiler/rustc_data_structures/src/sso/map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs
@@ -257,7 +257,11 @@ pub fn remove(&mut self, key: &K) -> Option<V> { match self { SsoHashMap::Array(array) => { - array.iter().position(|(k, _v)| k == key).map(|index| array.swap_remove(index).1) + if let Some(index) = array.iter().position(|(k, _v)| k == key) { + Some(array.swap_remove(index).1) + } else { + None + } } SsoHashMap::Map(map) => map.remove(key), } @@ -268,7 +272,11 @@ pub fn remove_entry(&mut self, key: &K) -> Option<(K, V)> { match self { SsoHashMap::Array(array) => { - array.iter().position(|(k, _v)| k == key).map(|index| array.swap_remove(index)) + if let Some(index) = array.iter().position(|(k, _v)| k == key) { + Some(array.swap_remove(index)) + } else { + None + } } SsoHashMap::Map(map) => map.remove_entry(key), } @@ -415,14 +423,14 @@ /// adapts Item of array reference iterator to Item of hashmap reference iterator. #[inline(always)] -fn adapt_array_ref_it<K, V>(pair: &(K, V)) -> (&K, &V) { +fn adapt_array_ref_it<K, V>(pair: &'a (K, V)) -> (&'a K, &'a V) { let (a, b) = pair; (a, b) } /// adapts Item of array mut reference iterator to Item of hashmap mut reference iterator. #[inline(always)] -fn adapt_array_mut_it<K, V>(pair: &mut (K, V)) -> (&K, &mut V) { +fn adapt_array_mut_it<K, V>(pair: &'a mut (K, V)) -> (&'a K, &'a mut V) { let (a, b) = pair; (a, b) }
diff --git a/compiler/rustc_data_structures/src/sso/set.rs b/compiler/rustc_data_structures/src/sso/set.rs index f71522d..29baf4e 100644 --- a/compiler/rustc_data_structures/src/sso/set.rs +++ b/compiler/rustc_data_structures/src/sso/set.rs
@@ -75,7 +75,7 @@ /// An iterator visiting all elements in arbitrary order. /// The iterator element type is `&'a T`. #[inline] - pub fn iter(&self) -> impl Iterator<Item = &T> { + pub fn iter(&'a self) -> impl Iterator<Item = &'a T> { self.into_iter() }
diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 2e992e7..f800ec6 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs
@@ -229,14 +229,14 @@ impl<CTX> HashStable<CTX> for f32 { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - let val: u32 = self.to_bits(); + let val: u32 = unsafe { ::std::mem::transmute(*self) }; val.hash_stable(ctx, hasher); } } impl<CTX> HashStable<CTX> for f64 { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - let val: u64 = self.to_bits(); + let val: u64 = unsafe { ::std::mem::transmute(*self) }; val.hash_stable(ctx, hasher); } } @@ -301,6 +301,13 @@ } } +impl<CTX> HashStable<CTX> for [u8] { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + self.len().hash_stable(ctx, hasher); + hasher.write(self); + } +} + impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Vec<T> { #[inline] fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
diff --git a/compiler/rustc_data_structures/src/stack.rs b/compiler/rustc_data_structures/src/stack.rs index ba22c7f..a4964b7 100644 --- a/compiler/rustc_data_structures/src/stack.rs +++ b/compiler/rustc_data_structures/src/stack.rs
@@ -5,7 +5,6 @@ // Only the first stack that is pushed, grows exponentially (2^n * STACK_PER_RECURSION) from then // on. This flag has performance relevant characteristics. Don't set it too high. -#[allow(clippy::identity_op)] const STACK_PER_RECURSION: usize = 1 * 1024 * 1024; // 1MB /// Grows the stack on demand to prevent stack overflow. Call this in strategic locations
diff --git a/compiler/rustc_data_structures/src/tiny_list.rs b/compiler/rustc_data_structures/src/tiny_list.rs index 9e605ea..9b07f86 100644 --- a/compiler/rustc_data_structures/src/tiny_list.rs +++ b/compiler/rustc_data_structures/src/tiny_list.rs
@@ -48,7 +48,7 @@ #[inline] pub fn contains(&self, data: &T) -> bool { let mut elem = self.head.as_ref(); - while let Some(e) = elem { + while let Some(ref e) = elem { if &e.data == data { return true; }
diff --git a/compiler/rustc_data_structures/src/vec_linked_list.rs b/compiler/rustc_data_structures/src/vec_linked_list.rs index ce60d40..1cf030d 100644 --- a/compiler/rustc_data_structures/src/vec_linked_list.rs +++ b/compiler/rustc_data_structures/src/vec_linked_list.rs
@@ -2,8 +2,8 @@ pub fn iter<Ls>( first: Option<Ls::LinkIndex>, - links: &Ls, -) -> impl Iterator<Item = Ls::LinkIndex> + '_ + links: &'a Ls, +) -> impl Iterator<Item = Ls::LinkIndex> + 'a where Ls: Links, {
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 9a57ec9..6ff9434 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs
@@ -267,6 +267,7 @@ None, compiler.output_dir(), compiler.output_file(), + compiler.temps_dir(), ); if should_stop == Compilation::Stop { @@ -295,6 +296,7 @@ Some(compiler.input()), compiler.output_dir(), compiler.output_file(), + compiler.temps_dir(), ) .and_then(|| { RustcDefaultCalls::list_metadata( @@ -647,6 +649,7 @@ input: Option<&Input>, odir: &Option<PathBuf>, ofile: &Option<PathBuf>, + temps_dir: &Option<PathBuf>, ) -> Compilation { use rustc_session::config::PrintRequest::*; // PrintRequest::NativeStaticLibs is special - printed during linking @@ -685,7 +688,7 @@ }); let attrs = attrs.as_ref().unwrap(); let t_outputs = rustc_interface::util::build_output_filenames( - input, odir, ofile, attrs, sess, + input, odir, ofile, temps_dir, attrs, sess, ); let id = rustc_session::output::find_crate_name(sess, attrs, input); if *req == PrintRequest::CrateName { @@ -733,7 +736,12 @@ println!("{}", cfg); } } - RelocationModels | CodeModels | TlsModels | TargetCPUs | TargetFeatures => { + RelocationModels + | CodeModels + | TlsModels + | TargetCPUs + | StackProtectorStrategies + | TargetFeatures => { codegen_backend.print(*req, sess); } // Any output here interferes with Cargo's parsing of other printed output
diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs index 8e8bea9..2e9050d 100644 --- a/compiler/rustc_driver/src/pretty.rs +++ b/compiler/rustc_driver/src/pretty.rs
@@ -59,23 +59,23 @@ } fn call_with_pp_support_hir<A, F>(ppmode: &PpHirMode, tcx: TyCtxt<'_>, f: F) -> A where - F: FnOnce(&dyn HirPrinterSupport<'_>, &hir::Crate<'_>) -> A, + F: FnOnce(&dyn HirPrinterSupport<'_>, hir_map::Map<'_>) -> A, { match *ppmode { PpHirMode::Normal => { let annotation = NoAnn { sess: tcx.sess, tcx: Some(tcx) }; - f(&annotation, tcx.hir().krate()) + f(&annotation, tcx.hir()) } PpHirMode::Identified => { let annotation = IdentifiedAnnotation { sess: tcx.sess, tcx: Some(tcx) }; - f(&annotation, tcx.hir().krate()) + f(&annotation, tcx.hir()) } PpHirMode::Typed => { abort_on_err(tcx.analysis(()), tcx.sess); let annotation = TypedAnnotation { tcx, maybe_typeck_results: Cell::new(None) }; - tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir().krate())) + tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir())) } } } @@ -443,17 +443,27 @@ format!("{:#?}", krate) } - Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, krate| { + Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, hir_map| { debug!("pretty printing HIR {:?}", s); let sess = annotation.sess(); let sm = sess.source_map(); - pprust_hir::print_crate(sm, krate, src_name, src, annotation.pp_ann()) + let attrs = |id| hir_map.attrs(id); + pprust_hir::print_crate( + sm, + hir_map.root_module(), + src_name, + src, + &attrs, + annotation.pp_ann(), + ) }), - HirTree => call_with_pp_support_hir(&PpHirMode::Normal, tcx, move |_annotation, krate| { - debug!("pretty printing HIR tree"); - format!("{:#?}", krate) - }), + HirTree => { + call_with_pp_support_hir(&PpHirMode::Normal, tcx, move |_annotation, hir_map| { + debug!("pretty printing HIR tree"); + format!("{:#?}", hir_map.krate()) + }) + } _ => unreachable!(), };
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 724e3f7..ce26ff6 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -484,12 +484,13 @@ E0783: include_str!("./error_codes/E0783.md"), E0784: include_str!("./error_codes/E0784.md"), E0785: include_str!("./error_codes/E0785.md"), +E0786: include_str!("./error_codes/E0786.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard -// E0019, merged into E0015 -// E0035, merged into E0087/E0089 -// E0036, merged into E0087/E0089 +// E0019, // merged into E0015 +// E0035, // merged into E0087/E0089 +// E0036, // merged into E0087/E0089 // E0068, // E0085, // E0086, @@ -504,8 +505,8 @@ // E0134, // E0135, // E0141, -// E0153, unused error code -// E0157, unused error code +// E0153, // unused error code +// E0157, // unused error code // E0159, // use of trait `{}` as struct constructor // E0163, // merged into E0071 // E0167, @@ -573,24 +574,24 @@ // between structures with the same definition // E0385, // {} in an aliasable location // E0402, // cannot use an outer type parameter in this context -// E0406, merged into 420 -// E0410, merged into 408 -// E0413, merged into 530 -// E0414, merged into 530 -// E0417, merged into 532 -// E0418, merged into 532 -// E0419, merged into 531 -// E0420, merged into 532 -// E0421, merged into 531 -// E0427, merged into 530 +// E0406, // merged into 420 +// E0410, // merged into 408 +// E0413, // merged into 530 +// E0414, // merged into 530 +// E0417, // merged into 532 +// E0418, // merged into 532 +// E0419, // merged into 531 +// E0420, // merged into 532 +// E0421, // merged into 531 +// E0427, // merged into 530 // E0456, // plugin `..` is not available for triple `..` E0457, // plugin `..` only found in rlib format, but must be available... E0460, // found possibly newer version of crate `..` E0461, // couldn't find crate `..` with expected target triple .. E0462, // found staticlib `..` instead of rlib or dylib E0465, // multiple .. candidates for `..` found -// E0467, removed -// E0470, removed +// E0467, // removed +// E0470, // removed // E0471, // constant evaluation error (in pattern) E0472, // llvm_asm! is unsupported on this target // E0473, // dereference of reference outside its lifetime @@ -610,8 +611,7 @@ E0490, // a value of type `..` is borrowed for too long E0514, // metadata version mismatch E0519, // local crate and dependency have same (crate-name, disambiguator) - // two dependencies have same (crate-name, disambiguator) but different SVH - E0523, + E0523, // two dependencies have same (crate-name, disambiguator) but different SVH // E0526, // shuffle indices are not constant // E0540, // multiple rustc_deprecated attributes // E0548, // replaced with a generic attribute input check @@ -638,7 +638,7 @@ E0711, // a feature has been declared with conflicting stability attributes E0717, // rustc_promotable without stability attribute // E0721, // `await` keyword -// E0723, unstable feature in `const` context +// E0723, // unstable feature in `const` context E0726, // non-explicit (not `'_`) elided lifetime in unsupported position // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. E0772, // `'static' obligation coming from `impl dyn Trait {}` or `impl Foo for dyn Bar {}`.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0206.md b/compiler/rustc_error_codes/src/error_codes/E0206.md index da53b67..4405a21 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0206.md +++ b/compiler/rustc_error_codes/src/error_codes/E0206.md
@@ -4,15 +4,12 @@ Erroneous code example: ```compile_fail,E0206 -type Foo = [u8; 256]; -impl Copy for Foo { } // error! - #[derive(Copy, Clone)] struct Bar; impl Copy for &'static mut Bar { } // error! ``` -You can only implement `Copy` for a struct or an enum. Both of the previous -examples will fail, because neither `[u8; 256]` nor `&'static mut Bar` -(mutable reference to `Bar`) is a struct or enum. +You can only implement `Copy` for a struct or an enum. +The previous example will fail because `&'static mut Bar` +is not a struct or enum.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0482.md b/compiler/rustc_error_codes/src/error_codes/E0482.md index 58ebf43..ad36381 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0482.md +++ b/compiler/rustc_error_codes/src/error_codes/E0482.md
@@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + A lifetime of a returned value does not outlive the function call. Erroneous code example: -```compile_fail,E0482 +```compile_fail,E0700 fn prefix<'a>( words: impl Iterator<Item = &'a str> ) -> impl Iterator<Item = String> { // error! @@ -41,7 +43,7 @@ A similar lifetime problem might arise when returning closures: -```compile_fail,E0482 +```compile_fail,E0700 fn foo( x: &mut Vec<i32> ) -> impl FnMut(&mut Vec<i32>) -> &[i32] { // error!
diff --git a/compiler/rustc_error_codes/src/error_codes/E0637.md b/compiler/rustc_error_codes/src/error_codes/E0637.md index d906895..62d5565 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0637.md +++ b/compiler/rustc_error_codes/src/error_codes/E0637.md
@@ -1,35 +1,51 @@ -An underscore `_` character has been used as the identifier for a lifetime. +`'_` lifetime name or `&T` without an explicit lifetime name has been used +on illegal place. Erroneous code example: ```compile_fail,E0106,E0637 -fn longest<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { - //^^ `'_` is a reserved lifetime name +fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { + //^^ `'_` is a reserved lifetime name if str1.len() > str2.len() { str1 } else { str2 } } + +fn and_without_explicit_lifetime<T>() +where + T: Into<&u32>, + //^ `&` without an explicit lifetime name +{ +} ``` -`'_`, cannot be used as a lifetime identifier because it is a reserved for the -anonymous lifetime. To fix this, use a lowercase letter such as 'a, or a series -of lowercase letters such as `'foo`. For more information, see [the -book][bk-no]. For more information on using the anonymous lifetime in rust -nightly, see [the nightly book][bk-al]. +First, `'_` cannot be used as a lifetime identifier in some places +because it is a reserved for the anonymous lifetime. Second, `&T` +without an explicit lifetime name cannot also be used in some places. +To fix them, use a lowercase letter such as `'a`, or a series +of lowercase letters such as `'foo`. For more information about lifetime +identifier, see [the book][bk-no]. For more information on using +the anonymous lifetime in Rust 2018, see [the Rust 2018 blog post][blog-al]. Corrected example: ``` -fn longest<'a>(str1: &'a str, str2: &'a str) -> &'a str { +fn underscore_lifetime<'a>(str1: &'a str, str2: &'a str) -> &'a str { if str1.len() > str2.len() { str1 } else { str2 } } + +fn and_without_explicit_lifetime<'foo, T>() +where + T: Into<&'foo u32>, +{ +} ``` [bk-no]: https://doc.rust-lang.org/book/appendix-02-operators.html#non-operator-symbols -[bk-al]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/ownership-and-lifetimes/the-anonymous-lifetime.html +[blog-al]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html#more-lifetime-elision-rules
diff --git a/compiler/rustc_error_codes/src/error_codes/E0786.md b/compiler/rustc_error_codes/src/error_codes/E0786.md new file mode 100644 index 0000000..4a9635bf --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0786.md
@@ -0,0 +1,14 @@ +A metadata file was invalid. + +Erroneous code example: + +```ignore (needs extern files) +use ::foo; // error: found invalid metadata files for crate `foo` +``` + +When loading crates, each crate must have a valid metadata file. +Invalid files could be caused by filesystem corruption, +an IO error while reading the file, or (rarely) a bug in the compiler itself. + +Consider deleting the file and recreating it, +or reporting a bug against the compiler.
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 1eb4974..9db8f75 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -66,7 +66,7 @@ /// Maps `Diagnostic::Level` to `snippet::AnnotationType` fn annotation_type_for_level(level: Level) -> AnnotationType { match level { - Level::Bug | Level::Fatal | Level::Error => AnnotationType::Error, + Level::Bug | Level::Fatal | Level::Error { .. } => AnnotationType::Error, Level::Warning => AnnotationType::Warning, Level::Note => AnnotationType::Note, Level::Help => AnnotationType::Help,
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 41a7326..e5116cd 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -114,7 +114,7 @@ pub fn is_error(&self) -> bool { match self.level { - Level::Bug | Level::Fatal | Level::Error | Level::FailureNote => true, + Level::Bug | Level::Fatal | Level::Error { .. } | Level::FailureNote => true, Level::Warning | Level::Note | Level::Help | Level::Cancelled | Level::Allow => false, } @@ -465,10 +465,14 @@ suggestions: impl Iterator<Item = String>, applicability: Applicability, ) -> &mut Self { + let mut suggestions: Vec<_> = suggestions.collect(); + suggestions.sort(); + let substitutions = suggestions + .into_iter() + .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }) + .collect(); self.suggestions.push(CodeSuggestion { - substitutions: suggestions - .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }) - .collect(), + substitutions, msg: msg.to_owned(), style: SuggestionStyle::ShowCode, applicability,
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 7fa29bd..d64a589 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs
@@ -15,7 +15,7 @@ use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::styled_buffer::StyledBuffer; use crate::{ - CodeSuggestion, Diagnostic, DiagnosticId, Level, SubDiagnostic, SubstitutionHighlight, + CodeSuggestion, Diagnostic, DiagnosticId, Handler, Level, SubDiagnostic, SubstitutionHighlight, SuggestionStyle, }; @@ -449,11 +449,7 @@ span: &mut MultiSpan, children: &mut Vec<SubDiagnostic>, ) { - let source_map = if let Some(ref sm) = source_map { - sm - } else { - return; - }; + let Some(source_map) = source_map else { return }; debug!("fix_multispans_in_extern_macros: before: span={:?} children={:?}", span, children); self.fix_multispan_in_extern_macros(source_map, span); for child in children.iter_mut() { @@ -527,14 +523,27 @@ } } -/// An emitter that does nothing when emitting a diagnostic. -pub struct SilentEmitter; +/// An emitter that does nothing when emitting a non-fatal diagnostic. +/// Fatal diagnostics are forwarded to `fatal_handler` to avoid silent +/// failures of rustc, as witnessed e.g. in issue #89358. +pub struct SilentEmitter { + pub fatal_handler: Handler, + pub fatal_note: Option<String>, +} impl Emitter for SilentEmitter { fn source_map(&self) -> Option<&Lrc<SourceMap>> { None } - fn emit_diagnostic(&mut self, _: &Diagnostic) {} + fn emit_diagnostic(&mut self, d: &Diagnostic) { + if d.level == Level::Fatal { + let mut d = d.clone(); + if let Some(ref note) = self.fatal_note { + d.note(note); + } + self.fatal_handler.emit_diagnostic(&d); + } + } } /// Maximum number of lines we will print for a multiline suggestion; arbitrary. @@ -721,7 +730,7 @@ } let source_string = match file.get_line(line.line_index - 1) { - Some(s) => replace_tabs(&*s), + Some(s) => normalize_whitespace(&*s), None => return Vec::new(), }; @@ -1257,22 +1266,37 @@ } self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None); } else { + let mut label_width = 0; // The failure note level itself does not provide any useful diagnostic information if *level != Level::FailureNote { buffer.append(0, level.to_str(), Style::Level(*level)); + label_width += level.to_str().len(); } // only render error codes, not lint codes if let Some(DiagnosticId::Error(ref code)) = *code { buffer.append(0, "[", Style::Level(*level)); buffer.append(0, &code, Style::Level(*level)); buffer.append(0, "]", Style::Level(*level)); + label_width += 2 + code.len(); } let header_style = if is_secondary { Style::HeaderMsg } else { Style::MainHeaderMsg }; if *level != Level::FailureNote { buffer.append(0, ": ", header_style); + label_width += 2; } for &(ref text, _) in msg.iter() { - buffer.append(0, &replace_tabs(text), header_style); + // Account for newlines to align output to its label. + for (line, text) in normalize_whitespace(text).lines().enumerate() { + buffer.append( + 0 + line, + &format!( + "{}{}", + if line == 0 { String::new() } else { " ".repeat(label_width) }, + text + ), + header_style, + ); + } } } @@ -1526,7 +1550,7 @@ self.draw_line( &mut buffer, - &replace_tabs(&unannotated_line), + &normalize_whitespace(&unannotated_line), annotated_file.lines[line_idx + 1].line_index - 1, last_buffer_line_num, width_offset, @@ -1648,7 +1672,7 @@ buffer.puts( row_num - 1, max_line_num_len + 3, - &replace_tabs( + &normalize_whitespace( &*file_lines .file .get_line(file_lines.lines[line_pos].line_index) @@ -1674,7 +1698,7 @@ } // print the suggestion - buffer.append(row_num, &replace_tabs(line), Style::NoStyle); + buffer.append(row_num, &normalize_whitespace(line), Style::NoStyle); // Colorize addition/replacements with green. for &SubstitutionHighlight { start, end } in highlight_parts { @@ -2057,6 +2081,7 @@ // We replace some characters so the CLI output is always consistent and underlines aligned. const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\t', " "), // We do our own tab replacement + ('\u{200D}', ""), // Replace ZWJ with nothing for consistent terminal output of grapheme clusters. ('\u{202A}', ""), // The following unicode text flow control characters are inconsistently ('\u{202B}', ""), // supported accross CLIs and can cause confusion due to the bytes on disk ('\u{202D}', ""), // not corresponding to the visible source code, so we replace them always. @@ -2068,7 +2093,7 @@ ('\u{2069}', ""), ]; -fn replace_tabs(str: &str) -> String { +fn normalize_whitespace(str: &str) -> String { let mut s = str.to_string(); for (c, replacement) in OUTPUT_REPLACEMENTS { s = s.replace(*c, replacement);
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 9b2094a..bb3d3a4 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs
@@ -6,8 +6,9 @@ #![feature(crate_visibility_modifier)] #![feature(backtrace)] #![feature(if_let_guard)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(iter_zip)] +#![feature(let_else)] #![feature(nll)] #[macro_use] @@ -410,6 +411,8 @@ /// as well as inconsistent state observation. struct HandlerInner { flags: HandlerFlags, + /// The number of lint errors that have been emitted. + lint_err_count: usize, /// The number of errors that have been emitted, including duplicates. /// /// This is not necessarily the count that's reported to the user once @@ -549,6 +552,7 @@ flags, inner: Lock::new(HandlerInner { flags, + lint_err_count: 0, err_count: 0, warn_count: 0, deduplicated_err_count: 0, @@ -725,7 +729,13 @@ /// Construct a builder at the `Error` level with the `msg`. // FIXME: This method should be removed (every error should have an associated error code). pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> { - DiagnosticBuilder::new(self, Level::Error, msg) + DiagnosticBuilder::new(self, Level::Error { lint: false }, msg) + } + + /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors. + #[doc(hidden)] + pub fn struct_err_lint(&self, msg: &str) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Level::Error { lint: true }, msg) } /// Construct a builder at the `Error` level with the `msg` and the `code`. @@ -789,11 +799,14 @@ } pub fn span_err(&self, span: impl Into<MultiSpan>, msg: &str) { - self.emit_diag_at_span(Diagnostic::new(Error, msg), span); + self.emit_diag_at_span(Diagnostic::new(Error { lint: false }, msg), span); } pub fn span_err_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) { - self.emit_diag_at_span(Diagnostic::new_with_code(Error, Some(code), msg), span); + self.emit_diag_at_span( + Diagnostic::new_with_code(Error { lint: false }, Some(code), msg), + span, + ); } pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: &str) { @@ -861,6 +874,9 @@ pub fn has_errors(&self) -> bool { self.inner.borrow().has_errors() } + pub fn has_errors_or_lint_errors(&self) -> bool { + self.inner.borrow().has_errors_or_lint_errors() + } pub fn has_errors_or_delayed_span_bugs(&self) -> bool { self.inner.borrow().has_errors_or_delayed_span_bugs() } @@ -978,7 +994,11 @@ } } if diagnostic.is_error() { - self.bump_err_count(); + if matches!(diagnostic.level, Level::Error { lint: true }) { + self.bump_lint_err_count(); + } else { + self.bump_err_count(); + } } else { self.bump_warn_count(); } @@ -1072,11 +1092,14 @@ fn has_errors(&self) -> bool { self.err_count() > 0 } + fn has_errors_or_lint_errors(&self) -> bool { + self.has_errors() || self.lint_err_count > 0 + } fn has_errors_or_delayed_span_bugs(&self) -> bool { self.has_errors() || !self.delayed_span_bugs.is_empty() } fn has_any_message(&self) -> bool { - self.err_count() > 0 || self.warn_count > 0 + self.err_count() > 0 || self.lint_err_count > 0 || self.warn_count > 0 } fn abort_if_errors(&mut self) { @@ -1130,7 +1153,7 @@ } fn err(&mut self, msg: &str) { - self.emit_error(Error, msg); + self.emit_error(Error { lint: false }, msg); } /// Emit an error; level should be `Error` or `Fatal`. @@ -1166,6 +1189,11 @@ } } + fn bump_lint_err_count(&mut self) { + self.lint_err_count += 1; + self.panic_if_treat_err_as_bug(); + } + fn bump_err_count(&mut self) { self.err_count += 1; self.panic_if_treat_err_as_bug(); @@ -1209,7 +1237,10 @@ pub enum Level { Bug, Fatal, - Error, + Error { + /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called. + lint: bool, + }, Warning, Note, Help, @@ -1228,7 +1259,7 @@ fn color(self) -> ColorSpec { let mut spec = ColorSpec::new(); match self { - Bug | Fatal | Error => { + Bug | Fatal | Error { .. } => { spec.set_fg(Some(Color::Red)).set_intense(true); } Warning => { @@ -1249,7 +1280,7 @@ pub fn to_str(self) -> &'static str { match self { Bug => "error: internal compiler error", - Fatal | Error => "error", + Fatal | Error { .. } => "error", Warning => "warning", Note => "note", Help => "help",
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index f548e28..89dbd64 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs
@@ -383,7 +383,7 @@ Unsafe::No, ModKind::Loaded(krate.items, Inline::Yes, krate.span) ), - ident: Ident::invalid(), + ident: Ident::empty(), id: ast::DUMMY_NODE_ID, vis: ast::Visibility { span: krate.span.shrink_to_lo(), @@ -447,9 +447,7 @@ let mut undetermined_invocations = Vec::new(); let (mut progress, mut force) = (false, !self.monotonic); loop { - let (invoc, ext) = if let Some(invoc) = invocations.pop() { - invoc - } else { + let Some((invoc, ext)) = invocations.pop() else { self.resolve_imports(); if undetermined_invocations.is_empty() { break; @@ -1426,7 +1424,7 @@ _ => unreachable!(), }) } - ast::ItemKind::Mod(_, ref mut mod_kind) if ident != Ident::invalid() => { + ast::ItemKind::Mod(_, ref mut mod_kind) if ident != Ident::empty() => { let (file_path, dir_path, dir_ownership) = match mod_kind { ModKind::Loaded(_, inline, _) => { // Inline `mod foo { ... }`, but we still need to push directories. @@ -1508,7 +1506,7 @@ _ => { item.attrs = attrs; // The crate root is special - don't assign an ID to it. - if !(matches!(item.kind, ast::ItemKind::Mod(..)) && ident == Ident::invalid()) { + if !(matches!(item.kind, ast::ItemKind::Mod(..)) && ident == Ident::empty()) { assign_id!(self, &mut item.id, || noop_flat_map_item(item, self)) } else { noop_flat_map_item(item, self)
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 6dfeb04..4e84a9d 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs
@@ -1,9 +1,10 @@ #![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![feature(destructuring_assignment)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(if_let_guard)] #![feature(iter_zip)] +#![feature(let_else)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] #![feature(proc_macro_span)]
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 363cc72..dedc6c6 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -204,7 +204,7 @@ pprust::token_to_string(&token), ); sess.span_diagnostic.span_err(token.span, &msg); - TokenTree::MetaVar(token.span, Ident::invalid()) + TokenTree::MetaVar(token.span, Ident::empty()) } // There are no more tokens. Just return the `$` we already have.
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 9ed5c8b..88e1623 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -19,9 +19,7 @@ struct Marker(LocalExpnId, Transparency); impl MutVisitor for Marker { - fn token_visiting_enabled(&self) -> bool { - true - } + const VISIT_TOKENS: bool = true; fn visit_span(&mut self, span: &mut Span) { *span = span.apply_mark(self.0.to_expn_id(), self.1) @@ -116,10 +114,8 @@ loop { // Look at the last frame on the stack. - let tree = if let Some(tree) = stack.last_mut().unwrap().next() { - // If it still has a TokenTree we have not looked at yet, use that tree. - tree - } else { + // If it still has a TokenTree we have not looked at yet, use that tree. + let Some(tree) = stack.last_mut().unwrap().next() else { // This else-case never produces a value for `tree` (it `continue`s or `return`s). // Otherwise, if we have just reached the end of a sequence and we can keep repeating, @@ -190,9 +186,7 @@ LockstepIterSize::Constraint(len, _) => { // We do this to avoid an extra clone above. We know that this is a // sequence already. - let (sp, seq) = if let mbe::TokenTree::Sequence(sp, seq) = seq { - (sp, seq) - } else { + let mbe::TokenTree::Sequence(sp, seq) = seq else { unreachable!() };
diff --git a/compiler/rustc_expand/src/mut_visit/tests.rs b/compiler/rustc_expand/src/mut_visit/tests.rs index 0068539..8974d45 100644 --- a/compiler/rustc_expand/src/mut_visit/tests.rs +++ b/compiler/rustc_expand/src/mut_visit/tests.rs
@@ -15,9 +15,8 @@ struct ToZzIdentMutVisitor; impl MutVisitor for ToZzIdentMutVisitor { - fn token_visiting_enabled(&self) -> bool { - true - } + const VISIT_TOKENS: bool = true; + fn visit_ident(&mut self, ident: &mut Ident) { *ident = Ident::from_str("zz"); }
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 8e78fcb..12b6bc7 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs
@@ -23,7 +23,7 @@ } } - let ident = Ident::invalid(); + let ident = Ident::empty(); let attrs = Vec::new(); let vis = vis.unwrap_or(ast::Visibility { span: DUMMY_SP,
diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 3f84979..42c17a6 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs
@@ -24,8 +24,9 @@ span: Span, input: TokenStream, ) -> Result<TokenStream, ErrorReported> { + let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; let server = proc_macro_server::Rustc::new(ecx); - self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace).map_err(|e| { + self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace).map_err(|e| { let mut err = ecx.struct_span_err(span, "proc macro panicked"); if let Some(s) = e.as_str() { err.help(&format!("message: {}", s)); @@ -48,9 +49,10 @@ annotation: TokenStream, annotated: TokenStream, ) -> Result<TokenStream, ErrorReported> { + let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; let server = proc_macro_server::Rustc::new(ecx); self.client - .run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace) + .run(&EXEC_STRATEGY, server, annotation, annotated, proc_macro_backtrace) .map_err(|e| { let mut err = ecx.struct_span_err(span, "custom attribute panicked"); if let Some(s) = e.as_str() { @@ -97,19 +99,19 @@ nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No) }; + let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; let server = proc_macro_server::Rustc::new(ecx); - let stream = - match self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace) { - Ok(stream) => stream, - Err(e) => { - let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); - if let Some(s) = e.as_str() { - err.help(&format!("message: {}", s)); - } - err.emit(); - return ExpandResult::Ready(vec![]); + let stream = match self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace) { + Ok(stream) => stream, + Err(e) => { + let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); + if let Some(s) = e.as_str() { + err.help(&format!("message: {}", s)); } - }; + err.emit(); + return ExpandResult::Ready(vec![]); + } + }; let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count(); let mut parser =
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 5cb9719..fa9e98b 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -1,4 +1,4 @@ -use crate::base::{ExtCtxt, ResolverExpand}; +use crate::base::ExtCtxt; use rustc_ast as ast; use rustc_ast::token::{self, Nonterminal, NtIdent}; @@ -7,7 +7,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; -use rustc_errors::Diagnostic; +use rustc_errors::{Diagnostic, PResult}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; use rustc_lint_defs::BuiltinLintDiagnostics; use rustc_parse::lexer::nfc_normalize; @@ -53,11 +53,11 @@ } } -impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_>)> +impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)> for TokenTree<Group, Punct, Ident, Literal> { fn from_internal( - ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec<Self>, &mut Rustc<'_>), + ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec<Self>, &mut Rustc<'_, '_>), ) -> Self { use rustc_ast::token::*; @@ -146,10 +146,10 @@ SingleQuote => op!('\''), Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()), - Ident(name, is_raw) => tt!(Ident::new(rustc.sess, name, is_raw)), + Ident(name, is_raw) => tt!(Ident::new(rustc.sess(), name, is_raw)), Lifetime(name) => { let ident = symbol::Ident::new(name, span).without_first_quote(); - stack.push(tt!(Ident::new(rustc.sess, ident.name, false))); + stack.push(tt!(Ident::new(rustc.sess(), ident.name, false))); tt!(Punct::new('\'', true)) } Literal(lit) => tt!(Literal { lit }), @@ -181,15 +181,15 @@ Interpolated(nt) if let Some((name, is_raw)) = ident_name_compatibility_hack(&nt, span, rustc) => { - TokenTree::Ident(Ident::new(rustc.sess, name.name, is_raw, name.span)) + TokenTree::Ident(Ident::new(rustc.sess(), name.name, is_raw, name.span)) } Interpolated(nt) => { - let stream = nt_to_tokenstream(&nt, rustc.sess, CanSynthesizeMissingTokens::No); + let stream = nt_to_tokenstream(&nt, rustc.sess(), CanSynthesizeMissingTokens::No); TokenTree::Group(Group { delimiter: Delimiter::None, stream, span: DelimSpan::from_single(span), - flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess), + flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess()), }) } @@ -273,7 +273,7 @@ impl ToInternal<rustc_errors::Level> for Level { fn to_internal(self) -> rustc_errors::Level { match self { - Level::Error => rustc_errors::Level::Error, + Level::Error => rustc_errors::Level::Error { lint: false }, Level::Warning => rustc_errors::Level::Warning, Level::Note => rustc_errors::Level::Note, Level::Help => rustc_errors::Level::Help, @@ -355,38 +355,38 @@ span: Span, } -pub(crate) struct Rustc<'a> { - resolver: &'a dyn ResolverExpand, - sess: &'a ParseSess, +pub(crate) struct Rustc<'a, 'b> { + ecx: &'a mut ExtCtxt<'b>, def_site: Span, call_site: Span, mixed_site: Span, - span_debug: bool, krate: CrateNum, rebased_spans: FxHashMap<usize, Span>, } -impl<'a> Rustc<'a> { - pub fn new(cx: &'a ExtCtxt<'_>) -> Self { - let expn_data = cx.current_expansion.id.expn_data(); +impl<'a, 'b> Rustc<'a, 'b> { + pub fn new(ecx: &'a mut ExtCtxt<'b>) -> Self { + let expn_data = ecx.current_expansion.id.expn_data(); Rustc { - resolver: cx.resolver, - sess: cx.parse_sess(), - def_site: cx.with_def_site_ctxt(expn_data.def_site), - call_site: cx.with_call_site_ctxt(expn_data.call_site), - mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site), - span_debug: cx.ecfg.span_debug, + def_site: ecx.with_def_site_ctxt(expn_data.def_site), + call_site: ecx.with_call_site_ctxt(expn_data.call_site), + mixed_site: ecx.with_mixed_site_ctxt(expn_data.call_site), krate: expn_data.macro_def_id.unwrap().krate, rebased_spans: FxHashMap::default(), + ecx, } } + fn sess(&self) -> &ParseSess { + self.ecx.parse_sess() + } + fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Literal { Literal { lit: token::Lit::new(kind, symbol, suffix), span: server::Span::call_site(self) } } } -impl server::Types for Rustc<'_> { +impl server::Types for Rustc<'_, '_> { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; type TokenStreamBuilder = tokenstream::TokenStreamBuilder; @@ -401,17 +401,20 @@ type Span = Span; } -impl server::FreeFunctions for Rustc<'_> { +impl server::FreeFunctions for Rustc<'_, '_> { fn track_env_var(&mut self, var: &str, value: Option<&str>) { - self.sess.env_depinfo.borrow_mut().insert((Symbol::intern(var), value.map(Symbol::intern))); + self.sess() + .env_depinfo + .borrow_mut() + .insert((Symbol::intern(var), value.map(Symbol::intern))); } fn track_path(&mut self, path: &str) { - self.sess.file_depinfo.borrow_mut().insert(Symbol::intern(path)); + self.sess().file_depinfo.borrow_mut().insert(Symbol::intern(path)); } } -impl server::TokenStream for Rustc<'_> { +impl server::TokenStream for Rustc<'_, '_> { fn new(&mut self) -> Self::TokenStream { TokenStream::default() } @@ -422,13 +425,62 @@ parse_stream_from_source_str( FileName::proc_macro_source_code(src), src.to_string(), - self.sess, + self.sess(), Some(self.call_site), ) } fn to_string(&mut self, stream: &Self::TokenStream) -> String { pprust::tts_to_string(stream) } + fn expand_expr(&mut self, stream: &Self::TokenStream) -> Result<Self::TokenStream, ()> { + // Parse the expression from our tokenstream. + let expr: PResult<'_, _> = try { + let mut p = rustc_parse::stream_to_parser( + self.sess(), + stream.clone(), + Some("proc_macro expand expr"), + ); + let expr = p.parse_expr()?; + if p.token != token::Eof { + p.unexpected()?; + } + expr + }; + let expr = expr.map_err(|mut err| err.emit())?; + + // Perform eager expansion on the expression. + let expr = self + .ecx + .expander() + .fully_expand_fragment(crate::expand::AstFragment::Expr(expr)) + .make_expr(); + + // NOTE: For now, limit `expand_expr` to exclusively expand to literals. + // This may be relaxed in the future. + // We don't use `nt_to_tokenstream` as the tokenstream currently cannot + // be recovered in the general case. + match &expr.kind { + ast::ExprKind::Lit(l) => { + Ok(tokenstream::TokenTree::token(token::Literal(l.token), l.span).into()) + } + ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind { + ast::ExprKind::Lit(l) => match l.token { + token::Lit { kind: token::Integer | token::Float, .. } => { + Ok(std::array::IntoIter::new([ + // FIXME: The span of the `-` token is lost when + // parsing, so we cannot faithfully recover it here. + tokenstream::TokenTree::token(token::BinOp(token::Minus), e.span), + tokenstream::TokenTree::token(token::Literal(l.token), l.span), + ]) + .collect()) + } + _ => Err(()), + }, + _ => Err(()), + }, + _ => Err(()), + } + } fn from_token_tree( &mut self, tree: TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>, @@ -440,7 +492,7 @@ } } -impl server::TokenStreamBuilder for Rustc<'_> { +impl server::TokenStreamBuilder for Rustc<'_, '_> { fn new(&mut self) -> Self::TokenStreamBuilder { tokenstream::TokenStreamBuilder::new() } @@ -452,7 +504,7 @@ } } -impl server::TokenStreamIter for Rustc<'_> { +impl server::TokenStreamIter for Rustc<'_, '_> { fn next( &mut self, iter: &mut Self::TokenStreamIter, @@ -477,7 +529,7 @@ } } -impl server::Group for Rustc<'_> { +impl server::Group for Rustc<'_, '_> { fn new(&mut self, delimiter: Delimiter, stream: Self::TokenStream) -> Self::Group { Group { delimiter, @@ -506,7 +558,7 @@ } } -impl server::Punct for Rustc<'_> { +impl server::Punct for Rustc<'_, '_> { fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct { Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self)) } @@ -524,9 +576,9 @@ } } -impl server::Ident for Rustc<'_> { +impl server::Ident for Rustc<'_, '_> { fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident { - Ident::new(self.sess, Symbol::intern(string), is_raw, span) + Ident::new(self.sess(), Symbol::intern(string), is_raw, span) } fn span(&mut self, ident: Self::Ident) -> Self::Span { ident.span @@ -536,10 +588,10 @@ } } -impl server::Literal for Rustc<'_> { +impl server::Literal for Rustc<'_, '_> { fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> { let name = FileName::proc_macro_source_code(s); - let mut parser = rustc_parse::new_parser_from_source_str(self.sess, name, s.to_owned()); + let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned()); let first_span = parser.token.span.data(); let minus_present = parser.eat(&token::BinOp(token::Minus)); @@ -675,7 +727,7 @@ } } -impl server::SourceFile for Rustc<'_> { +impl server::SourceFile for Rustc<'_, '_> { fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { Lrc::ptr_eq(file1, file2) } @@ -695,7 +747,7 @@ } } -impl server::MultiSpan for Rustc<'_> { +impl server::MultiSpan for Rustc<'_, '_> { fn new(&mut self) -> Self::MultiSpan { vec![] } @@ -704,7 +756,7 @@ } } -impl server::Diagnostic for Rustc<'_> { +impl server::Diagnostic for Rustc<'_, '_> { fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { let mut diag = Diagnostic::new(level.to_internal(), msg); diag.set_span(MultiSpan::from_spans(spans)); @@ -720,13 +772,13 @@ diag.sub(level.to_internal(), msg, MultiSpan::from_spans(spans), None); } fn emit(&mut self, diag: Self::Diagnostic) { - self.sess.span_diagnostic.emit_diagnostic(&diag); + self.sess().span_diagnostic.emit_diagnostic(&diag); } } -impl server::Span for Rustc<'_> { +impl server::Span for Rustc<'_, '_> { fn debug(&mut self, span: Self::Span) -> String { - if self.span_debug { + if self.ecx.ecfg.span_debug { format!("{:?}", span) } else { format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0) @@ -742,7 +794,7 @@ self.mixed_site } fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { - self.sess.source_map().lookup_char_pos(span.lo()).file + self.sess().source_map().lookup_char_pos(span.lo()).file } fn parent(&mut self, span: Self::Span) -> Option<Self::Span> { span.parent_callsite() @@ -751,11 +803,11 @@ span.source_callsite() } fn start(&mut self, span: Self::Span) -> LineColumn { - let loc = self.sess.source_map().lookup_char_pos(span.lo()); + let loc = self.sess().source_map().lookup_char_pos(span.lo()); LineColumn { line: loc.line, column: loc.col.to_usize() } } fn end(&mut self, span: Self::Span) -> LineColumn { - let loc = self.sess.source_map().lookup_char_pos(span.hi()); + let loc = self.sess().source_map().lookup_char_pos(span.hi()); LineColumn { line: loc.line, column: loc.col.to_usize() } } fn before(&mut self, span: Self::Span) -> Self::Span { @@ -765,8 +817,8 @@ span.shrink_to_hi() } fn join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> { - let self_loc = self.sess.source_map().lookup_char_pos(first.lo()); - let other_loc = self.sess.source_map().lookup_char_pos(second.lo()); + let self_loc = self.sess().source_map().lookup_char_pos(first.lo()); + let other_loc = self.sess().source_map().lookup_char_pos(second.lo()); if self_loc.file.name != other_loc.file.name { return None; @@ -778,7 +830,7 @@ span.with_ctxt(at.ctxt()) } fn source_text(&mut self, span: Self::Span) -> Option<String> { - self.sess.source_map().span_to_snippet(span).ok() + self.sess().source_map().span_to_snippet(span).ok() } /// Saves the provided span into the metadata of /// *the crate we are currently compiling*, which must @@ -805,10 +857,10 @@ /// since we've loaded `my_proc_macro` from disk in order to execute it). /// In this way, we have obtained a span pointing into `my_proc_macro` fn save_span(&mut self, span: Self::Span) -> usize { - self.sess.save_proc_macro_span(span) + self.sess().save_proc_macro_span(span) } fn recover_proc_macro_span(&mut self, id: usize) -> Self::Span { - let (resolver, krate, def_site) = (self.resolver, self.krate, self.def_site); + let (resolver, krate, def_site) = (&*self.ecx.resolver, self.krate, self.def_site); *self.rebased_spans.entry(id).or_insert_with(|| { // FIXME: `SyntaxContext` for spans from proc macro crates is lost during encoding, // replace it with a def-site context until we are encoding it properly. @@ -821,11 +873,11 @@ fn ident_name_compatibility_hack( nt: &Nonterminal, orig_span: Span, - rustc: &mut Rustc<'_>, + rustc: &mut Rustc<'_, '_>, ) -> Option<(rustc_span::symbol::Ident, bool)> { if let NtIdent(ident, is_raw) = nt { if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind { - let source_map = rustc.sess.source_map(); + let source_map = rustc.sess().source_map(); let filename = source_map.span_to_filename(orig_span); if let FileName::Real(RealFileName::LocalPath(path)) = filename { let matches_prefix = |prefix, filename| { @@ -846,7 +898,7 @@ let snippet = source_map.span_to_snippet(orig_span); if snippet.as_deref() == Ok("$name") { if time_macros_impl { - rustc.sess.buffer_lint_with_diagnostic( + rustc.sess().buffer_lint_with_diagnostic( &PROC_MACRO_BACK_COMPAT, orig_span, ast::CRATE_NODE_ID, @@ -871,7 +923,7 @@ .and_then(|c| c.parse::<u32>().ok()) .map_or(false, |v| v < 40) { - rustc.sess.buffer_lint_with_diagnostic( + rustc.sess().buffer_lint_with_diagnostic( &PROC_MACRO_BACK_COMPAT, orig_span, ast::CRATE_NODE_ID, @@ -894,7 +946,7 @@ source_map.span_to_filename(rustc.def_site) { if macro_path.to_string_lossy().contains("pin-project-internal-0.") { - rustc.sess.buffer_lint_with_diagnostic( + rustc.sess().buffer_lint_with_diagnostic( &PROC_MACRO_BACK_COMPAT, orig_span, ast::CRATE_NODE_ID,
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 0d7a2af..6950fae 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs
@@ -34,6 +34,9 @@ /// These are used to test this portion of the compiler, /// they don't actually mean anything. (accepted, test_accepted_feature, "1.0.0", None, None), + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! + // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // ------------------------------------------------------------------------- // feature-group-end: for testing purposes @@ -43,260 +46,269 @@ // feature-group-start: accepted features // ------------------------------------------------------------------------- - /// Allows using associated `type`s in `trait`s. - (accepted, associated_types, "1.0.0", None, None), - /// Allows using assigning a default type to type parameters in algebraic data type definitions. - (accepted, default_type_params, "1.0.0", None, None), - // FIXME: explain `globs`. - (accepted, globs, "1.0.0", None, None), - /// Allows `macro_rules!` items. - (accepted, macro_rules, "1.0.0", None, None), - /// Allows use of `&foo[a..b]` as a slicing syntax. - (accepted, slicing_syntax, "1.0.0", None, None), - /// Allows struct variants `Foo { baz: u8, .. }` in enums (RFC 418). - (accepted, struct_variant, "1.0.0", None, None), - /// Allows indexing tuples. - (accepted, tuple_indexing, "1.0.0", None, None), - /// Allows the use of `if let` expressions. - (accepted, if_let, "1.0.0", None, None), - /// Allows the use of `while let` expressions. - (accepted, while_let, "1.0.0", None, None), - /// Allows using `#![no_std]`. - (accepted, no_std, "1.6.0", None, None), - /// Allows overloading augmented assignment operations like `a += b`. - (accepted, augmented_assignments, "1.8.0", Some(28235), None), - /// Allows empty structs and enum variants with braces. - (accepted, braced_empty_structs, "1.8.0", Some(29720), None), - /// Allows `#[deprecated]` attribute. - (accepted, deprecated, "1.9.0", Some(29935), None), - /// Allows macros to appear in the type position. - (accepted, type_macros, "1.13.0", Some(27245), None), - /// Allows use of the postfix `?` operator in expressions. - (accepted, question_mark, "1.13.0", Some(31436), None), - /// Allows `..` in tuple (struct) patterns. - (accepted, dotdot_in_tuple_patterns, "1.14.0", Some(33627), None), - /// Allows some increased flexibility in the name resolution rules, - /// especially around globs and shadowing (RFC 1560). - (accepted, item_like_imports, "1.15.0", Some(35120), None), - /// Allows using `Self` and associated types in struct expressions and patterns. - (accepted, more_struct_aliases, "1.16.0", Some(37544), None), - /// Allows elision of `'static` lifetimes in `static`s and `const`s. - (accepted, static_in_const, "1.17.0", Some(35897), None), - /// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions. - (accepted, field_init_shorthand, "1.17.0", Some(37340), None), - /// Allows the definition recursive static items. - (accepted, static_recursion, "1.17.0", Some(29719), None), - /// Allows `pub(restricted)` visibilities (RFC 1422). - (accepted, pub_restricted, "1.18.0", Some(32409), None), - /// Allows `#![windows_subsystem]`. - (accepted, windows_subsystem, "1.18.0", Some(37499), None), - /// Allows `break {expr}` with a value inside `loop`s. - (accepted, loop_break_value, "1.19.0", Some(37339), None), - /// Allows numeric fields in struct expressions and patterns. - (accepted, relaxed_adts, "1.19.0", Some(35626), None), - /// Allows coercing non capturing closures to function pointers. - (accepted, closure_to_fn_coercion, "1.19.0", Some(39817), None), - /// Allows attributes on struct literal fields. - (accepted, struct_field_attributes, "1.20.0", Some(38814), None), - /// Allows the definition of associated constants in `trait` or `impl` blocks. - (accepted, associated_consts, "1.20.0", Some(29646), None), - /// Allows usage of the `compile_error!` macro. - (accepted, compile_error, "1.20.0", Some(40872), None), - /// Allows code like `let x: &'static u32 = &42` to work (RFC 1414). - (accepted, rvalue_static_promotion, "1.21.0", Some(38865), None), - /// Allows `Drop` types in constants (RFC 1440). - (accepted, drop_types_in_const, "1.22.0", Some(33156), None), /// Allows the sysV64 ABI to be specified on all platforms /// instead of just the platforms on which it is the C ABI. (accepted, abi_sysv64, "1.24.0", Some(36167), None), - /// Allows `repr(align(16))` struct attribute (RFC 1358). - (accepted, repr_align, "1.25.0", Some(33626), None), - /// Allows '|' at beginning of match arms (RFC 1925). - (accepted, match_beginning_vert, "1.25.0", Some(44101), None), - /// Allows nested groups in `use` items (RFC 2128). - (accepted, use_nested_groups, "1.25.0", Some(44494), None), - /// Allows indexing into constant arrays. - (accepted, const_indexing, "1.26.0", Some(29947), None), - /// Allows using `a..=b` and `..=b` as inclusive range syntaxes. - (accepted, inclusive_range_syntax, "1.26.0", Some(28237), None), - /// Allows `..=` in patterns (RFC 1192). - (accepted, dotdoteq_in_patterns, "1.26.0", Some(28237), None), - /// Allows `fn main()` with return types which implements `Termination` (RFC 1937). - (accepted, termination_trait, "1.26.0", Some(43301), None), - /// Allows implementing `Clone` for closures where possible (RFC 2132). - (accepted, clone_closures, "1.26.0", Some(44490), None), - /// Allows implementing `Copy` for closures where possible (RFC 2132). - (accepted, copy_closures, "1.26.0", Some(44490), None), - /// Allows `impl Trait` in function arguments. - (accepted, universal_impl_trait, "1.26.0", Some(34511), None), - /// Allows `impl Trait` in function return types. - (accepted, conservative_impl_trait, "1.26.0", Some(34511), None), - /// Allows using the `u128` and `i128` types. - (accepted, i128_type, "1.26.0", Some(35118), None), - /// Allows default match binding modes (RFC 2005). - (accepted, match_default_bindings, "1.26.0", Some(42640), None), - /// Allows `'_` placeholder lifetimes. - (accepted, underscore_lifetimes, "1.26.0", Some(44524), None), - /// Allows attributes on lifetime/type formal parameters in generics (RFC 1327). - (accepted, generic_param_attrs, "1.27.0", Some(48848), None), - /// Allows `cfg(target_feature = "...")`. - (accepted, cfg_target_feature, "1.27.0", Some(29717), None), - /// Allows `#[target_feature(...)]`. - (accepted, target_feature, "1.27.0", None, None), - /// Allows using `dyn Trait` as a syntax for trait objects. - (accepted, dyn_trait, "1.27.0", Some(44662), None), - /// Allows `#[must_use]` on functions, and introduces must-use operators (RFC 1940). - (accepted, fn_must_use, "1.27.0", Some(43302), None), - /// Allows use of the `:lifetime` macro fragment specifier. - (accepted, macro_lifetime_matcher, "1.27.0", Some(34303), None), - /// Allows `#[test]` functions where the return type implements `Termination` (RFC 1937). - (accepted, termination_trait_test, "1.27.0", Some(48854), None), - /// Allows the `#[global_allocator]` attribute. - (accepted, global_allocator, "1.28.0", Some(27389), None), - /// Allows `#[repr(transparent)]` attribute on newtype structs. - (accepted, repr_transparent, "1.28.0", Some(43036), None), - /// Allows procedural macros in `proc-macro` crates. - (accepted, proc_macro, "1.29.0", Some(38356), None), - /// Allows `foo.rs` as an alternative to `foo/mod.rs`. - (accepted, non_modrs_mods, "1.30.0", Some(44660), None), - /// Allows use of the `:vis` macro fragment specifier - (accepted, macro_vis_matcher, "1.30.0", Some(41022), None), - /// Allows importing and reexporting macros with `use`, - /// enables macro modularization in general. - (accepted, use_extern_macros, "1.30.0", Some(35896), None), - /// Allows keywords to be escaped for use as identifiers. - (accepted, raw_identifiers, "1.30.0", Some(48589), None), - /// Allows attributes scoped to tools. - (accepted, tool_attributes, "1.30.0", Some(44690), None), - /// Allows multi-segment paths in attributes and derives. - (accepted, proc_macro_path_invoc, "1.30.0", Some(38356), None), - /// Allows all literals in attribute lists and values of key-value pairs. - (accepted, attr_literals, "1.30.0", Some(34981), None), - /// Allows inferring outlives requirements (RFC 2093). - (accepted, infer_outlives_requirements, "1.30.0", Some(44493), None), - /// Allows annotating functions conforming to `fn(&PanicInfo) -> !` with `#[panic_handler]`. - /// This defines the behavior of panics. - (accepted, panic_handler, "1.30.0", Some(44489), None), - /// Allows `#[used]` to preserve symbols (see llvm.compiler.used). - (accepted, used, "1.30.0", Some(40289), None), - /// Allows `crate` in paths. - (accepted, crate_in_paths, "1.30.0", Some(45477), None), - /// Allows resolving absolute paths as paths from other crates. - (accepted, extern_absolute_paths, "1.30.0", Some(44660), None), - /// Allows access to crate names passed via `--extern` through prelude. - (accepted, extern_prelude, "1.30.0", Some(44660), None), - /// Allows parentheses in patterns. - (accepted, pattern_parentheses, "1.31.0", Some(51087), None), - /// Allows the definition of `const fn` functions. - (accepted, min_const_fn, "1.31.0", Some(53555), None), - /// Allows scoped lints. - (accepted, tool_lints, "1.31.0", Some(44690), None), - /// Allows lifetime elision in `impl` headers. For example: - /// + `impl<I:Iterator> Iterator for &mut Iterator` - /// + `impl Debug for Foo<'_>` - (accepted, impl_header_lifetime_elision, "1.31.0", Some(15872), None), - /// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude. - (accepted, extern_crate_item_prelude, "1.31.0", Some(55599), None), - /// Allows use of the `:literal` macro fragment specifier (RFC 1576). - (accepted, macro_literal_matcher, "1.32.0", Some(35625), None), - /// Allows use of `?` as the Kleene "at most one" operator in macros. - (accepted, macro_at_most_once_rep, "1.32.0", Some(48075), None), - /// Allows `Self` struct constructor (RFC 2302). - (accepted, self_struct_ctor, "1.32.0", Some(51994), None), - /// Allows `Self` in type definitions (RFC 2300). - (accepted, self_in_typedefs, "1.32.0", Some(49303), None), - /// Allows `use x::y;` to search `x` in the current scope. - (accepted, uniform_paths, "1.32.0", Some(53130), None), - /// Allows integer match exhaustiveness checking (RFC 2591). - (accepted, exhaustive_integer_patterns, "1.33.0", Some(50907), None), - /// Allows `use path as _;` and `extern crate c as _;`. - (accepted, underscore_imports, "1.33.0", Some(48216), None), - /// Allows `#[repr(packed(N))]` attribute on structs. - (accepted, repr_packed, "1.33.0", Some(33158), None), - /// Allows irrefutable patterns in `if let` and `while let` statements (RFC 2086). - (accepted, irrefutable_let_patterns, "1.33.0", Some(44495), None), - /// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions. - (accepted, min_const_unsafe_fn, "1.33.0", Some(55607), None), - /// Allows let bindings, assignments and destructuring in `const` functions and constants. - /// As long as control flow is not implemented in const eval, `&&` and `||` may not be used - /// at the same time as let bindings. - (accepted, const_let, "1.33.0", Some(48821), None), - /// Allows `#[cfg_attr(predicate, multiple, attributes, here)]`. - (accepted, cfg_attr_multi, "1.33.0", Some(54881), None), - /// Allows top level or-patterns (`p | q`) in `if let` and `while let`. - (accepted, if_while_or_patterns, "1.33.0", Some(48215), None), - /// Allows `cfg(target_vendor = "...")`. - (accepted, cfg_target_vendor, "1.33.0", Some(29718), None), - /// Allows `extern crate self as foo;`. - /// This puts local crate root into extern prelude under name `foo`. - (accepted, extern_crate_self, "1.34.0", Some(56409), None), - /// Allows arbitrary delimited token streams in non-macro attributes. - (accepted, unrestricted_attribute_tokens, "1.34.0", Some(55208), None), - /// Allows paths to enum variants on type aliases including `Self`. - (accepted, type_alias_enum_variants, "1.37.0", Some(49683), None), - /// Allows using `#[repr(align(X))]` on enums with equivalent semantics - /// to wrapping an enum in a wrapper struct with `#[repr(align(X))]`. - (accepted, repr_align_enum, "1.37.0", Some(57996), None), - /// Allows `const _: TYPE = VALUE`. - (accepted, underscore_const_names, "1.37.0", Some(54912), None), + /// Allows the definition of associated constants in `trait` or `impl` blocks. + (accepted, associated_consts, "1.20.0", Some(29646), None), + /// Allows using associated `type`s in `trait`s. + (accepted, associated_types, "1.0.0", None, None), /// Allows free and inherent `async fn`s, `async` blocks, and `<expr>.await` expressions. (accepted, async_await, "1.39.0", Some(50547), None), + /// Allows all literals in attribute lists and values of key-value pairs. + (accepted, attr_literals, "1.30.0", Some(34981), None), + /// Allows overloading augmented assignment operations like `a += b`. + (accepted, augmented_assignments, "1.8.0", Some(28235), None), /// Allows mixing bind-by-move in patterns and references to those identifiers in guards. (accepted, bind_by_move_pattern_guards, "1.39.0", Some(15287), None), - /// Allows attributes in formal function parameters. - (accepted, param_attrs, "1.39.0", Some(60406), None), - /// Allows macro invocations in `extern {}` blocks. - (accepted, macros_in_extern, "1.40.0", Some(49476), None), - /// Allows future-proofing enums/structs with the `#[non_exhaustive]` attribute (RFC 2008). - (accepted, non_exhaustive, "1.40.0", Some(44109), None), - /// Allows calling constructor functions in `const fn`. - (accepted, const_constructor, "1.40.0", Some(61456), None), - /// Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests. - (accepted, cfg_doctest, "1.40.0", Some(62210), None), - /// Allows relaxing the coherence rules such that - /// `impl<T> ForeignTrait<LocalType> for ForeignType<T>` is permitted. - (accepted, re_rebalance_coherence, "1.41.0", Some(55437), None), - /// Allows #[repr(transparent)] on univariant enums (RFC 2645). - (accepted, transparent_enums, "1.42.0", Some(60405), None), - /// Allows using subslice patterns, `[a, .., b]` and `[a, xs @ .., b]`. - (accepted, slice_patterns, "1.42.0", Some(62254), None), - /// Allows the use of `if` and `match` in constants. - (accepted, const_if_match, "1.46.0", Some(49146), None), - /// Allows the use of `loop` and `while` in constants. - (accepted, const_loop, "1.46.0", Some(52000), None), - /// Allows `#[track_caller]` to be used which provides - /// accurate caller location reporting during panic (RFC 2091). - (accepted, track_caller, "1.46.0", Some(47809), None), - /// Allows `#[doc(alias = "...")]`. - (accepted, doc_alias, "1.48.0", Some(50146), None), - /// Allows patterns with concurrent by-move and by-ref bindings. - /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref. - (accepted, move_ref_pattern, "1.49.0", Some(68354), None), - /// The smallest useful subset of const generics. - (accepted, min_const_generics, "1.51.0", Some(74878), None), - /// The `unsafe_op_in_unsafe_fn` lint (allowed by default): no longer treat an unsafe function as an unsafe block. - (accepted, unsafe_block_in_unsafe_fn, "1.52.0", Some(71668), None), - /// Allows the use of or-patterns (e.g., `0 | 1`). - (accepted, or_patterns, "1.53.0", Some(54883), None), - /// Allows defining identifiers beyond ASCII. - (accepted, non_ascii_idents, "1.53.0", Some(55467), None), - /// Allows arbitrary expressions in key-value attributes at parse time. - (accepted, extended_key_value_attributes, "1.54.0", Some(78835), None), - /// Allows unsizing coercions in `const fn`. - (accepted, const_fn_unsize, "1.54.0", Some(64992), None), - /// Allows `impl Trait` with multiple unrelated lifetimes. - (accepted, member_constraints, "1.54.0", Some(61997), None), /// Allows bindings in the subpattern of a binding pattern. /// For example, you can write `x @ Some(y)`. (accepted, bindings_after_at, "1.56.0", Some(65490), None), + /// Allows empty structs and enum variants with braces. + (accepted, braced_empty_structs, "1.8.0", Some(29720), None), + /// Allows `#[cfg_attr(predicate, multiple, attributes, here)]`. + (accepted, cfg_attr_multi, "1.33.0", Some(54881), None), + /// Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests. + (accepted, cfg_doctest, "1.40.0", Some(62210), None), + /// Allows `cfg(target_feature = "...")`. + (accepted, cfg_target_feature, "1.27.0", Some(29717), None), + /// Allows `cfg(target_vendor = "...")`. + (accepted, cfg_target_vendor, "1.33.0", Some(29718), None), + /// Allows implementing `Clone` for closures where possible (RFC 2132). + (accepted, clone_closures, "1.26.0", Some(44490), None), + /// Allows coercing non capturing closures to function pointers. + (accepted, closure_to_fn_coercion, "1.19.0", Some(39817), None), + /// Allows usage of the `compile_error!` macro. + (accepted, compile_error, "1.20.0", Some(40872), None), + /// Allows `impl Trait` in function return types. + (accepted, conservative_impl_trait, "1.26.0", Some(34511), None), + /// Allows calling constructor functions in `const fn`. + (accepted, const_constructor, "1.40.0", Some(61456), None), /// Allows calling `transmute` in const fn (accepted, const_fn_transmute, "1.56.0", Some(53605), None), /// Allows accessing fields of unions inside `const` functions. (accepted, const_fn_union, "1.56.0", Some(51909), None), - /// Allows macro attributes to observe output of `#[derive]`. - (accepted, macro_attributes_in_derive_output, "1.57.0", Some(81119), None), + /// Allows unsizing coercions in `const fn`. + (accepted, const_fn_unsize, "1.54.0", Some(64992), None), + /// Allows the use of `if` and `match` in constants. + (accepted, const_if_match, "1.46.0", Some(49146), None), + /// Allows indexing into constant arrays. + (accepted, const_indexing, "1.26.0", Some(29947), None), + /// Allows let bindings, assignments and destructuring in `const` functions and constants. + /// As long as control flow is not implemented in const eval, `&&` and `||` may not be used + /// at the same time as let bindings. + (accepted, const_let, "1.33.0", Some(48821), None), + /// Allows the use of `loop` and `while` in constants. + (accepted, const_loop, "1.46.0", Some(52000), None), /// Allows panicking during const eval (producing compile-time errors). (accepted, const_panic, "1.57.0", Some(51999), None), + /// Allows dereferencing raw pointers during const eval. + (accepted, const_raw_ptr_deref, "1.58.0", Some(51911), None), + /// Allows implementing `Copy` for closures where possible (RFC 2132). + (accepted, copy_closures, "1.26.0", Some(44490), None), + /// Allows `crate` in paths. + (accepted, crate_in_paths, "1.30.0", Some(45477), None), + /// Allows using assigning a default type to type parameters in algebraic data type definitions. + (accepted, default_type_params, "1.0.0", None, None), + /// Allows `#[deprecated]` attribute. + (accepted, deprecated, "1.9.0", Some(29935), None), + /// Allows `#[doc(alias = "...")]`. + (accepted, doc_alias, "1.48.0", Some(50146), None), + /// Allows `..` in tuple (struct) patterns. + (accepted, dotdot_in_tuple_patterns, "1.14.0", Some(33627), None), + /// Allows `..=` in patterns (RFC 1192). + (accepted, dotdoteq_in_patterns, "1.26.0", Some(28237), None), + /// Allows `Drop` types in constants (RFC 1440). + (accepted, drop_types_in_const, "1.22.0", Some(33156), None), + /// Allows using `dyn Trait` as a syntax for trait objects. + (accepted, dyn_trait, "1.27.0", Some(44662), None), + /// Allows integer match exhaustiveness checking (RFC 2591). + (accepted, exhaustive_integer_patterns, "1.33.0", Some(50907), None), + /// Allows arbitrary expressions in key-value attributes at parse time. + (accepted, extended_key_value_attributes, "1.54.0", Some(78835), None), + /// Allows resolving absolute paths as paths from other crates. + (accepted, extern_absolute_paths, "1.30.0", Some(44660), None), + /// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude. + (accepted, extern_crate_item_prelude, "1.31.0", Some(55599), None), + /// Allows `extern crate self as foo;`. + /// This puts local crate root into extern prelude under name `foo`. + (accepted, extern_crate_self, "1.34.0", Some(56409), None), + /// Allows access to crate names passed via `--extern` through prelude. + (accepted, extern_prelude, "1.30.0", Some(44660), None), + /// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions. + (accepted, field_init_shorthand, "1.17.0", Some(37340), None), + /// Allows `#[must_use]` on functions, and introduces must-use operators (RFC 1940). + (accepted, fn_must_use, "1.27.0", Some(43302), None), + /// Allows capturing variables in scope using format_args! + (accepted, format_args_capture, "1.58.0", Some(67984), None), + /// Allows attributes on lifetime/type formal parameters in generics (RFC 1327). + (accepted, generic_param_attrs, "1.27.0", Some(48848), None), + /// Allows the `#[global_allocator]` attribute. + (accepted, global_allocator, "1.28.0", Some(27389), None), + // FIXME: explain `globs`. + (accepted, globs, "1.0.0", None, None), + /// Allows using the `u128` and `i128` types. + (accepted, i128_type, "1.26.0", Some(35118), None), + /// Allows the use of `if let` expressions. + (accepted, if_let, "1.0.0", None, None), + /// Allows top level or-patterns (`p | q`) in `if let` and `while let`. + (accepted, if_while_or_patterns, "1.33.0", Some(48215), None), + /// Allows lifetime elision in `impl` headers. For example: + /// + `impl<I:Iterator> Iterator for &mut Iterator` + /// + `impl Debug for Foo<'_>` + (accepted, impl_header_lifetime_elision, "1.31.0", Some(15872), None), + /// Allows using `a..=b` and `..=b` as inclusive range syntaxes. + (accepted, inclusive_range_syntax, "1.26.0", Some(28237), None), + /// Allows inferring outlives requirements (RFC 2093). + (accepted, infer_outlives_requirements, "1.30.0", Some(44493), None), + /// Allows irrefutable patterns in `if let` and `while let` statements (RFC 2086). + (accepted, irrefutable_let_patterns, "1.33.0", Some(44495), None), + /// Allows some increased flexibility in the name resolution rules, + /// especially around globs and shadowing (RFC 1560). + (accepted, item_like_imports, "1.15.0", Some(35120), None), + /// Allows `break {expr}` with a value inside `loop`s. + (accepted, loop_break_value, "1.19.0", Some(37339), None), + /// Allows use of `?` as the Kleene "at most one" operator in macros. + (accepted, macro_at_most_once_rep, "1.32.0", Some(48075), None), + /// Allows macro attributes to observe output of `#[derive]`. + (accepted, macro_attributes_in_derive_output, "1.57.0", Some(81119), None), + /// Allows use of the `:lifetime` macro fragment specifier. + (accepted, macro_lifetime_matcher, "1.27.0", Some(34303), None), + /// Allows use of the `:literal` macro fragment specifier (RFC 1576). + (accepted, macro_literal_matcher, "1.32.0", Some(35625), None), + /// Allows `macro_rules!` items. + (accepted, macro_rules, "1.0.0", None, None), + /// Allows use of the `:vis` macro fragment specifier + (accepted, macro_vis_matcher, "1.30.0", Some(41022), None), + /// Allows macro invocations in `extern {}` blocks. + (accepted, macros_in_extern, "1.40.0", Some(49476), None), + /// Allows '|' at beginning of match arms (RFC 1925). + (accepted, match_beginning_vert, "1.25.0", Some(44101), None), + /// Allows default match binding modes (RFC 2005). + (accepted, match_default_bindings, "1.26.0", Some(42640), None), + /// Allows `impl Trait` with multiple unrelated lifetimes. + (accepted, member_constraints, "1.54.0", Some(61997), None), + /// Allows the definition of `const fn` functions. + (accepted, min_const_fn, "1.31.0", Some(53555), None), + /// The smallest useful subset of const generics. + (accepted, min_const_generics, "1.51.0", Some(74878), None), + /// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions. + (accepted, min_const_unsafe_fn, "1.33.0", Some(55607), None), + /// Allows using `Self` and associated types in struct expressions and patterns. + (accepted, more_struct_aliases, "1.16.0", Some(37544), None), + /// Allows patterns with concurrent by-move and by-ref bindings. + /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref. + (accepted, move_ref_pattern, "1.49.0", Some(68354), None), + /// Allows using `#![no_std]`. + (accepted, no_std, "1.6.0", None, None), + /// Allows defining identifiers beyond ASCII. + (accepted, non_ascii_idents, "1.53.0", Some(55467), None), + /// Allows future-proofing enums/structs with the `#[non_exhaustive]` attribute (RFC 2008). + (accepted, non_exhaustive, "1.40.0", Some(44109), None), + /// Allows `foo.rs` as an alternative to `foo/mod.rs`. + (accepted, non_modrs_mods, "1.30.0", Some(44660), None), + /// Allows the use of or-patterns (e.g., `0 | 1`). + (accepted, or_patterns, "1.53.0", Some(54883), None), + /// Allows annotating functions conforming to `fn(&PanicInfo) -> !` with `#[panic_handler]`. + /// This defines the behavior of panics. + (accepted, panic_handler, "1.30.0", Some(44489), None), + /// Allows attributes in formal function parameters. + (accepted, param_attrs, "1.39.0", Some(60406), None), + /// Allows parentheses in patterns. + (accepted, pattern_parentheses, "1.31.0", Some(51087), None), + /// Allows procedural macros in `proc-macro` crates. + (accepted, proc_macro, "1.29.0", Some(38356), None), + /// Allows multi-segment paths in attributes and derives. + (accepted, proc_macro_path_invoc, "1.30.0", Some(38356), None), + /// Allows `pub(restricted)` visibilities (RFC 1422). + (accepted, pub_restricted, "1.18.0", Some(32409), None), + /// Allows use of the postfix `?` operator in expressions. + (accepted, question_mark, "1.13.0", Some(31436), None), + /// Allows keywords to be escaped for use as identifiers. + (accepted, raw_identifiers, "1.30.0", Some(48589), None), + /// Allows relaxing the coherence rules such that + /// `impl<T> ForeignTrait<LocalType> for ForeignType<T>` is permitted. + (accepted, re_rebalance_coherence, "1.41.0", Some(55437), None), + /// Allows numeric fields in struct expressions and patterns. + (accepted, relaxed_adts, "1.19.0", Some(35626), None), + /// Lessens the requirements for structs to implement `Unsize`. + (accepted, relaxed_struct_unsize, "1.58.0", Some(81793), None), + /// Allows `repr(align(16))` struct attribute (RFC 1358). + (accepted, repr_align, "1.25.0", Some(33626), None), + /// Allows using `#[repr(align(X))]` on enums with equivalent semantics + /// to wrapping an enum in a wrapper struct with `#[repr(align(X))]`. + (accepted, repr_align_enum, "1.37.0", Some(57996), None), + /// Allows `#[repr(packed(N))]` attribute on structs. + (accepted, repr_packed, "1.33.0", Some(33158), None), + /// Allows `#[repr(transparent)]` attribute on newtype structs. + (accepted, repr_transparent, "1.28.0", Some(43036), None), + /// Allows code like `let x: &'static u32 = &42` to work (RFC 1414). + (accepted, rvalue_static_promotion, "1.21.0", Some(38865), None), + /// Allows `Self` in type definitions (RFC 2300). + (accepted, self_in_typedefs, "1.32.0", Some(49303), None), + /// Allows `Self` struct constructor (RFC 2302). + (accepted, self_struct_ctor, "1.32.0", Some(51994), None), + /// Allows using subslice patterns, `[a, .., b]` and `[a, xs @ .., b]`. + (accepted, slice_patterns, "1.42.0", Some(62254), None), + /// Allows use of `&foo[a..b]` as a slicing syntax. + (accepted, slicing_syntax, "1.0.0", None, None), + /// Allows elision of `'static` lifetimes in `static`s and `const`s. + (accepted, static_in_const, "1.17.0", Some(35897), None), + /// Allows the definition recursive static items. + (accepted, static_recursion, "1.17.0", Some(29719), None), + /// Allows attributes on struct literal fields. + (accepted, struct_field_attributes, "1.20.0", Some(38814), None), + /// Allows struct variants `Foo { baz: u8, .. }` in enums (RFC 418). + (accepted, struct_variant, "1.0.0", None, None), + /// Allows `#[target_feature(...)]`. + (accepted, target_feature, "1.27.0", None, None), + /// Allows `fn main()` with return types which implements `Termination` (RFC 1937). + (accepted, termination_trait, "1.26.0", Some(43301), None), + /// Allows `#[test]` functions where the return type implements `Termination` (RFC 1937). + (accepted, termination_trait_test, "1.27.0", Some(48854), None), + /// Allows attributes scoped to tools. + (accepted, tool_attributes, "1.30.0", Some(44690), None), + /// Allows scoped lints. + (accepted, tool_lints, "1.31.0", Some(44690), None), + /// Allows `#[track_caller]` to be used which provides + /// accurate caller location reporting during panic (RFC 2091). + (accepted, track_caller, "1.46.0", Some(47809), None), + /// Allows #[repr(transparent)] on univariant enums (RFC 2645). + (accepted, transparent_enums, "1.42.0", Some(60405), None), + /// Allows indexing tuples. + (accepted, tuple_indexing, "1.0.0", None, None), + /// Allows paths to enum variants on type aliases including `Self`. + (accepted, type_alias_enum_variants, "1.37.0", Some(49683), None), + /// Allows macros to appear in the type position. + (accepted, type_macros, "1.13.0", Some(27245), None), + /// Allows `const _: TYPE = VALUE`. + (accepted, underscore_const_names, "1.37.0", Some(54912), None), + /// Allows `use path as _;` and `extern crate c as _;`. + (accepted, underscore_imports, "1.33.0", Some(48216), None), + /// Allows `'_` placeholder lifetimes. + (accepted, underscore_lifetimes, "1.26.0", Some(44524), None), + /// Allows `use x::y;` to search `x` in the current scope. + (accepted, uniform_paths, "1.32.0", Some(53130), None), + /// Allows `impl Trait` in function arguments. + (accepted, universal_impl_trait, "1.26.0", Some(34511), None), + /// Allows arbitrary delimited token streams in non-macro attributes. + (accepted, unrestricted_attribute_tokens, "1.34.0", Some(55208), None), + /// The `unsafe_op_in_unsafe_fn` lint (allowed by default): no longer treat an unsafe function as an unsafe block. + (accepted, unsafe_block_in_unsafe_fn, "1.52.0", Some(71668), None), + /// Allows importing and reexporting macros with `use`, + /// enables macro modularization in general. + (accepted, use_extern_macros, "1.30.0", Some(35896), None), + /// Allows nested groups in `use` items (RFC 2128). + (accepted, use_nested_groups, "1.25.0", Some(44494), None), + /// Allows `#[used]` to preserve symbols (see llvm.compiler.used). + (accepted, used, "1.30.0", Some(40289), None), + /// Allows the use of `while let` expressions. + (accepted, while_let, "1.0.0", None, None), + /// Allows `#![windows_subsystem]`. + (accepted, windows_subsystem, "1.18.0", Some(37499), None), + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! + // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // ------------------------------------------------------------------------- // feature-group-end: accepted features
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 0ba7640..7860f92 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs
@@ -101,9 +101,13 @@ } } +// See https://rustc-dev-guide.rust-lang.org/feature-gates.html#feature-gates for more +// documentation about handling feature gates. +// // If you change this, please modify `src/doc/unstable-book` as well. // -// Don't ever remove anything from this list; move them to `removed.rs`. +// Don't ever remove anything from this list; move them to `accepted.rs` if +// accepted or `removed.rs` if removed. // // The version numbers here correspond to the version in which the current status // was set. This is most important for knowing when a particular feature became @@ -118,130 +122,103 @@ #[rustfmt::skip] declare_features! ( // ------------------------------------------------------------------------- - // feature-group-start: internal feature gates + // feature-group-start: internal feature gates (no tracking issue) // ------------------------------------------------------------------------- - // no-tracking-issue-start - /// Allows using `rustc_*` attributes (RFC 572). - (active, rustc_attrs, "1.0.0", None, None), - - /// Allows using compiler's own crates. - (active, rustc_private, "1.0.0", Some(27812), None), - - /// Allows using the `rust-intrinsic`'s "ABI". - (active, intrinsics, "1.0.0", None, None), - - /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. - (active, lang_items, "1.0.0", None, None), - - /// Allows using the `#[stable]` and `#[unstable]` attributes. - (active, staged_api, "1.0.0", None, None), - - /// Allows using `#[allow_internal_unstable]`. This is an - /// attribute on `macro_rules!` and can't use the attribute handling - /// below (it has to be checked before expansion possibly makes - /// macros disappear). - (active, allow_internal_unstable, "1.0.0", None, None), - + /// Allows using the `thiscall` ABI. + (active, abi_thiscall, "1.19.0", None, None), + /// Allows using the `unadjusted` ABI; perma-unstable. + (active, abi_unadjusted, "1.16.0", None, None), + /// Allows using the `vectorcall` ABI. + (active, abi_vectorcall, "1.7.0", None, None), + /// Allows using `#![needs_allocator]`, an implementation detail of `#[global_allocator]`. + (active, allocator_internals, "1.20.0", None, None), /// Allows using `#[allow_internal_unsafe]`. This is an /// attribute on `macro_rules!` and can't use the attribute handling /// below (it has to be checked before expansion possibly makes /// macros disappear). (active, allow_internal_unsafe, "1.0.0", None, None), - - /// no-tracking-issue-end - - /// Allows using `#[link_name="llvm.*"]`. - (active, link_llvm_intrinsics, "1.0.0", Some(29602), None), - - /// Allows using the `box $expr` syntax. - (active, box_syntax, "1.0.0", Some(49733), None), - - /// Allows using `#[start]` on a function indicating that it is the program entrypoint. - (active, start, "1.0.0", Some(29633), None), - - /// Allows using the `#[fundamental]` attribute. - (active, fundamental, "1.0.0", Some(29635), None), - - /// Allows using the `rust-call` ABI. - (active, unboxed_closures, "1.0.0", Some(29625), None), - - /// Allows using the `#[linkage = ".."]` attribute. - (active, linkage, "1.0.0", Some(29603), None), - - /// Allows using `box` in patterns (RFC 469). - (active, box_patterns, "1.0.0", Some(29641), None), - - // no-tracking-issue-start - - /// Allows using `#[prelude_import]` on glob `use` items. - (active, prelude_import, "1.2.0", None, None), - - // no-tracking-issue-end - - // no-tracking-issue-start - - /// Allows using `#[omit_gdb_pretty_printer_section]`. - (active, omit_gdb_pretty_printer_section, "1.5.0", None, None), - - /// Allows using the `vectorcall` ABI. - (active, abi_vectorcall, "1.7.0", None, None), - - // no-tracking-issue-end - - /// Allows using `#[structural_match]` which indicates that a type is structurally matchable. - /// FIXME: Subsumed by trait `StructuralPartialEq`, cannot move to removed until a library - /// feature with the same name exists. - (active, structural_match, "1.8.0", Some(31434), None), - - /// Allows using the `may_dangle` attribute (RFC 1327). - (active, dropck_eyepatch, "1.10.0", Some(34761), None), - - /// Allows using the `#![panic_runtime]` attribute. - (active, panic_runtime, "1.10.0", Some(32837), None), - - /// Allows declaring with `#![needs_panic_runtime]` that a panic runtime is needed. - (active, needs_panic_runtime, "1.10.0", Some(32837), None), - - // no-tracking-issue-start - + /// Allows using `#[allow_internal_unstable]`. This is an + /// attribute on `macro_rules!` and can't use the attribute handling + /// below (it has to be checked before expansion possibly makes + /// macros disappear). + (active, allow_internal_unstable, "1.0.0", None, None), /// Allows identifying the `compiler_builtins` crate. (active, compiler_builtins, "1.13.0", None, None), - - /// Allows using the `unadjusted` ABI; perma-unstable. - (active, abi_unadjusted, "1.16.0", None, None), - - /// Used to identify crates that contain the profiler runtime. - (active, profiler_runtime, "1.18.0", None, None), - - /// Allows using the `thiscall` ABI. - (active, abi_thiscall, "1.19.0", None, None), - - /// Allows using `#![needs_allocator]`, an implementation detail of `#[global_allocator]`. - (active, allocator_internals, "1.20.0", None, None), - - /// Added for testing E0705; perma-unstable. - (active, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)), - + /// Allows using the `rust-intrinsic`'s "ABI". + (active, intrinsics, "1.0.0", None, None), + /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. + (active, lang_items, "1.0.0", None, None), /// Allows `#[repr(no_niche)]` (an implementation detail of `rustc`, /// it is not on path for eventual stabilization). (active, no_niche, "1.42.0", None, None), + /// Allows using `#[omit_gdb_pretty_printer_section]`. + (active, omit_gdb_pretty_printer_section, "1.5.0", None, None), + /// Allows using `#[prelude_import]` on glob `use` items. + (active, prelude_import, "1.2.0", None, None), + /// Used to identify crates that contain the profiler runtime. + (active, profiler_runtime, "1.18.0", None, None), + /// Allows using `rustc_*` attributes (RFC 572). + (active, rustc_attrs, "1.0.0", None, None), + /// Allows using the `#[stable]` and `#[unstable]` attributes. + (active, staged_api, "1.0.0", None, None), + /// Added for testing E0705; perma-unstable. + (active, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)), + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! + // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! - /// Allows using `#[rustc_allow_const_fn_unstable]`. - /// This is an attribute on `const fn` for the same - /// purpose as `#[allow_internal_unstable]`. - (active, rustc_allow_const_fn_unstable, "1.49.0", Some(69399), None), + // no-tracking-issue-end + // ------------------------------------------------------------------------- + // feature-group-end: internal feature gates (no tracking issue) + // ------------------------------------------------------------------------- + + // ------------------------------------------------------------------------- + // feature-group-start: internal feature gates + // ------------------------------------------------------------------------- /// Allows features specific to auto traits. /// Renamed from `optin_builtin_traits`. (active, auto_traits, "1.50.0", Some(13231), None), - + /// Allows using `box` in patterns (RFC 469). + (active, box_patterns, "1.0.0", Some(29641), None), + /// Allows using the `box $expr` syntax. + (active, box_syntax, "1.0.0", Some(49733), None), /// Allows `#[doc(notable_trait)]`. /// Renamed from `doc_spotlight`. (active, doc_notable_trait, "1.52.0", Some(45040), None), - - // no-tracking-issue-end + /// Allows using the `may_dangle` attribute (RFC 1327). + (active, dropck_eyepatch, "1.10.0", Some(34761), None), + /// Allows using the `#[fundamental]` attribute. + (active, fundamental, "1.0.0", Some(29635), None), + /// Allows using `#[link_name="llvm.*"]`. + (active, link_llvm_intrinsics, "1.0.0", Some(29602), None), + /// Allows using the `#[linkage = ".."]` attribute. + (active, linkage, "1.0.0", Some(29603), None), + /// Allows declaring with `#![needs_panic_runtime]` that a panic runtime is needed. + (active, needs_panic_runtime, "1.10.0", Some(32837), None), + /// Allows using the `#![panic_runtime]` attribute. + (active, panic_runtime, "1.10.0", Some(32837), None), + /// Allows using `#[rustc_allow_const_fn_unstable]`. + /// This is an attribute on `const fn` for the same + /// purpose as `#[allow_internal_unstable]`. + (active, rustc_allow_const_fn_unstable, "1.49.0", Some(69399), None), + /// Allows using compiler's own crates. + (active, rustc_private, "1.0.0", Some(27812), None), + /// Allows using internal rustdoc features like `doc(primitive)` or `doc(keyword)`. + (active, rustdoc_internals, "1.58.0", Some(90418), None), + /// Allows using `#[start]` on a function indicating that it is the program entrypoint. + (active, start, "1.0.0", Some(29633), None), + /// Allows using `#[structural_match]` which indicates that a type is structurally matchable. + /// FIXME: Subsumed by trait `StructuralPartialEq`, cannot move to removed until a library + /// feature with the same name exists. + (active, structural_match, "1.8.0", Some(31434), None), + /// Allows using the `rust-call` ABI. + (active, unboxed_closures, "1.0.0", Some(29625), None), + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! + // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // ------------------------------------------------------------------------- // feature-group-end: internal feature gates @@ -254,23 +231,26 @@ // FIXME: Document these and merge with the list below. // Unstable `#[target_feature]` directives. - (active, arm_target_feature, "1.27.0", Some(44839), None), (active, aarch64_target_feature, "1.27.0", Some(44839), None), - (active, hexagon_target_feature, "1.27.0", Some(44839), None), - (active, powerpc_target_feature, "1.27.0", Some(44839), None), - (active, mips_target_feature, "1.27.0", Some(44839), None), + (active, adx_target_feature, "1.32.0", Some(44839), None), + (active, arm_target_feature, "1.27.0", Some(44839), None), (active, avx512_target_feature, "1.27.0", Some(44839), None), + (active, bpf_target_feature, "1.54.0", Some(44839), None), + (active, cmpxchg16b_target_feature, "1.32.0", Some(44839), None), + (active, ermsb_target_feature, "1.49.0", Some(44839), None), + (active, f16c_target_feature, "1.36.0", Some(44839), None), + (active, hexagon_target_feature, "1.27.0", Some(44839), None), + (active, mips_target_feature, "1.27.0", Some(44839), None), + (active, movbe_target_feature, "1.34.0", Some(44839), None), + (active, powerpc_target_feature, "1.27.0", Some(44839), None), + (active, riscv_target_feature, "1.45.0", Some(44839), None), + (active, rtm_target_feature, "1.35.0", Some(44839), None), (active, sse4a_target_feature, "1.27.0", Some(44839), None), (active, tbm_target_feature, "1.27.0", Some(44839), None), (active, wasm_target_feature, "1.30.0", Some(44839), None), - (active, adx_target_feature, "1.32.0", Some(44839), None), - (active, cmpxchg16b_target_feature, "1.32.0", Some(44839), None), - (active, movbe_target_feature, "1.34.0", Some(44839), None), - (active, rtm_target_feature, "1.35.0", Some(44839), None), - (active, f16c_target_feature, "1.36.0", Some(44839), None), - (active, riscv_target_feature, "1.45.0", Some(44839), None), - (active, ermsb_target_feature, "1.49.0", Some(44839), None), - (active, bpf_target_feature, "1.54.0", Some(44839), None), + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! + // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // ------------------------------------------------------------------------- // feature-group-end: actual feature gates (target features) @@ -280,65 +260,273 @@ // feature-group-start: actual feature gates // ------------------------------------------------------------------------- - /// Allows using `#![plugin(myplugin)]`. - (active, plugin, "1.0.0", Some(29597), None), - - /// Allows using `#[thread_local]` on `static` items. - (active, thread_local, "1.0.0", Some(29594), None), - - /// Allows the use of SIMD types in functions declared in `extern` blocks. - (active, simd_ffi, "1.0.0", Some(27731), None), - - /// Allows using non lexical lifetimes (RFC 2094). - (active, nll, "1.0.0", Some(43234), None), - + /// Allows using the `amdgpu-kernel` ABI. + (active, abi_amdgpu_kernel, "1.29.0", Some(51575), None), + /// Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`. + (active, abi_avr_interrupt, "1.45.0", Some(69664), None), + /// Allows `extern "C-cmse-nonsecure-call" fn()`. + (active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None), + /// Allows using the `efiapi` ABI. + (active, abi_efiapi, "1.40.0", Some(65815), None), + /// Allows `extern "msp430-interrupt" fn()`. + (active, abi_msp430_interrupt, "1.16.0", Some(38487), None), + /// Allows `extern "ptx-*" fn()`. + (active, abi_ptx, "1.15.0", Some(38788), None), + /// Allows `extern "x86-interrupt" fn()`. + (active, abi_x86_interrupt, "1.17.0", Some(40180), None), + /// Allows additional const parameter types, such as `&'static str` or user defined types + (incomplete, adt_const_params, "1.56.0", Some(44580), None), + /// Allows defining an `#[alloc_error_handler]`. + (active, alloc_error_handler, "1.29.0", Some(51540), None), + /// Allows a test to fail without failing the whole suite. + (active, allow_fail, "1.19.0", Some(46488), None), + /// Allows explicit discriminants on non-unit enum variants. + (active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None), + /// Allows trait methods with arbitrary self types. + (active, arbitrary_self_types, "1.23.0", Some(44874), None), + /// Allows using `const` operands in inline assembly. + (active, asm_const, "1.58.0", Some(72016), None), + /// Enables experimental inline assembly support for additional architectures. + (active, asm_experimental_arch, "1.58.0", Some(72016), None), + /// Allows using `sym` operands in inline assembly. + (active, asm_sym, "1.58.0", Some(72016), None), + /// Allows the user of associated type bounds. + (active, associated_type_bounds, "1.34.0", Some(52662), None), /// Allows associated type defaults. (active, associated_type_defaults, "1.2.0", Some(29661), None), - - /// Allows `#![no_core]`. - (active, no_core, "1.3.0", Some(29639), None), - - /// Allows default type parameters to influence type inference. - (active, default_type_parameter_fallback, "1.3.0", Some(27336), None), - - /// Allows `repr(simd)` and importing the various simd intrinsics. - (active, repr_simd, "1.4.0", Some(27731), None), - - /// Allows `extern "platform-intrinsic" { ... }`. - (active, platform_intrinsics, "1.4.0", Some(27731), None), - - /// Allows attributes on expressions and non-item statements. - (active, stmt_expr_attributes, "1.6.0", Some(15701), None), - - /// Allows the use of type ascription in expressions. - (active, type_ascription, "1.6.0", Some(23416), None), - + /// Allows `async || body` closures. + (active, async_closure, "1.37.0", Some(62290), None), + /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries. + (active, c_unwind, "1.52.0", Some(74990), None), + /// Allows using C-variadics. + (active, c_variadic, "1.34.0", Some(44930), None), + /// Allows capturing disjoint fields in a closure/generator (RFC 2229). + (incomplete, capture_disjoint_fields, "1.49.0", Some(53488), None), + /// Enables `#[cfg(panic = "...")]` config key. + (active, cfg_panic, "1.49.0", Some(77443), None), + /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used. + (active, cfg_sanitize, "1.41.0", Some(39699), None), + /// Allows `cfg(target_abi = "...")`. + (active, cfg_target_abi, "1.55.0", Some(80970), None), + /// Allows `cfg(target_has_atomic = "...")`. + (active, cfg_target_has_atomic, "1.9.0", Some(32976), None), /// Allows `cfg(target_thread_local)`. (active, cfg_target_thread_local, "1.7.0", Some(29594), None), - - /// Allows specialization of implementations (RFC 1210). - (incomplete, specialization, "1.7.0", Some(31844), None), - + /// Allow conditional compilation depending on rust version + (active, cfg_version, "1.45.0", Some(64796), None), + /// Allows `#[track_caller]` on closures and generators. + (active, closure_track_caller, "1.57.0", Some(87417), None), + /// Allows to use the `#[cmse_nonsecure_entry]` attribute. + (active, cmse_nonsecure_entry, "1.48.0", Some(75835), None), + /// Allows `async {}` expressions in const contexts. + (active, const_async_blocks, "1.53.0", Some(85368), None), + // Allows limiting the evaluation steps of const expressions + (active, const_eval_limit, "1.43.0", Some(67217), None), + /// Allows the definition of `const extern fn` and `const unsafe extern fn`. + (active, const_extern_fn, "1.40.0", Some(64926), None), + /// Allows basic arithmetic on floating point types in a `const fn`. + (active, const_fn_floating_point_arithmetic, "1.48.0", Some(57241), None), + /// Allows using and casting function pointers in a `const fn`. + (active, const_fn_fn_ptr_basics, "1.48.0", Some(57563), None), + /// Allows trait bounds in `const fn`. + (active, const_fn_trait_bound, "1.53.0", Some(57563), None), + /// Allows `for _ in _` loops in const contexts. + (active, const_for, "1.56.0", Some(87575), None), + /// Allows const generics to have default values (e.g. `struct Foo<const N: usize = 3>(...);`). + (active, const_generics_defaults, "1.51.0", Some(44580), None), + /// Allows argument and return position `impl Trait` in a `const fn`. + (active, const_impl_trait, "1.48.0", Some(77463), None), + /// Allows using `&mut` in constant functions. + (active, const_mut_refs, "1.41.0", Some(57349), None), + /// Be more precise when looking for live drops in a const context. + (active, const_precise_live_drops, "1.46.0", Some(73255), None), + /// Allows references to types with interior mutability within constants + (active, const_refs_to_cell, "1.51.0", Some(80384), None), + /// Allows `impl const Trait for T` syntax. + (active, const_trait_impl, "1.42.0", Some(67792), None), + /// Allows the `?` operator in const contexts. + (active, const_try, "1.56.0", Some(74935), None), + /// Allows using `crate` as visibility modifier, synonymous with `pub(crate)`. + (active, crate_visibility_modifier, "1.23.0", Some(53120), None), + /// Allows non-builtin attributes in inner attribute position. + (active, custom_inner_attributes, "1.30.0", Some(54726), None), + /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`. + (active, custom_test_frameworks, "1.30.0", Some(50297), None), + /// Allows declarative macros 2.0 (`macro`). + (active, decl_macro, "1.17.0", Some(39412), None), + /// Allows rustc to inject a default alloc_error_handler + (active, default_alloc_error_handler, "1.48.0", Some(66741), None), + /// Allows default type parameters to influence type inference. + (active, default_type_parameter_fallback, "1.3.0", Some(27336), None), + /// Allows `#[derive(Default)]` and `#[default]` on enums. + (active, derive_default_enum, "1.56.0", Some(86985), None), + /// Allows the use of destructuring assignments. + (active, destructuring_assignment, "1.49.0", Some(71126), None), + /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. + (active, doc_auto_cfg, "1.58.0", Some(43781), None), + /// Allows `#[doc(cfg(...))]`. + (active, doc_cfg, "1.21.0", Some(43781), None), + /// Allows `#[doc(cfg_hide(...))]`. + (active, doc_cfg_hide, "1.57.0", Some(43781), None), + /// Allows `#[doc(masked)]`. + (active, doc_masked, "1.21.0", Some(44027), None), + /// Allows `X..Y` patterns. + (active, exclusive_range_pattern, "1.11.0", Some(37854), None), + /// Allows exhaustive pattern matching on types that contain uninhabited types. + (active, exhaustive_patterns, "1.13.0", Some(51085), None), + /// Allows explicit generic arguments specification with `impl Trait` present. + (active, explicit_generic_args_with_impl_trait, "1.56.0", Some(83701), None), + /// Allows defining `extern type`s. + (active, extern_types, "1.23.0", Some(43467), None), + /// Allows the use of `#[ffi_const]` on foreign functions. + (active, ffi_const, "1.45.0", Some(58328), None), + /// Allows the use of `#[ffi_pure]` on foreign functions. + (active, ffi_pure, "1.45.0", Some(58329), None), + /// Allows using `#[ffi_returns_twice]` on foreign functions. + (active, ffi_returns_twice, "1.34.0", Some(58314), None), + /// Allows using `#[repr(align(...))]` on function items + (active, fn_align, "1.53.0", Some(82232), None), + /// Allows defining generators. + (active, generators, "1.21.0", Some(43122), None), + /// Infer generic args for both consts and types. + (active, generic_arg_infer, "1.55.0", Some(85077), None), + /// Allows associated types to be generic, e.g., `type Foo<T>;` (RFC 1598). + (active, generic_associated_types, "1.23.0", Some(44265), None), + /// Allows non-trivial generic constants which have to have wfness manually propagated to callers + (incomplete, generic_const_exprs, "1.56.0", Some(76560), None), + /// Allows using `..X`, `..=X`, `...X`, and `X..` as a pattern. + (active, half_open_range_patterns, "1.41.0", Some(67264), None), + /// Allows `if let` guard in match arms. + (active, if_let_guard, "1.47.0", Some(51114), None), + /// Allows using imported `main` function + (active, imported_main, "1.53.0", Some(28937), None), + /// Allows in-band quantification of lifetime bindings (e.g., `fn foo(x: &'a u8) -> &'a u8`). + (active, in_band_lifetimes, "1.23.0", Some(44524), None), + /// Allows inferring `'static` outlives requirements (RFC 2093). + (active, infer_static_outlives_requirements, "1.26.0", Some(54185), None), + /// Allows associated types in inherent impls. + (incomplete, inherent_associated_types, "1.52.0", Some(8995), None), + /// Allow anonymous constants from an inline `const` block + (active, inline_const, "1.49.0", Some(76001), None), + /// Allow anonymous constants from an inline `const` block in pattern position + (incomplete, inline_const_pat, "1.58.0", Some(76001), None), + /// Allows using `pointer` and `reference` in intra-doc links + (active, intra_doc_pointers, "1.51.0", Some(80896), None), + /// Allows `#[instruction_set(_)]` attribute + (active, isa_attribute, "1.48.0", Some(74727), None), + /// Allows `'a: { break 'a; }`. + (active, label_break_value, "1.28.0", Some(48594), None), + // Allows setting the threshold for the `large_assignments` lint. + (active, large_assignments, "1.52.0", Some(83518), None), + /// Allows `if/while p && let q = r && ...` chains. + (incomplete, let_chains, "1.37.0", Some(53667), None), + /// Allows `let...else` statements. + (active, let_else, "1.56.0", Some(87335), None), + /// Allows `#[link(..., cfg(..))]`. + (active, link_cfg, "1.14.0", Some(37406), None), + /// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check. + (active, lint_reasons, "1.31.0", Some(54503), None), + /// Allows `#[marker]` on certain traits allowing overlapping implementations. + (active, marker_trait_attr, "1.30.0", Some(29864), None), /// A minimal, sound subset of specialization intended to be used by the /// standard library until the soundness issues with specialization /// are fixed. (active, min_specialization, "1.7.0", Some(31844), None), - + /// Allows qualified paths in struct expressions, struct patterns and tuple struct patterns. + (active, more_qualified_paths, "1.54.0", Some(86935), None), + /// Allows the `#[must_not_suspend]` attribute. + (active, must_not_suspend, "1.57.0", Some(83310), None), /// Allows using `#[naked]` on functions. (active, naked_functions, "1.9.0", Some(32408), None), - - /// Allows `cfg(target_has_atomic = "...")`. - (active, cfg_target_has_atomic, "1.9.0", Some(32976), None), - - /// Allows `X..Y` patterns. - (active, exclusive_range_pattern, "1.11.0", Some(37854), None), - + /// Allows specifying modifiers in the link attribute: `#[link(modifiers = "...")]` + (active, native_link_modifiers, "1.53.0", Some(81490), None), + /// Allows specifying the as-needed link modifier + (active, native_link_modifiers_as_needed, "1.53.0", Some(81490), None), + /// Allows specifying the bundle link modifier + (active, native_link_modifiers_bundle, "1.53.0", Some(81490), None), + /// Allows specifying the verbatim link modifier + (active, native_link_modifiers_verbatim, "1.53.0", Some(81490), None), + /// Allows specifying the whole-archive link modifier + (active, native_link_modifiers_whole_archive, "1.53.0", Some(81490), None), + /// Allow negative trait implementations. + (active, negative_impls, "1.44.0", Some(68318), None), /// Allows the `!` type. Does not imply 'exhaustive_patterns' (below) any more. (active, never_type, "1.13.0", Some(35121), None), - - /// Allows exhaustive pattern matching on types that contain uninhabited types. - (active, exhaustive_patterns, "1.13.0", Some(51085), None), - + /// Allows diverging expressions to fall back to `!` rather than `()`. + (active, never_type_fallback, "1.41.0", Some(65992), None), + /// Allows using non lexical lifetimes (RFC 2094). + (active, nll, "1.0.0", Some(43234), None), + /// Allows `#![no_core]`. + (active, no_core, "1.3.0", Some(29639), None), + /// Allows function attribute `#[no_coverage]`, to bypass coverage + /// instrumentation of that function. + (active, no_coverage, "1.53.0", Some(84605), None), + /// Allows the use of `no_sanitize` attribute. + (active, no_sanitize, "1.42.0", Some(39699), None), + /// Allows using the `non_exhaustive_omitted_patterns` lint. + (active, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554), None), + /// Allows making `dyn Trait` well-formed even if `Trait` is not object safe. + /// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and + /// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden. + (active, object_safe_for_dispatch, "1.40.0", Some(43561), None), + /// Allows using `#[optimize(X)]`. + (active, optimize_attribute, "1.34.0", Some(54882), None), + /// Allows `extern "platform-intrinsic" { ... }`. + (active, platform_intrinsics, "1.4.0", Some(27731), None), + /// Allows using `#![plugin(myplugin)]`. + (active, plugin, "1.0.0", Some(29597), None), + /// Allows exhaustive integer pattern matching on `usize` and `isize`. + (active, precise_pointer_size_matching, "1.32.0", Some(56354), None), + /// Allows macro attributes on expressions, statements and non-inline modules. + (active, proc_macro_hygiene, "1.30.0", Some(54727), None), + /// Allows the use of raw-dylibs (RFC 2627). + (incomplete, raw_dylib, "1.40.0", Some(58713), None), + /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. + (active, raw_ref_op, "1.41.0", Some(64490), None), + /// Allows using the `#[register_attr]` attribute. + (active, register_attr, "1.41.0", Some(66080), None), + /// Allows using the `#[register_tool]` attribute. + (active, register_tool, "1.41.0", Some(66079), None), + /// Allows the `#[repr(i128)]` attribute for enums. + (incomplete, repr128, "1.16.0", Some(56071), None), + /// Allows `repr(simd)` and importing the various simd intrinsics. + (active, repr_simd, "1.4.0", Some(27731), None), + /// Allows the use of SIMD types in functions declared in `extern` blocks. + (active, simd_ffi, "1.0.0", Some(27731), None), + /// Allows specialization of implementations (RFC 1210). + (incomplete, specialization, "1.7.0", Some(31844), None), + /// Allows `#[link(kind="static-nobundle"...)]`. + (active, static_nobundle, "1.16.0", Some(37403), None), + /// Allows attributes on expressions and non-item statements. + (active, stmt_expr_attributes, "1.6.0", Some(15701), None), + /// Allows the use of `#[target_feature]` on safe functions. + (active, target_feature_11, "1.45.0", Some(69098), None), + /// Allows using `#[thread_local]` on `static` items. + (active, thread_local, "1.0.0", Some(29594), None), + /// Allows defining `trait X = A + B;` alias items. + (active, trait_alias, "1.24.0", Some(41517), None), + /// Allows upcasting trait objects via supertraits. + /// Trait upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`. + (incomplete, trait_upcasting, "1.56.0", Some(65991), None), + /// Allows #[repr(transparent)] on unions (RFC 2645). + (active, transparent_unions, "1.37.0", Some(60405), None), + /// Allows inconsistent bounds in where clauses. + (active, trivial_bounds, "1.28.0", Some(48214), None), + /// Allows using `try {...}` expressions. + (active, try_blocks, "1.29.0", Some(31436), None), + /// Allows `impl Trait` to be used inside type aliases (RFC 2515). + (active, type_alias_impl_trait, "1.38.0", Some(63063), None), + /// Allows the use of type ascription in expressions. + (active, type_ascription, "1.6.0", Some(23416), None), + /// Allows creation of instances of a struct by moving fields that have + /// not changed from prior instances of the same struct (RFC #2528) + (incomplete, type_changing_struct_update, "1.58.0", Some(86555), None), + /// Allows unsized fn parameters. + (active, unsized_fn_params, "1.49.0", Some(48055), None), + /// Allows unsized rvalues at arguments and parameters. + (incomplete, unsized_locals, "1.30.0", Some(48055), None), + /// Allows unsized tuple coercion. + (active, unsized_tuple_coercion, "1.20.0", Some(42877), None), /// Allows `union`s to implement `Drop`. Moreover, `union`s may now include fields /// that don't implement `Copy` as long as they don't have any drop glue. /// This is checked recursively. On encountering type variable where no progress can be made, @@ -346,346 +534,11 @@ /// /// NOTE: A limited form of `union U { ... }` was accepted in 1.19.0. (active, untagged_unions, "1.13.0", Some(55149), None), - - /// Allows `#[link(..., cfg(..))]`. - (active, link_cfg, "1.14.0", Some(37406), None), - - /// Allows `extern "ptx-*" fn()`. - (active, abi_ptx, "1.15.0", Some(38788), None), - - /// Allows the `#[repr(i128)]` attribute for enums. - (incomplete, repr128, "1.16.0", Some(56071), None), - - /// Allows `#[link(kind="static-nobundle"...)]`. - (active, static_nobundle, "1.16.0", Some(37403), None), - - /// Allows `extern "msp430-interrupt" fn()`. - (active, abi_msp430_interrupt, "1.16.0", Some(38487), None), - - /// Allows declarative macros 2.0 (`macro`). - (active, decl_macro, "1.17.0", Some(39412), None), - - /// Allows `extern "x86-interrupt" fn()`. - (active, abi_x86_interrupt, "1.17.0", Some(40180), None), - - /// Allows a test to fail without failing the whole suite. - (active, allow_fail, "1.19.0", Some(46488), None), - - /// Allows unsized tuple coercion. - (active, unsized_tuple_coercion, "1.20.0", Some(42877), None), - - /// Allows defining generators. - (active, generators, "1.21.0", Some(43122), None), - - /// Allows `#[doc(cfg(...))]`. - (active, doc_cfg, "1.21.0", Some(43781), None), - - /// Allows `#[doc(masked)]`. - (active, doc_masked, "1.21.0", Some(44027), None), - - /// Allows using `crate` as visibility modifier, synonymous with `pub(crate)`. - (active, crate_visibility_modifier, "1.23.0", Some(53120), None), - - /// Allows defining `extern type`s. - (active, extern_types, "1.23.0", Some(43467), None), - - /// Allows trait methods with arbitrary self types. - (active, arbitrary_self_types, "1.23.0", Some(44874), None), - - /// Allows in-band quantification of lifetime bindings (e.g., `fn foo(x: &'a u8) -> &'a u8`). - (active, in_band_lifetimes, "1.23.0", Some(44524), None), - - /// Allows associated types to be generic, e.g., `type Foo<T>;` (RFC 1598). - (active, generic_associated_types, "1.23.0", Some(44265), None), - - /// Allows defining `trait X = A + B;` alias items. - (active, trait_alias, "1.24.0", Some(41517), None), - - /// Allows inferring `'static` outlives requirements (RFC 2093). - (active, infer_static_outlives_requirements, "1.26.0", Some(54185), None), - - /// Allows dereferencing raw pointers during const eval. - (active, const_raw_ptr_deref, "1.27.0", Some(51911), None), - - /// Allows inconsistent bounds in where clauses. - (active, trivial_bounds, "1.28.0", Some(48214), None), - - /// Allows `'a: { break 'a; }`. - (active, label_break_value, "1.28.0", Some(48594), None), - - /// Allows using `#[doc(keyword = "...")]`. - (active, doc_keyword, "1.28.0", Some(51315), None), - - /// Allows using `try {...}` expressions. - (active, try_blocks, "1.29.0", Some(31436), None), - - /// Allows defining an `#[alloc_error_handler]`. - (active, alloc_error_handler, "1.29.0", Some(51540), None), - - /// Allows using the `amdgpu-kernel` ABI. - (active, abi_amdgpu_kernel, "1.29.0", Some(51575), None), - - /// Allows `#[marker]` on certain traits allowing overlapping implementations. - (active, marker_trait_attr, "1.30.0", Some(29864), None), - - /// Allows macro attributes on expressions, statements and non-inline modules. - (active, proc_macro_hygiene, "1.30.0", Some(54727), None), - - /// Allows unsized rvalues at arguments and parameters. - (incomplete, unsized_locals, "1.30.0", Some(48055), None), - - /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`. - (active, custom_test_frameworks, "1.30.0", Some(50297), None), - - /// Allows non-builtin attributes in inner attribute position. - (active, custom_inner_attributes, "1.30.0", Some(54726), None), - - /// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check. - (active, lint_reasons, "1.31.0", Some(54503), None), - - /// Allows exhaustive integer pattern matching on `usize` and `isize`. - (active, precise_pointer_size_matching, "1.32.0", Some(56354), None), - - /// Allows using `#[ffi_returns_twice]` on foreign functions. - (active, ffi_returns_twice, "1.34.0", Some(58314), None), - - /// Allows using `#[optimize(X)]`. - (active, optimize_attribute, "1.34.0", Some(54882), None), - - /// Allows using C-variadics. - (active, c_variadic, "1.34.0", Some(44930), None), - - /// Allows the user of associated type bounds. - (active, associated_type_bounds, "1.34.0", Some(52662), None), - - /// Allows `if/while p && let q = r && ...` chains. - (incomplete, let_chains, "1.37.0", Some(53667), None), - - /// Allows #[repr(transparent)] on unions (RFC 2645). - (active, transparent_unions, "1.37.0", Some(60405), None), - - /// Allows explicit discriminants on non-unit enum variants. - (active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None), - - /// Allows `async || body` closures. - (active, async_closure, "1.37.0", Some(62290), None), - - /// Allows `impl Trait` to be used inside type aliases (RFC 2515). - (active, type_alias_impl_trait, "1.38.0", Some(63063), None), - - /// Allows the definition of `const extern fn` and `const unsafe extern fn`. - (active, const_extern_fn, "1.40.0", Some(64926), None), - - /// Allows the use of raw-dylibs (RFC 2627). - (incomplete, raw_dylib, "1.40.0", Some(58713), None), - - /// Allows making `dyn Trait` well-formed even if `Trait` is not object safe. - /// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and - /// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden. - (active, object_safe_for_dispatch, "1.40.0", Some(43561), None), - - /// Allows using the `efiapi` ABI. - (active, abi_efiapi, "1.40.0", Some(65815), None), - - /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. - (active, raw_ref_op, "1.41.0", Some(64490), None), - - /// Allows diverging expressions to fall back to `!` rather than `()`. - (active, never_type_fallback, "1.41.0", Some(65992), None), - - /// Allows using the `#[register_attr]` attribute. - (active, register_attr, "1.41.0", Some(66080), None), - - /// Allows using the `#[register_tool]` attribute. - (active, register_tool, "1.41.0", Some(66079), None), - - /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used. - (active, cfg_sanitize, "1.41.0", Some(39699), None), - - /// Allows using `..X`, `..=X`, `...X`, and `X..` as a pattern. - (active, half_open_range_patterns, "1.41.0", Some(67264), None), - - /// Allows using `&mut` in constant functions. - (active, const_mut_refs, "1.41.0", Some(57349), None), - - /// Allows `impl const Trait for T` syntax. - (active, const_trait_impl, "1.42.0", Some(67792), None), - - /// Allows the use of `no_sanitize` attribute. - (active, no_sanitize, "1.42.0", Some(39699), None), - - // Allows limiting the evaluation steps of const expressions - (active, const_eval_limit, "1.43.0", Some(67217), None), - - /// Allow negative trait implementations. - (active, negative_impls, "1.44.0", Some(68318), None), - - /// Allows the use of `#[target_feature]` on safe functions. - (active, target_feature_11, "1.45.0", Some(69098), None), - - /// Allow conditional compilation depending on rust version - (active, cfg_version, "1.45.0", Some(64796), None), - - /// Allows the use of `#[ffi_pure]` on foreign functions. - (active, ffi_pure, "1.45.0", Some(58329), None), - - /// Allows the use of `#[ffi_const]` on foreign functions. - (active, ffi_const, "1.45.0", Some(58328), None), - - /// Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`. - (active, abi_avr_interrupt, "1.45.0", Some(69664), None), - - /// Be more precise when looking for live drops in a const context. - (active, const_precise_live_drops, "1.46.0", Some(73255), None), - - /// Allows capturing variables in scope using format_args! - (active, format_args_capture, "1.46.0", Some(67984), None), - - /// Allows `if let` guard in match arms. - (active, if_let_guard, "1.47.0", Some(51114), None), - - /// Allows basic arithmetic on floating point types in a `const fn`. - (active, const_fn_floating_point_arithmetic, "1.48.0", Some(57241), None), - - /// Allows using and casting function pointers in a `const fn`. - (active, const_fn_fn_ptr_basics, "1.48.0", Some(57563), None), - - /// Allows to use the `#[cmse_nonsecure_entry]` attribute. - (active, cmse_nonsecure_entry, "1.48.0", Some(75835), None), - - /// Allows rustc to inject a default alloc_error_handler - (active, default_alloc_error_handler, "1.48.0", Some(66741), None), - - /// Allows argument and return position `impl Trait` in a `const fn`. - (active, const_impl_trait, "1.48.0", Some(77463), None), - - /// Allows `#[instruction_set(_)]` attribute - (active, isa_attribute, "1.48.0", Some(74727), None), - - /// Allow anonymous constants from an inline `const` block - (incomplete, inline_const, "1.49.0", Some(76001), None), - - /// Allows unsized fn parameters. - (active, unsized_fn_params, "1.49.0", Some(48055), None), - - /// Allows the use of destructuring assignments. - (active, destructuring_assignment, "1.49.0", Some(71126), None), - - /// Enables `#[cfg(panic = "...")]` config key. - (active, cfg_panic, "1.49.0", Some(77443), None), - - /// Allows capturing disjoint fields in a closure/generator (RFC 2229). - (incomplete, capture_disjoint_fields, "1.49.0", Some(53488), None), - - /// Allows const generics to have default values (e.g. `struct Foo<const N: usize = 3>(...);`). - (active, const_generics_defaults, "1.51.0", Some(44580), None), - - /// Allows references to types with interior mutability within constants - (active, const_refs_to_cell, "1.51.0", Some(80384), None), - - /// Allows using `pointer` and `reference` in intra-doc links - (active, intra_doc_pointers, "1.51.0", Some(80896), None), - - /// Allows `extern "C-cmse-nonsecure-call" fn()`. - (active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None), - - /// Lessens the requirements for structs to implement `Unsize`. - (active, relaxed_struct_unsize, "1.51.0", Some(81793), None), - - /// Allows associated types in inherent impls. - (incomplete, inherent_associated_types, "1.52.0", Some(8995), None), - - // Allows setting the threshold for the `large_assignments` lint. - (active, large_assignments, "1.52.0", Some(83518), None), - - /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries. - (active, c_unwind, "1.52.0", Some(74990), None), - - /// Allows using `#[repr(align(...))]` on function items - (active, fn_align, "1.53.0", Some(82232), None), - /// Allows `extern "wasm" fn` (active, wasm_abi, "1.53.0", Some(83788), None), - - /// Allows function attribute `#[no_coverage]`, to bypass coverage - /// instrumentation of that function. - (active, no_coverage, "1.53.0", Some(84605), None), - - /// Allows trait bounds in `const fn`. - (active, const_fn_trait_bound, "1.53.0", Some(57563), None), - - /// Allows `async {}` expressions in const contexts. - (active, const_async_blocks, "1.53.0", Some(85368), None), - - /// Allows using imported `main` function - (active, imported_main, "1.53.0", Some(28937), None), - - /// Allows specifying modifiers in the link attribute: `#[link(modifiers = "...")]` - (active, native_link_modifiers, "1.53.0", Some(81490), None), - - /// Allows specifying the bundle link modifier - (active, native_link_modifiers_bundle, "1.53.0", Some(81490), None), - - /// Allows specifying the verbatim link modifier - (active, native_link_modifiers_verbatim, "1.53.0", Some(81490), None), - - /// Allows specifying the whole-archive link modifier - (active, native_link_modifiers_whole_archive, "1.53.0", Some(81490), None), - - /// Allows specifying the as-needed link modifier - (active, native_link_modifiers_as_needed, "1.53.0", Some(81490), None), - - /// Allows qualified paths in struct expressions, struct patterns and tuple struct patterns. - (active, more_qualified_paths, "1.54.0", Some(86935), None), - - /// Allows `cfg(target_abi = "...")`. - (active, cfg_target_abi, "1.55.0", Some(80970), None), - - /// Infer generic args for both consts and types. - (active, generic_arg_infer, "1.55.0", Some(85077), None), - - /// Allows `#[derive(Default)]` and `#[default]` on enums. - (active, derive_default_enum, "1.56.0", Some(86985), None), - - /// Allows `for _ in _` loops in const contexts. - (active, const_for, "1.56.0", Some(87575), None), - - /// Allows the `?` operator in const contexts. - (active, const_try, "1.56.0", Some(74935), None), - - /// Allows upcasting trait objects via supertraits. - /// Trait upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`. - (incomplete, trait_upcasting, "1.56.0", Some(65991), None), - - /// Allows explicit generic arguments specification with `impl Trait` present. - (active, explicit_generic_args_with_impl_trait, "1.56.0", Some(83701), None), - - /// Allows using doc(primitive) without a future-incompat warning - (active, doc_primitive, "1.56.0", Some(88070), None), - - /// Allows non-trivial generic constants which have to have wfness manually propagated to callers - (incomplete, generic_const_exprs, "1.56.0", Some(76560), None), - - /// Allows additional const parameter types, such as `&'static str` or user defined types - (incomplete, adt_const_params, "1.56.0", Some(44580), None), - - /// Allows `let...else` statements. - (active, let_else, "1.56.0", Some(87335), None), - - /// Allows the `#[must_not_suspend]` attribute. - (active, must_not_suspend, "1.57.0", Some(83310), None), - - /// Allows `#[track_caller]` on closures and generators. - (active, closure_track_caller, "1.57.0", Some(87417), None), - - /// Allows `#[doc(cfg_hide(...))]`. - (active, doc_cfg_hide, "1.57.0", Some(43781), None), - - /// Allows using the `non_exhaustive_omitted_patterns` lint. - (active, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554), None), - - /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. - (active, doc_auto_cfg, "1.57.0", Some(43781), None), + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! + // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // ------------------------------------------------------------------------- // feature-group-end: actual feature gates
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 85b0db4..f25b2d8 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -1,5 +1,6 @@ //! Built-in attributes and `cfg` flag gating. +use AttributeDuplicates::*; use AttributeGate::*; use AttributeType::*; @@ -88,11 +89,66 @@ /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now. #[derive(Clone, Copy, Default)] pub struct AttributeTemplate { + /// If `true`, the attribute is allowed to be a bare word like `#[test]`. pub word: bool, + /// If `Some`, the attribute is allowed to take a list of items like `#[allow(..)]`. pub list: Option<&'static str>, + /// If `Some`, the attribute is allowed to be a name/value pair where the + /// value is a string, like `#[must_use = "reason"]`. pub name_value_str: Option<&'static str>, } +/// How to handle multiple duplicate attributes on the same item. +#[derive(Clone, Copy, Default)] +pub enum AttributeDuplicates { + /// Duplicates of this attribute are allowed. + /// + /// This should only be used with attributes where duplicates have semantic + /// meaning, or some kind of "additive" behavior. For example, `#[warn(..)]` + /// can be specified multiple times, and it combines all the entries. Or use + /// this if there is validation done elsewhere. + #[default] + DuplicatesOk, + /// Duplicates after the first attribute will be an unused_attribute warning. + /// + /// This is usually used for "word" attributes, where they are used as a + /// boolean marker, like `#[used]`. It is not necessarily wrong that there + /// are duplicates, but the others should probably be removed. + WarnFollowing, + /// Same as `WarnFollowing`, but only issues warnings for word-style attributes. + /// + /// This is only for special cases, for example multiple `#[macro_use]` can + /// be warned, but multiple `#[macro_use(...)]` should not because the list + /// form has different meaning from the word form. + WarnFollowingWordOnly, + /// Duplicates after the first attribute will be an error. + /// + /// This should be used where duplicates would be ignored, but carry extra + /// meaning that could cause confusion. For example, `#[stable(since="1.0")] + /// #[stable(since="2.0")]`, which version should be used for `stable`? + ErrorFollowing, + /// Duplicates preceding the last instance of the attribute will be an error. + /// + /// This is the same as `ErrorFollowing`, except the last attribute is the + /// one that is "used". This is typically used in cases like codegen + /// attributes which usually only honor the last attribute. + ErrorPreceding, + /// Duplicates after the first attribute will be an unused_attribute warning + /// with a note that this will be an error in the future. + /// + /// This should be used for attributes that should be `ErrorFollowing`, but + /// because older versions of rustc silently accepted (and ignored) the + /// attributes, this is used to transition. + FutureWarnFollowing, + /// Duplicates preceding the last instance of the attribute will be a + /// warning, with a note that this will be an error in the future. + /// + /// This is the same as `FutureWarnFollowing`, except the last attribute is + /// the one that is "used". Ideally these can eventually migrate to + /// `ErrorPreceding`. + FutureWarnPreceding, +} + /// A convenience macro for constructing attribute templates. /// E.g., `template!(Word, List: "description")` means that the attribute /// supports forms `#[attr]` and `#[attr(description)]`. @@ -114,26 +170,45 @@ } macro_rules! ungated { - ($attr:ident, $typ:expr, $tpl:expr $(,)?) => { - (sym::$attr, $typ, $tpl, Ungated) + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + type_: $typ, + template: $tpl, + gate: Ungated, + duplicates: $duplicates, + } }; } macro_rules! gated { - ($attr:ident, $typ:expr, $tpl:expr, $gate:ident, $msg:expr $(,)?) => { - (sym::$attr, $typ, $tpl, Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate))) + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $gate:ident, $msg:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + type_: $typ, + template: $tpl, + duplicates: $duplicates, + gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), + } }; - ($attr:ident, $typ:expr, $tpl:expr, $msg:expr $(,)?) => { - (sym::$attr, $typ, $tpl, Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr))) + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $msg:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + type_: $typ, + template: $tpl, + duplicates: $duplicates, + gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)), + } }; } macro_rules! rustc_attr { - (TEST, $attr:ident, $typ:expr, $tpl:expr $(,)?) => { + (TEST, $attr:ident, $typ:expr, $tpl:expr, $duplicate:expr $(,)?) => { rustc_attr!( $attr, $typ, $tpl, + $duplicate, concat!( "the `#[", stringify!($attr), @@ -142,13 +217,14 @@ ), ) }; - ($attr:ident, $typ:expr, $tpl:expr, $msg:expr $(,)?) => { - ( - sym::$attr, - $typ, - $tpl, - Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)), - ) + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $msg:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + type_: $typ, + template: $tpl, + duplicates: $duplicates, + gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)), + } }; } @@ -161,7 +237,13 @@ const IMPL_DETAIL: &str = "internal implementation detail"; const INTERNAL_UNSTABLE: &str = "this is an internal attribute that will never be stable"; -pub type BuiltinAttribute = (Symbol, AttributeType, AttributeTemplate, AttributeGate); +pub struct BuiltinAttribute { + pub name: Symbol, + pub type_: AttributeType, + pub template: AttributeTemplate, + pub duplicates: AttributeDuplicates, + pub gate: AttributeGate, +} /// Attributes that have a special meaning to rustc or rustdoc. #[rustfmt::skip] @@ -171,42 +253,48 @@ // ========================================================================== // Conditional compilation: - ungated!(cfg, Normal, template!(List: "predicate")), - ungated!(cfg_attr, Normal, template!(List: "predicate, attr1, attr2, ...")), + ungated!(cfg, Normal, template!(List: "predicate"), DuplicatesOk), + ungated!(cfg_attr, Normal, template!(List: "predicate, attr1, attr2, ..."), DuplicatesOk), // Testing: - ungated!(ignore, Normal, template!(Word, NameValueStr: "reason")), + ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing), ungated!( should_panic, Normal, - template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"), + template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"), FutureWarnFollowing, ), // FIXME(Centril): This can be used on stable but shouldn't. - ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name")), + ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing), // Macros: - ungated!(automatically_derived, Normal, template!(Word)), - // FIXME(#14407) - ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ...")), - ungated!(macro_escape, Normal, template!(Word)), // Deprecated synonym for `macro_use`. - ungated!(macro_export, Normal, template!(Word, List: "local_inner_macros")), - ungated!(proc_macro, Normal, template!(Word)), + ungated!(automatically_derived, Normal, template!(Word), WarnFollowing), + ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ..."), WarnFollowingWordOnly), + ungated!(macro_escape, Normal, template!(Word), WarnFollowing), // Deprecated synonym for `macro_use`. + ungated!(macro_export, Normal, template!(Word, List: "local_inner_macros"), WarnFollowing), + ungated!(proc_macro, Normal, template!(Word), ErrorFollowing), ungated!( proc_macro_derive, Normal, - template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)"), + template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)"), ErrorFollowing, ), - ungated!(proc_macro_attribute, Normal, template!(Word)), + ungated!(proc_macro_attribute, Normal, template!(Word), ErrorFollowing), // Lints: - ungated!(warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)), - ungated!(allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)), - ungated!(forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)), - ungated!(deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)), - ungated!(must_use, Normal, template!(Word, NameValueStr: "reason")), - gated!( - must_not_suspend, Normal, template!(Word, NameValueStr: "reason"), must_not_suspend, - experimental!(must_not_suspend) + ungated!( + warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk ), - // FIXME(#14407) + ungated!( + allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + ), + ungated!( + forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + ), + ungated!( + deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + ), + ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing), + gated!( + must_not_suspend, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, + must_not_suspend, experimental!(must_not_suspend) + ), ungated!( deprecated, Normal, template!( @@ -214,85 +302,96 @@ List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#, NameValueStr: "reason" ), + // This has special duplicate handling in E0550 to handle duplicates with rustc_deprecated + DuplicatesOk ), // Crate properties: - ungated!(crate_name, CrateLevel, template!(NameValueStr: "name")), - ungated!(crate_type, CrateLevel, template!(NameValueStr: "bin|lib|...")), - ungated!(crate_id, CrateLevel, template!(NameValueStr: "ignored")), + ungated!(crate_name, CrateLevel, template!(NameValueStr: "name"), FutureWarnFollowing), + ungated!(crate_type, CrateLevel, template!(NameValueStr: "bin|lib|..."), FutureWarnFollowing), + // crate_id is deprecated + ungated!(crate_id, CrateLevel, template!(NameValueStr: "ignored"), FutureWarnFollowing), // ABI, linking, symbols, and FFI ungated!( link, Normal, template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#), + DuplicatesOk, ), - ungated!(link_name, Normal, template!(NameValueStr: "name")), - ungated!(no_link, Normal, template!(Word)), - ungated!(repr, Normal, template!(List: "C")), - ungated!(export_name, Normal, template!(NameValueStr: "name")), - ungated!(link_section, Normal, template!(NameValueStr: "name")), - ungated!(no_mangle, Normal, template!(Word)), - ungated!(used, Normal, template!(Word)), + ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), + ungated!(no_link, Normal, template!(Word), WarnFollowing), + ungated!(repr, Normal, template!(List: "C"), DuplicatesOk), + ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), + ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), + ungated!(no_mangle, Normal, template!(Word), WarnFollowing), + ungated!(used, Normal, template!(Word), WarnFollowing), // Limits: - ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N")), - ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N")), + ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), + ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), gated!( - const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit, - experimental!(const_eval_limit) + const_eval_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing, + const_eval_limit, experimental!(const_eval_limit) ), gated!( - move_size_limit, CrateLevel, template!(NameValueStr: "N"), large_assignments, - experimental!(move_size_limit) + move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing, + large_assignments, experimental!(move_size_limit) ), // Entry point: - ungated!(main, Normal, template!(Word)), - ungated!(start, Normal, template!(Word)), - ungated!(no_start, CrateLevel, template!(Word)), - ungated!(no_main, CrateLevel, template!(Word)), + ungated!(main, Normal, template!(Word), WarnFollowing), + ungated!(start, Normal, template!(Word), WarnFollowing), + ungated!(no_start, CrateLevel, template!(Word), WarnFollowing), + ungated!(no_main, CrateLevel, template!(Word), WarnFollowing), // Modules, prelude, and resolution: - ungated!(path, Normal, template!(NameValueStr: "file")), - ungated!(no_std, CrateLevel, template!(Word)), - ungated!(no_implicit_prelude, Normal, template!(Word)), - ungated!(non_exhaustive, Normal, template!(Word)), + ungated!(path, Normal, template!(NameValueStr: "file"), FutureWarnFollowing), + ungated!(no_std, CrateLevel, template!(Word), WarnFollowing), + ungated!(no_implicit_prelude, Normal, template!(Word), WarnFollowing), + ungated!(non_exhaustive, Normal, template!(Word), WarnFollowing), // Runtime - ungated!(windows_subsystem, Normal, template!(NameValueStr: "windows|console")), - ungated!(panic_handler, Normal, template!(Word)), // RFC 2070 + ungated!( + windows_subsystem, Normal, + template!(NameValueStr: "windows|console"), FutureWarnFollowing + ), + ungated!(panic_handler, Normal, template!(Word), WarnFollowing), // RFC 2070 // Code generation: - ungated!(inline, Normal, template!(Word, List: "always|never")), - ungated!(cold, Normal, template!(Word)), - ungated!(no_builtins, Normal, template!(Word)), - ungated!(target_feature, Normal, template!(List: r#"enable = "name""#)), - ungated!(track_caller, Normal, template!(Word)), + ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing), + ungated!(cold, Normal, template!(Word), WarnFollowing), + ungated!(no_builtins, Normal, template!(Word), WarnFollowing), + ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk), + ungated!(track_caller, Normal, template!(Word), WarnFollowing), gated!( no_sanitize, Normal, - template!(List: "address, memory, thread"), + template!(List: "address, memory, thread"), DuplicatesOk, experimental!(no_sanitize) ), - gated!(no_coverage, Normal, template!(Word), experimental!(no_coverage)), + gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)), - // FIXME: #14408 assume docs are used since rustdoc looks at them. - ungated!(doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string")), + ungated!( + doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk + ), // ========================================================================== // Unstable attributes: // ========================================================================== // Linking: - gated!(naked, Normal, template!(Word), naked_functions, experimental!(naked)), + gated!(naked, Normal, template!(Word), WarnFollowing, naked_functions, experimental!(naked)), gated!( - link_ordinal, Normal, template!(List: "ordinal"), raw_dylib, + link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib, experimental!(link_ordinal) ), // Plugins: - ( - sym::plugin, CrateLevel, template!(List: "name"), - Gated( + BuiltinAttribute { + name: sym::plugin, + type_: CrateLevel, + template: template!(List: "name"), + duplicates: DuplicatesOk, + gate: Gated( Stability::Deprecated( "https://github.com/rust-lang/rust/pull/64675", Some("may be removed in a future compiler version"), @@ -300,46 +399,56 @@ sym::plugin, "compiler plugins are deprecated", cfg_fn!(plugin) - ) - ), + ), + }, // Testing: - gated!(allow_fail, Normal, template!(Word), experimental!(allow_fail)), + gated!(allow_fail, Normal, template!(Word), WarnFollowing, experimental!(allow_fail)), gated!( - test_runner, CrateLevel, template!(List: "path"), custom_test_frameworks, + test_runner, CrateLevel, template!(List: "path"), ErrorFollowing, custom_test_frameworks, "custom test frameworks are an unstable feature", ), // RFC #1268 - gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)), gated!( - thread_local, Normal, template!(Word), + marker, Normal, template!(Word), WarnFollowing, marker_trait_attr, experimental!(marker) + ), + gated!( + thread_local, Normal, template!(Word), WarnFollowing, "`#[thread_local]` is an experimental feature, and does not currently handle destructors", ), - gated!(no_core, CrateLevel, template!(Word), experimental!(no_core)), + gated!(no_core, CrateLevel, template!(Word), WarnFollowing, experimental!(no_core)), // RFC 2412 gated!( - optimize, Normal, template!(List: "size|speed"), optimize_attribute, + optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute, experimental!(optimize), ), // RFC 2867 - gated!(instruction_set, Normal, template!(List: "set"), isa_attribute, experimental!(instruction_set)), - - gated!(ffi_returns_twice, Normal, template!(Word), experimental!(ffi_returns_twice)), - gated!(ffi_pure, Normal, template!(Word), experimental!(ffi_pure)), - gated!(ffi_const, Normal, template!(Word), experimental!(ffi_const)), gated!( - register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), + instruction_set, Normal, template!(List: "set"), ErrorPreceding, + isa_attribute, experimental!(instruction_set) + ), + + gated!( + ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice) + ), + gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)), + gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)), + gated!( + register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), DuplicatesOk, experimental!(register_attr), ), gated!( - register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), + register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk, experimental!(register_tool), ), - gated!(cmse_nonsecure_entry, Normal, template!(Word), experimental!(cmse_nonsecure_entry)), + gated!( + cmse_nonsecure_entry, Normal, template!(Word), WarnFollowing, + experimental!(cmse_nonsecure_entry) + ), // RFC 2632 gated!( - default_method_body_is_const, Normal, template!(Word), const_trait_impl, + default_method_body_is_const, Normal, template!(Word), WarnFollowing, const_trait_impl, "`default_method_body_is_const` is a temporary placeholder for declaring default bodies \ as `const`, which may be removed or renamed in the future." ), @@ -348,34 +457,33 @@ // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== - ungated!(feature, CrateLevel, template!(List: "name1, name1, ...")), - // FIXME(#14407) -- only looked at on-demand so we can't - // guarantee they'll have already been checked. + ungated!(feature, CrateLevel, template!(List: "name1, name1, ..."), DuplicatesOk), + // DuplicatesOk since it has its own validation ungated!( rustc_deprecated, Normal, - template!(List: r#"since = "version", reason = "...""#) + template!(List: r#"since = "version", reason = "...""#), DuplicatesOk // See E0550 ), - // FIXME(#14407) - ungated!(stable, Normal, template!(List: r#"feature = "name", since = "version""#)), - // FIXME(#14407) + // DuplicatesOk since it has its own validation + ungated!( + stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk + ), ungated!( unstable, Normal, - template!(List: r#"feature = "name", reason = "...", issue = "N""#), + template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk, ), - // FIXME(#14407) - ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#)), - // FIXME(#14407) - ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#)), + ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), + ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), gated!( - allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), + allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, "allow_internal_unstable side-steps feature gating and stability checks", ), gated!( - rustc_allow_const_fn_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), + rustc_allow_const_fn_unstable, Normal, + template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, "rustc_allow_const_fn_unstable side-steps feature gating and stability checks" ), gated!( - allow_internal_unsafe, Normal, template!(Word), + allow_internal_unsafe, Normal, template!(Word), WarnFollowing, "allow_internal_unsafe side-steps the unsafe_code lint", ), @@ -383,9 +491,9 @@ // Internal attributes: Type system related: // ========================================================================== - gated!(fundamental, Normal, template!(Word), experimental!(fundamental)), + gated!(fundamental, Normal, template!(Word), WarnFollowing, experimental!(fundamental)), gated!( - may_dangle, Normal, template!(Word), dropck_eyepatch, + may_dangle, Normal, template!(Word), WarnFollowing, dropck_eyepatch, "`may_dangle` has unstable semantics and may be removed in the future", ), @@ -393,26 +501,32 @@ // Internal attributes: Runtime related: // ========================================================================== - rustc_attr!(rustc_allocator, Normal, template!(Word), IMPL_DETAIL), - rustc_attr!(rustc_allocator_nounwind, Normal, template!(Word), IMPL_DETAIL), - gated!(alloc_error_handler, Normal, template!(Word), experimental!(alloc_error_handler)), + rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_allocator_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), gated!( - default_lib_allocator, Normal, template!(Word), allocator_internals, + alloc_error_handler, Normal, template!(Word), WarnFollowing, + experimental!(alloc_error_handler) + ), + gated!( + default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals, experimental!(default_lib_allocator), ), gated!( - needs_allocator, Normal, template!(Word), allocator_internals, + needs_allocator, Normal, template!(Word), WarnFollowing, allocator_internals, experimental!(needs_allocator), ), - gated!(panic_runtime, Normal, template!(Word), experimental!(panic_runtime)), - gated!(needs_panic_runtime, Normal, template!(Word), experimental!(needs_panic_runtime)), + gated!(panic_runtime, Normal, template!(Word), WarnFollowing, experimental!(panic_runtime)), gated!( - compiler_builtins, Normal, template!(Word), + needs_panic_runtime, Normal, template!(Word), WarnFollowing, + experimental!(needs_panic_runtime) + ), + gated!( + compiler_builtins, Normal, template!(Word), WarnFollowing, "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \ which contains compiler-rt intrinsics and will never be stable", ), gated!( - profiler_runtime, Normal, template!(Word), + profiler_runtime, Normal, template!(Word), WarnFollowing, "the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \ which contains the profiler runtime and will never be stable", ), @@ -422,10 +536,12 @@ // ========================================================================== gated!( - linkage, Normal, template!(NameValueStr: "external|internal|..."), + linkage, Normal, template!(NameValueStr: "external|internal|..."), ErrorPreceding, "the `linkage` attribute is experimental and not portable across platforms", ), - rustc_attr!(rustc_std_internal_symbol, Normal, template!(Word), INTERNAL_UNSTABLE), + rustc_attr!( + rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE + ), // ========================================================================== // Internal attributes, Macro related: @@ -433,13 +549,13 @@ rustc_attr!( rustc_builtin_macro, Normal, - template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"), + template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"), ErrorFollowing, IMPL_DETAIL, ), - rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE), + rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), rustc_attr!( rustc_macro_transparency, Normal, - template!(NameValueStr: "transparent|semitransparent|opaque"), + template!(NameValueStr: "transparent|semitransparent|opaque"), ErrorFollowing, "used internally for testing macro hygiene", ), @@ -453,39 +569,49 @@ List: r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#, NameValueStr: "message" ), + ErrorFollowing, INTERNAL_UNSTABLE ), // Enumerates "identity-like" conversion methods to suggest on type mismatch. - rustc_attr!(rustc_conversion_suggestion, Normal, template!(Word), INTERNAL_UNSTABLE), + rustc_attr!( + rustc_conversion_suggestion, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE + ), // Prevents field reads in the marked trait or method to be considered // during dead code analysis. - rustc_attr!(rustc_trivial_field_reads, Normal, template!(Word), INTERNAL_UNSTABLE), + rustc_attr!( + rustc_trivial_field_reads, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE + ), // ========================================================================== // Internal attributes, Const related: // ========================================================================== - rustc_attr!(rustc_promotable, Normal, template!(Word), IMPL_DETAIL), - rustc_attr!(rustc_legacy_const_generics, Normal, template!(List: "N"), INTERNAL_UNSTABLE), + rustc_attr!(rustc_promotable, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!( + rustc_legacy_const_generics, Normal, template!(List: "N"), ErrorFollowing, + INTERNAL_UNSTABLE + ), // Do not const-check this function's body. It will always get replaced during CTFE. - rustc_attr!(rustc_do_not_const_check, Normal, template!(Word), INTERNAL_UNSTABLE), + rustc_attr!( + rustc_do_not_const_check, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE + ), // ========================================================================== // Internal attributes, Layout related: // ========================================================================== rustc_attr!( - rustc_layout_scalar_valid_range_start, Normal, template!(List: "value"), + rustc_layout_scalar_valid_range_start, Normal, template!(List: "value"), ErrorFollowing, "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \ niche optimizations in libcore and will never be stable", ), rustc_attr!( - rustc_layout_scalar_valid_range_end, Normal, template!(List: "value"), + rustc_layout_scalar_valid_range_end, Normal, template!(List: "value"), ErrorFollowing, "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \ niche optimizations in libcore and will never be stable", ), rustc_attr!( - rustc_nonnull_optimization_guaranteed, Normal, template!(Word), + rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing, "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to enable \ niche optimizations in libcore and will never be stable", ), @@ -494,57 +620,60 @@ // Internal attributes, Misc: // ========================================================================== gated!( - lang, Normal, template!(NameValueStr: "name"), lang_items, + lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, lang_items, "language items are subject to change", ), - ( - sym::rustc_diagnostic_item, - Normal, - template!(NameValueStr: "name"), - Gated( + BuiltinAttribute { + name: sym::rustc_diagnostic_item, + type_: Normal, + template: template!(NameValueStr: "name"), + duplicates: ErrorFollowing, + gate: Gated( Stability::Unstable, sym::rustc_attrs, "diagnostic items compiler internal support for linting", cfg_fn!(rustc_attrs), ), - ), + }, gated!( // Used in resolve: - prelude_import, Normal, template!(Word), + prelude_import, Normal, template!(Word), WarnFollowing, "`#[prelude_import]` is for use by rustc only", ), gated!( - rustc_paren_sugar, Normal, template!(Word), unboxed_closures, + rustc_paren_sugar, Normal, template!(Word), WarnFollowing, unboxed_closures, "unboxed_closures are still evolving", ), rustc_attr!( - rustc_inherit_overflow_checks, Normal, template!(Word), + rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing, "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \ overflow checking behavior of several libcore functions that are inlined \ across crates and will never be stable", ), - rustc_attr!(rustc_reservation_impl, Normal, template!(NameValueStr: "reservation message"), - "the `#[rustc_reservation_impl]` attribute is internally used \ - for reserving for `for<T> From<!> for T` impl" + rustc_attr!( + rustc_reservation_impl, Normal, + template!(NameValueStr: "reservation message"), ErrorFollowing, + "the `#[rustc_reservation_impl]` attribute is internally used \ + for reserving for `for<T> From<!> for T` impl" ), rustc_attr!( - rustc_test_marker, Normal, template!(Word), + rustc_test_marker, Normal, template!(Word), WarnFollowing, "the `#[rustc_test_marker]` attribute is used internally to track tests", ), rustc_attr!( - rustc_unsafe_specialization_marker, Normal, template!(Word), + rustc_unsafe_specialization_marker, Normal, template!(Word), WarnFollowing, "the `#[rustc_unsafe_specialization_marker]` attribute is used to check specializations" ), rustc_attr!( - rustc_specialization_trait, Normal, template!(Word), + rustc_specialization_trait, Normal, template!(Word), WarnFollowing, "the `#[rustc_specialization_trait]` attribute is used to check specializations" ), rustc_attr!( - rustc_main, Normal, template!(Word), + rustc_main, Normal, template!(Word), WarnFollowing, "the `#[rustc_main]` attribute is used internally to specify test entry point function", ), rustc_attr!( - rustc_skip_array_during_method_dispatch, Normal, template!(Word), + rustc_skip_array_during_method_dispatch, Normal, template!(Word), WarnFollowing, "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \ from method dispatch when the receiver is an array, for compatibility in editions < 2021." ), @@ -553,54 +682,59 @@ // Internal attributes, Testing: // ========================================================================== - rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), - rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)), - rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word)), - rustc_attr!(TEST, rustc_variance, Normal, template!(Word)), - rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")), - rustc_attr!(TEST, rustc_regions, Normal, template!(Word)), + rustc_attr!(TEST, rustc_outlives, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_strict_coherence, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_variance, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ..."), WarnFollowing), + rustc_attr!(TEST, rustc_regions, Normal, template!(Word), WarnFollowing), rustc_attr!( TEST, rustc_error, Normal, - template!(Word, List: "delay_span_bug_from_inside_query") + template!(Word, List: "delay_span_bug_from_inside_query"), WarnFollowingWordOnly ), - rustc_attr!(TEST, rustc_dump_user_substs, Normal, template!(Word)), - rustc_attr!(TEST, rustc_evaluate_where_clauses, Normal, template!(Word)), - rustc_attr!(TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode")), - rustc_attr!(TEST, rustc_then_this_would_need, Normal, template!(List: "DepNode")), + rustc_attr!(TEST, rustc_dump_user_substs, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_evaluate_where_clauses, Normal, template!(Word), WarnFollowing), + rustc_attr!( + TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode"), DuplicatesOk + ), + rustc_attr!( + TEST, rustc_then_this_would_need, Normal, template!(List: "DepNode"), DuplicatesOk + ), rustc_attr!( TEST, rustc_clean, Normal, template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#), + DuplicatesOk, ), rustc_attr!( TEST, rustc_partition_reused, Normal, - template!(List: r#"cfg = "...", module = "...""#), + template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, ), rustc_attr!( TEST, rustc_partition_codegened, Normal, - template!(List: r#"cfg = "...", module = "...""#), + template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, ), rustc_attr!( TEST, rustc_expected_cgu_reuse, Normal, - template!(List: r#"cfg = "...", module = "...", kind = "...""#), + template!(List: r#"cfg = "...", module = "...", kind = "...""#), DuplicatesOk, ), - rustc_attr!(TEST, rustc_synthetic, Normal, template!(Word)), - rustc_attr!(TEST, rustc_symbol_name, Normal, template!(Word)), - rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word)), - rustc_attr!(TEST, rustc_def_path, Normal, template!(Word)), - rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ...")), - rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word)), - rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word)), - rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word)), - rustc_attr!(TEST, rustc_dump_vtable, Normal, template!(Word)), - rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/)), + rustc_attr!(TEST, rustc_symbol_name, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk), + rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_dump_vtable, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), DuplicatesOk), gated!( - omit_gdb_pretty_printer_section, Normal, template!(Word), + omit_gdb_pretty_printer_section, Normal, template!(Word), WarnFollowing, "the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite", ), ]; pub fn deprecated_attributes() -> Vec<&'static BuiltinAttribute> { - BUILTIN_ATTRIBUTES.iter().filter(|(.., gate)| gate.is_deprecated()).collect() + BUILTIN_ATTRIBUTES.iter().filter(|attr| attr.gate.is_deprecated()).collect() } pub fn is_builtin_attr_name(name: Symbol) -> bool { @@ -611,8 +745,8 @@ SyncLazy::new(|| { let mut map = FxHashMap::default(); for attr in BUILTIN_ATTRIBUTES.iter() { - if map.insert(attr.0, attr).is_some() { - panic!("duplicate builtin attribute `{}`", attr.0); + if map.insert(attr.name, attr).is_some() { + panic!("duplicate builtin attribute `{}`", attr.name); } } map
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index b25aab2..bfc537c 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs
@@ -11,6 +11,7 @@ //! even if it is stabilized or removed, *do not remove it*. Instead, move the //! symbol to the `accepted` or `removed` modules respectively. +#![feature(derive_default_enum)] #![feature(once_cell)] mod accepted; @@ -146,6 +147,7 @@ pub use accepted::ACCEPTED_FEATURES; pub use active::{Features, ACTIVE_FEATURES, INCOMPATIBLE_FEATURES}; +pub use builtin_attrs::AttributeDuplicates; pub use builtin_attrs::{ deprecated_attributes, find_gated_cfg, is_builtin_attr_name, AttributeGate, AttributeTemplate, AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 7b9b682..b9f3b5a 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs
@@ -45,132 +45,136 @@ // feature-group-start: removed features // ------------------------------------------------------------------------- - (removed, import_shadowing, "1.0.0", None, None, None), - (removed, managed_boxes, "1.0.0", None, None, None), - /// Allows use of unary negate on unsigned integers, e.g., -e for e: u8 - (removed, negate_unsigned, "1.0.0", Some(29645), None, None), - (removed, reflect, "1.0.0", Some(27749), None, None), - /// A way to temporarily opt out of opt in copy. This will *never* be accepted. - (removed, opt_out_copy, "1.0.0", None, None, None), - (removed, quad_precision_float, "1.0.0", None, None, None), - (removed, struct_inherit, "1.0.0", None, None, None), - (removed, test_removed_feature, "1.0.0", None, None, None), - (removed, visible_private_types, "1.0.0", None, None, None), - (removed, unsafe_no_drop_flag, "1.0.0", None, None, None), - /// Allows using items which are missing stability attributes - (removed, unmarked_api, "1.0.0", None, None, None), - (removed, allocator, "1.0.0", None, None, None), - (removed, simd, "1.0.0", Some(27731), None, - Some("removed in favor of `#[repr(simd)]`")), (removed, advanced_slice_patterns, "1.0.0", Some(62254), None, Some("merged into `#![feature(slice_patterns)]`")), - (removed, macro_reexport, "1.0.0", Some(29638), None, - Some("subsumed by `pub use`")), - /// Allows using custom attributes (RFC 572). - (removed, custom_attribute, "1.0.0", Some(29642), None, - Some("removed in favor of `#![register_tool]` and `#![register_attr]`")), - /// Allows features specific to OIBIT (now called auto traits). - /// Renamed to `auto_traits`. - (removed, optin_builtin_traits, "1.0.0", Some(13231), None, - Some("renamed to `auto_traits`")), - (removed, pushpop_unsafe, "1.2.0", None, None, None), - (removed, needs_allocator, "1.4.0", Some(27389), None, - Some("subsumed by `#![feature(allocator_internals)]`")), - /// Allows identifying crates that contain sanitizer runtimes. - (removed, sanitizer_runtime, "1.17.0", None, None, None), - /// Allows `#[doc(spotlight)]`. - /// The attribute was renamed to `#[doc(notable_trait)]` - /// and the feature to `doc_notable_trait`. - (removed, doc_spotlight, "1.22.0", Some(45040), None, - Some("renamed to `doc_notable_trait`")), - (removed, proc_macro_mod, "1.27.0", Some(54727), None, - Some("subsumed by `#![feature(proc_macro_hygiene)]`")), - (removed, proc_macro_expr, "1.27.0", Some(54727), None, - Some("subsumed by `#![feature(proc_macro_hygiene)]`")), - (removed, proc_macro_non_items, "1.27.0", Some(54727), None, - Some("subsumed by `#![feature(proc_macro_hygiene)]`")), - (removed, proc_macro_gen, "1.27.0", Some(54727), None, - Some("subsumed by `#![feature(proc_macro_hygiene)]`")), - (removed, panic_implementation, "1.28.0", Some(44489), None, - Some("subsumed by `#[panic_handler]`")), - /// Allows the use of `#[derive(Anything)]` as sugar for `#[derive_Anything]`. - (removed, custom_derive, "1.32.0", Some(29644), None, - Some("subsumed by `#[proc_macro_derive]`")), - /// Paths of the form: `extern::foo::bar` - (removed, extern_in_paths, "1.33.0", Some(55600), None, - Some("subsumed by `::foo::bar` paths")), - (removed, quote, "1.33.0", Some(29601), None, None), + (removed, allocator, "1.0.0", None, None, None), + (removed, await_macro, "1.38.0", Some(50547), None, + Some("subsumed by `.await` syntax")), + /// Allows comparing raw pointers during const eval. + (removed, const_compare_raw_pointers, "1.46.0", Some(53020), None, + Some("cannot be allowed in const eval in any meaningful way")), + /// Allows non-trivial generic constants which have to be manually propagated upwards. + (removed, const_evaluatable_checked, "1.48.0", Some(76560), None, Some("renamed to `generic_const_exprs`")), + /// Allows the definition of `const` functions with some advanced features. + (removed, const_fn, "1.54.0", Some(57563), None, + Some("split into finer-grained feature gates")), /// Allows const generic types (e.g. `struct Foo<const N: usize>(...);`). (removed, const_generics, "1.34.0", Some(44580), None, Some("removed in favor of `#![feature(adt_const_params)]` and `#![feature(generic_const_exprs)]`")), /// Allows `[x; N]` where `x` is a constant (RFC 2203). (removed, const_in_array_repeat_expressions, "1.37.0", Some(49147), None, Some("removed due to causing promotable bugs")), + /// Allows casting raw pointers to `usize` during const eval. + (removed, const_raw_ptr_to_usize_cast, "1.55.0", Some(51910), None, + Some("at compile-time, pointers do not have an integer value, so these casts cannot be properly supported")), + /// Allows `T: ?const Trait` syntax in bounds. + (removed, const_trait_bound_opt_out, "1.42.0", Some(67794), None, + Some("Removed in favor of `~const` bound in #![feature(const_trait_impl)]")), + /// Allows using custom attributes (RFC 572). + (removed, custom_attribute, "1.0.0", Some(29642), None, + Some("removed in favor of `#![register_tool]` and `#![register_attr]`")), + /// Allows the use of `#[derive(Anything)]` as sugar for `#[derive_Anything]`. + (removed, custom_derive, "1.32.0", Some(29644), None, + Some("subsumed by `#[proc_macro_derive]`")), + /// Allows using `#[doc(keyword = "...")]`. + (removed, doc_keyword, "1.28.0", Some(51315), None, + Some("merged into `#![feature(rustdoc_internals)]`")), + /// Allows using `doc(primitive)` without a future-incompat warning. + (removed, doc_primitive, "1.56.0", Some(88070), None, + Some("merged into `#![feature(rustdoc_internals)]`")), + /// Allows `#[doc(spotlight)]`. + /// The attribute was renamed to `#[doc(notable_trait)]` + /// and the feature to `doc_notable_trait`. + (removed, doc_spotlight, "1.22.0", Some(45040), None, + Some("renamed to `doc_notable_trait`")), /// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238). (removed, dropck_parametricity, "1.38.0", Some(28498), None, None), - (removed, await_macro, "1.38.0", Some(50547), None, - Some("subsumed by `.await` syntax")), /// Allows defining `existential type`s. (removed, existential_type, "1.38.0", Some(63063), None, Some("removed in favor of `#![feature(type_alias_impl_trait)]`")), + /// Paths of the form: `extern::foo::bar` + (removed, extern_in_paths, "1.33.0", Some(55600), None, + Some("subsumed by `::foo::bar` paths")), + /// Allows `#[doc(include = "some-file")]`. + (removed, external_doc, "1.54.0", Some(44732), None, + Some("use #[doc = include_str!(\"filename\")] instead, which handles macro invocations")), + /// Allows `impl Trait` in bindings (`let`, `const`, `static`). + (removed, impl_trait_in_bindings, "1.55.0", Some(63065), None, + Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")), + (removed, import_shadowing, "1.0.0", None, None, None), + /// Lazily evaluate constants. This allows constants to depend on type parameters. + (removed, lazy_normalization_consts, "1.46.0", Some(72219), None, Some("superseded by `generic_const_exprs`")), + /// Allows using the `#[link_args]` attribute. + (removed, link_args, "1.53.0", Some(29596), None, + Some("removed in favor of using `-C link-arg=ARG` on command line, \ + which is available from cargo build scripts with `cargo:rustc-link-arg` now")), + (removed, macro_reexport, "1.0.0", Some(29638), None, + Some("subsumed by `pub use`")), + /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls. + (removed, main, "1.53.0", Some(29634), None, None), + (removed, managed_boxes, "1.0.0", None, None, None), + /// Allows the use of type alias impl trait in function return positions + (removed, min_type_alias_impl_trait, "1.56.0", Some(63063), None, + Some("removed in favor of full type_alias_impl_trait")), + (removed, needs_allocator, "1.4.0", Some(27389), None, + Some("subsumed by `#![feature(allocator_internals)]`")), + /// Allows use of unary negate on unsigned integers, e.g., -e for e: u8 + (removed, negate_unsigned, "1.0.0", Some(29645), None, None), + /// Allows `#[no_debug]`. + (removed, no_debug, "1.43.0", Some(29721), None, Some("removed due to lack of demand")), + /// Allows using `#[on_unimplemented(..)]` on traits. + /// (Moved to `rustc_attrs`.) + (removed, on_unimplemented, "1.40.0", None, None, None), + /// A way to temporarily opt out of opt in copy. This will *never* be accepted. + (removed, opt_out_copy, "1.0.0", None, None, None), + /// Allows features specific to OIBIT (now called auto traits). + /// Renamed to `auto_traits`. + (removed, optin_builtin_traits, "1.0.0", Some(13231), None, + Some("renamed to `auto_traits`")), + /// Allows overlapping impls of marker traits. + (removed, overlapping_marker_traits, "1.42.0", Some(29864), None, + Some("removed in favor of `#![feature(marker_trait_attr)]`")), + (removed, panic_implementation, "1.28.0", Some(44489), None, + Some("subsumed by `#[panic_handler]`")), + /// Allows using `#[plugin_registrar]` on functions. + (removed, plugin_registrar, "1.54.0", Some(29597), None, + Some("a __rustc_plugin_registrar symbol must now be defined instead")), + (removed, proc_macro_expr, "1.27.0", Some(54727), None, + Some("subsumed by `#![feature(proc_macro_hygiene)]`")), + (removed, proc_macro_gen, "1.27.0", Some(54727), None, + Some("subsumed by `#![feature(proc_macro_hygiene)]`")), + (removed, proc_macro_mod, "1.27.0", Some(54727), None, + Some("subsumed by `#![feature(proc_macro_hygiene)]`")), + (removed, proc_macro_non_items, "1.27.0", Some(54727), None, + Some("subsumed by `#![feature(proc_macro_hygiene)]`")), + (removed, pub_macro_rules, "1.53.0", Some(78855), None, + Some("removed due to being incomplete, in particular it does not work across crates")), + (removed, pushpop_unsafe, "1.2.0", None, None, None), + (removed, quad_precision_float, "1.0.0", None, None, None), + (removed, quote, "1.33.0", Some(29601), None, None), + (removed, reflect, "1.0.0", Some(27749), None, None), /// Allows using the macros: /// + `__diagnostic_used` /// + `__register_diagnostic` /// +`__build_diagnostic_array` (removed, rustc_diagnostic_macros, "1.38.0", None, None, None), - /// Allows using `#[on_unimplemented(..)]` on traits. - /// (Moved to `rustc_attrs`.) - (removed, on_unimplemented, "1.40.0", None, None, None), - /// Allows overlapping impls of marker traits. - (removed, overlapping_marker_traits, "1.42.0", Some(29864), None, - Some("removed in favor of `#![feature(marker_trait_attr)]`")), - /// Allows `T: ?const Trait` syntax in bounds. - (removed, const_trait_bound_opt_out, "1.42.0", Some(67794), None, - Some("Removed in favor of `~const` bound in #![feature(const_trait_impl)]")), - /// Allows `#[no_debug]`. - (removed, no_debug, "1.43.0", Some(29721), None, Some("removed due to lack of demand")), - /// Lazily evaluate constants. This allows constants to depend on type parameters. - (removed, lazy_normalization_consts, "1.46.0", Some(72219), None, Some("superseded by `generic_const_exprs`")), - /// Allows comparing raw pointers during const eval. - (removed, const_compare_raw_pointers, "1.46.0", Some(53020), None, - Some("cannot be allowed in const eval in any meaningful way")), - /// Allows non-trivial generic constants which have to be manually propagated upwards. - (removed, const_evaluatable_checked, "1.48.0", Some(76560), None, Some("renamed to `generic_const_exprs`")), - /// Allows using the `#[link_args]` attribute. - (removed, link_args, "1.53.0", Some(29596), None, - Some("removed in favor of using `-C link-arg=ARG` on command line, \ - which is available from cargo build scripts with `cargo:rustc-link-arg` now")), - /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls. - (removed, main, "1.53.0", Some(29634), None, None), - (removed, pub_macro_rules, "1.53.0", Some(78855), None, - Some("removed due to being incomplete, in particular it does not work across crates")), - /// Allows the definition of `const` functions with some advanced features. - (removed, const_fn, "1.54.0", Some(57563), None, - Some("split into finer-grained feature gates")), - /// Allows using `#[plugin_registrar]` on functions. - (removed, plugin_registrar, "1.54.0", Some(29597), None, - Some("a __rustc_plugin_registrar symbol must now be defined instead")), - - /// Allows `#[doc(include = "some-file")]`. - (removed, external_doc, "1.54.0", Some(44732), None, - Some("use #[doc = include_str!(\"filename\")] instead, which handles macro invocations")), - - /// Allows casting raw pointers to `usize` during const eval. - (removed, const_raw_ptr_to_usize_cast, "1.55.0", Some(51910), None, - Some("at compile-time, pointers do not have an integer value, so these casts cannot be properly supported")), - - /// Allows `impl Trait` in bindings (`let`, `const`, `static`). - (removed, impl_trait_in_bindings, "1.55.0", Some(63065), None, - Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")), - - /// Allows the use of type alias impl trait in function return positions - (removed, min_type_alias_impl_trait, "1.56.0", Some(63063), None, - Some("removed in favor of full type_alias_impl_trait")), - + /// Allows identifying crates that contain sanitizer runtimes. + (removed, sanitizer_runtime, "1.17.0", None, None, None), + (removed, simd, "1.0.0", Some(27731), None, + Some("removed in favor of `#[repr(simd)]`")), + (removed, struct_inherit, "1.0.0", None, None, None), + (removed, test_removed_feature, "1.0.0", None, None, None), + /// Allows using items which are missing stability attributes + (removed, unmarked_api, "1.0.0", None, None, None), + (removed, unsafe_no_drop_flag, "1.0.0", None, None, None), /// Allows `#[unwind(..)]`. /// /// Permits specifying whether a function should permit unwinding or abort on unwind. (removed, unwind_attributes, "1.56.0", Some(58760), None, Some("use the C-unwind ABI instead")), + (removed, visible_private_types, "1.0.0", None, None, None), + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! + // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. + // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // ------------------------------------------------------------------------- // feature-group-end: removed features
diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index e69289b..27390fd 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs
@@ -512,7 +512,7 @@ pub fn to_dot_string(&self) -> String { match *self { LabelStr(ref s) => format!("\"{}\"", s.escape_default()), - EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(s)), + EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s)), HtmlStr(ref s) => format!("<{}>", s), } }
diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index f07e52e..f19ca49 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs
@@ -1,55 +1,52 @@ -/// This declares a list of types which can be allocated by `Arena`. -/// -/// The `few` modifier will cause allocation to use the shared arena and recording the destructor. -/// This is faster and more memory efficient if there's only a few allocations of the type. -/// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is -/// faster and more memory efficient if there is lots of allocations. +/// This higher-order macro declares a list of types which can be allocated by `Arena`. /// /// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]`, /// where `T` is the type listed. These impls will appear in the implement_ty_decoder! macro. #[macro_export] macro_rules! arena_types { - ($macro:path, $tcx:lifetime) => ( + ($macro:path) => ( $macro!([ // HIR types - [few] hir_krate: rustc_hir::Crate<$tcx>, - [] arm: rustc_hir::Arm<$tcx>, - [] asm_operand: (rustc_hir::InlineAsmOperand<$tcx>, Span), + [] hir_krate: rustc_hir::Crate<'tcx>, + [] arm: rustc_hir::Arm<'tcx>, + [] asm_operand: (rustc_hir::InlineAsmOperand<'tcx>, Span), [] asm_template: rustc_ast::InlineAsmTemplatePiece, [] attribute: rustc_ast::Attribute, - [] block: rustc_hir::Block<$tcx>, - [] bare_fn_ty: rustc_hir::BareFnTy<$tcx>, - [] generic_arg: rustc_hir::GenericArg<$tcx>, - [] generic_args: rustc_hir::GenericArgs<$tcx>, - [] generic_bound: rustc_hir::GenericBound<$tcx>, - [] generic_param: rustc_hir::GenericParam<$tcx>, - [] expr: rustc_hir::Expr<$tcx>, - [] expr_field: rustc_hir::ExprField<$tcx>, - [] pat_field: rustc_hir::PatField<$tcx>, - [] fn_decl: rustc_hir::FnDecl<$tcx>, - [] foreign_item: rustc_hir::ForeignItem<$tcx>, - [few] foreign_item_ref: rustc_hir::ForeignItemRef, - [] impl_item: rustc_hir::ImplItem<$tcx>, + [] block: rustc_hir::Block<'tcx>, + [] bare_fn_ty: rustc_hir::BareFnTy<'tcx>, + [] body: rustc_hir::Body<'tcx>, + [] generic_arg: rustc_hir::GenericArg<'tcx>, + [] generic_args: rustc_hir::GenericArgs<'tcx>, + [] generic_bound: rustc_hir::GenericBound<'tcx>, + [] generic_param: rustc_hir::GenericParam<'tcx>, + [] expr: rustc_hir::Expr<'tcx>, + [] expr_field: rustc_hir::ExprField<'tcx>, + [] pat_field: rustc_hir::PatField<'tcx>, + [] fn_decl: rustc_hir::FnDecl<'tcx>, + [] foreign_item: rustc_hir::ForeignItem<'tcx>, + [] foreign_item_ref: rustc_hir::ForeignItemRef, + [] impl_item: rustc_hir::ImplItem<'tcx>, [] impl_item_ref: rustc_hir::ImplItemRef, - [] item: rustc_hir::Item<$tcx>, - [few] inline_asm: rustc_hir::InlineAsm<$tcx>, - [few] llvm_inline_asm: rustc_hir::LlvmInlineAsm<$tcx>, - [] local: rustc_hir::Local<$tcx>, - [few] mod_: rustc_hir::Mod<$tcx>, - [] param: rustc_hir::Param<$tcx>, - [] pat: rustc_hir::Pat<$tcx>, - [] path: rustc_hir::Path<$tcx>, - [] path_segment: rustc_hir::PathSegment<$tcx>, - [] poly_trait_ref: rustc_hir::PolyTraitRef<$tcx>, - [] qpath: rustc_hir::QPath<$tcx>, - [] stmt: rustc_hir::Stmt<$tcx>, - [] field_def: rustc_hir::FieldDef<$tcx>, - [] trait_item: rustc_hir::TraitItem<$tcx>, + [] item: rustc_hir::Item<'tcx>, + [] inline_asm: rustc_hir::InlineAsm<'tcx>, + [] llvm_inline_asm: rustc_hir::LlvmInlineAsm<'tcx>, + [] local: rustc_hir::Local<'tcx>, + [] mod_: rustc_hir::Mod<'tcx>, + [] owner_info: rustc_hir::OwnerInfo<'tcx>, + [] param: rustc_hir::Param<'tcx>, + [] pat: rustc_hir::Pat<'tcx>, + [] path: rustc_hir::Path<'tcx>, + [] path_segment: rustc_hir::PathSegment<'tcx>, + [] poly_trait_ref: rustc_hir::PolyTraitRef<'tcx>, + [] qpath: rustc_hir::QPath<'tcx>, + [] stmt: rustc_hir::Stmt<'tcx>, + [] field_def: rustc_hir::FieldDef<'tcx>, + [] trait_item: rustc_hir::TraitItem<'tcx>, [] trait_item_ref: rustc_hir::TraitItemRef, - [] ty: rustc_hir::Ty<$tcx>, - [] type_binding: rustc_hir::TypeBinding<$tcx>, - [] variant: rustc_hir::Variant<$tcx>, - [] where_predicate: rustc_hir::WherePredicate<$tcx>, - ], $tcx); + [] ty: rustc_hir::Ty<'tcx>, + [] type_binding: rustc_hir::TypeBinding<'tcx>, + [] variant: rustc_hir::Variant<'tcx>, + [] where_predicate: rustc_hir::WherePredicate<'tcx>, + ]); ) }
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index cb668eb..60761a0 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs
@@ -104,8 +104,10 @@ Use, /// An `extern` block. ForeignMod, - /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`, or `const { 1 + 2}` + /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]` AnonConst, + /// An inline constant, e.g. `const { 1 + 2 }` + InlineConst, /// Opaque type, aka `impl Trait`. OpaqueTy, Field, @@ -155,6 +157,7 @@ DefKind::Use => "import", DefKind::ForeignMod => "foreign module", DefKind::AnonConst => "constant expression", + DefKind::InlineConst => "inline constant", DefKind::Field => "field", DefKind::Impl => "implementation", DefKind::Closure => "closure", @@ -174,6 +177,7 @@ | DefKind::OpaqueTy | DefKind::Impl | DefKind::Use + | DefKind::InlineConst | DefKind::ExternCrate => "an", DefKind::Macro(macro_kind) => macro_kind.article(), _ => "a", @@ -207,6 +211,7 @@ // Not namespaced. DefKind::AnonConst + | DefKind::InlineConst | DefKind::Field | DefKind::LifetimeParam | DefKind::ExternCrate
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 11d0178..c67d3df 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs
@@ -1,6 +1,7 @@ use crate::def::{CtorKind, DefKind, Res}; -use crate::def_id::{DefId, CRATE_DEF_ID}; +use crate::def_id::DefId; crate use crate::hir_id::{HirId, ItemLocalId}; +use crate::intravisit::FnKind; use crate::LangItem; use rustc_ast::util::parser::ExprPrecedence; @@ -9,7 +10,9 @@ pub use rustc_ast::{BorrowKind, ImplPolarity, IsAuto}; pub use rustc_ast::{CaptureBy, Movability, Mutability}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sorted_map::SortedMap; use rustc_index::vec::IndexVec; use rustc_macros::HashStable_Generic; use rustc_span::source_map::Spanned; @@ -20,7 +23,6 @@ use rustc_target::spec::abi::Abi; use smallvec::SmallVec; -use std::collections::BTreeMap; use std::fmt; #[derive(Copy, Clone, Encodable, HashStable_Generic)] @@ -121,7 +123,7 @@ match *self { LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Implicit - | LifetimeName::Error => Ident::invalid(), + | LifetimeName::Error => Ident::empty(), LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime), LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime), LifetimeName::Param(param_name) => param_name.ident(), @@ -233,7 +235,7 @@ } pub fn invalid() -> Self { - Self::from_ident(Ident::invalid()) + Self::from_ident(Ident::empty()) } pub fn args(&self) -> &GenericArgs<'hir> { @@ -310,7 +312,7 @@ } pub fn is_synthetic(&self) -> bool { - matches!(self, GenericArg::Lifetime(lifetime) if lifetime.name.ident() == Ident::invalid()) + matches!(self, GenericArg::Lifetime(lifetime) if lifetime.name.ident() == Ident::empty()) } pub fn descr(&self) -> &'static str { @@ -502,7 +504,7 @@ }, Type { default: Option<&'hir Ty<'hir>>, - synthetic: Option<SyntheticTyParamKind>, + synthetic: bool, }, Const { ty: &'hir Ty<'hir>, @@ -575,16 +577,6 @@ } } -/// Synthetic type parameters are converted to another form during lowering; this allows -/// us to track the original form they had, and is useful for error messages. -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] -#[derive(HashStable_Generic)] -pub enum SyntheticTyParamKind { - ImplTrait, - // Created by the `#[rustc_synthetic]` attribute. - FromAttr, -} - /// A where-clause in a definition. #[derive(Debug, HashStable_Generic)] pub struct WhereClause<'hir> { @@ -645,6 +637,22 @@ pub bounds: GenericBounds<'hir>, } +impl WhereBoundPredicate<'hir> { + /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate. + pub fn is_param_bound(&self, param_def_id: DefId) -> bool { + let path = match self.bounded_ty.kind { + TyKind::Path(QPath::Resolved(None, path)) => path, + _ => return false, + }; + match path.res { + Res::Def(DefKind::TyParam, def_id) | Res::SelfTy(Some(def_id), None) => { + def_id == param_def_id + } + _ => false, + } + } +} + /// A lifetime predicate (e.g., `'a: 'b + 'c`). #[derive(Debug, HashStable_Generic)] pub struct WhereRegionPredicate<'hir> { @@ -662,6 +670,74 @@ pub rhs_ty: &'hir Ty<'hir>, } +/// HIR node coupled with its parent's id in the same HIR owner. +/// +/// The parent is trash when the node is a HIR owner. +#[derive(Clone, Debug)] +pub struct ParentedNode<'tcx> { + pub parent: ItemLocalId, + pub node: Node<'tcx>, +} + +/// Attributes owned by a HIR owner. +#[derive(Debug)] +pub struct AttributeMap<'tcx> { + pub map: SortedMap<ItemLocalId, &'tcx [Attribute]>, + pub hash: Fingerprint, +} + +impl<'tcx> AttributeMap<'tcx> { + pub const EMPTY: &'static AttributeMap<'static> = + &AttributeMap { map: SortedMap::new(), hash: Fingerprint::ZERO }; + + #[inline] + pub fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] { + self.map.get(&id).copied().unwrap_or(&[]) + } +} + +/// Map of all HIR nodes inside the current owner. +/// These nodes are mapped by `ItemLocalId` alongside the index of their parent node. +/// The HIR tree, including bodies, is pre-hashed. +#[derive(Debug)] +pub struct OwnerNodes<'tcx> { + /// Pre-computed hash of the full HIR. + pub hash_including_bodies: Fingerprint, + /// Pre-computed hash of the item signature, sithout recursing into the body. + pub hash_without_bodies: Fingerprint, + /// Full HIR for the current owner. + // The zeroth node's parent should never be accessed: the owner's parent is computed by the + // hir_owner_parent query. It is set to `ItemLocalId::INVALID` to force an ICE if accidentally + // used. + pub nodes: IndexVec<ItemLocalId, Option<ParentedNode<'tcx>>>, + /// Content of local bodies. + pub bodies: SortedMap<ItemLocalId, &'tcx Body<'tcx>>, +} + +/// Full information resulting from lowering an AST node. +#[derive(Debug, HashStable_Generic)] +pub struct OwnerInfo<'hir> { + /// Contents of the HIR. + pub nodes: OwnerNodes<'hir>, + /// Map from each nested owner to its parent's local id. + pub parenting: FxHashMap<LocalDefId, ItemLocalId>, + /// Collected attributes of the HIR nodes. + pub attrs: AttributeMap<'hir>, + /// Map indicating what traits are in scope for places where this + /// is relevant; generated by resolve. + pub trait_map: FxHashMap<ItemLocalId, Box<[TraitCandidate]>>, +} + +impl<'tcx> OwnerInfo<'tcx> { + #[inline] + pub fn node(&self) -> OwnerNode<'tcx> { + use rustc_index::vec::Idx; + let node = self.nodes.nodes[ItemLocalId::new(0)].as_ref().unwrap().node; + let node = node.as_owner().unwrap(); // Indexing must ensure it is an OwnerNode. + node + } +} + /// The top-level data structure that stores the entire contents of /// the crate currently being compiled. /// @@ -670,41 +746,8 @@ /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html #[derive(Debug)] pub struct Crate<'hir> { - pub owners: IndexVec<LocalDefId, Option<OwnerNode<'hir>>>, - pub bodies: BTreeMap<BodyId, Body<'hir>>, - - /// Map indicating what traits are in scope for places where this - /// is relevant; generated by resolve. - pub trait_map: FxHashMap<LocalDefId, FxHashMap<ItemLocalId, Box<[TraitCandidate]>>>, - - /// Collected attributes from HIR nodes. - pub attrs: BTreeMap<HirId, &'hir [Attribute]>, -} - -impl Crate<'hir> { - pub fn module(&self) -> &'hir Mod<'hir> { - if let Some(OwnerNode::Crate(m)) = self.owners[CRATE_DEF_ID] { m } else { panic!() } - } - - pub fn item(&self, id: ItemId) -> &'hir Item<'hir> { - self.owners[id.def_id].as_ref().unwrap().expect_item() - } - - pub fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { - self.owners[id.def_id].as_ref().unwrap().expect_trait_item() - } - - pub fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { - self.owners[id.def_id].as_ref().unwrap().expect_impl_item() - } - - pub fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir> { - self.owners[id.def_id].as_ref().unwrap().expect_foreign_item() - } - - pub fn body(&self, id: BodyId) -> &Body<'hir> { - &self.bodies[&id] - } + pub owners: IndexVec<LocalDefId, Option<OwnerInfo<'hir>>>, + pub hir_hash: Fingerprint, } /// A block of statements `{ .. }`, which may have a label (in this case the @@ -1778,8 +1821,6 @@ pub enum LocalSource { /// A `match _ { .. }`. Normal, - /// A desugared `for _ in _ { .. }` loop. - ForLoopDesugar, /// When lowering async functions, we create locals within the `async move` so that /// all parameters are dropped after the future is polled. /// @@ -3222,6 +3263,32 @@ _ => None, } } + + pub fn fn_kind(self) -> Option<FnKind<'hir>> { + match self { + Node::Item(i) => match i.kind { + ItemKind::Fn(ref sig, ref generics, _) => { + Some(FnKind::ItemFn(i.ident, generics, sig.header, &i.vis)) + } + _ => None, + }, + Node::TraitItem(ti) => match ti.kind { + TraitItemKind::Fn(ref sig, TraitFn::Provided(_)) => { + Some(FnKind::Method(ti.ident, sig, None)) + } + _ => None, + }, + Node::ImplItem(ii) => match ii.kind { + ImplItemKind::Fn(ref sig, _) => Some(FnKind::Method(ii.ident, sig, Some(&ii.vis))), + _ => None, + }, + Node::Expr(e) => match e.kind { + ExprKind::Closure(..) => Some(FnKind::Closure), + _ => None, + }, + _ => None, + } + } } // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index 0b25ebc..39552eb 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs
@@ -1,5 +1,4 @@ use crate::def_id::{LocalDefId, CRATE_DEF_INDEX}; -use rustc_index::vec::IndexVec; use std::fmt; /// Uniquely identifies a node in the HIR of the current crate. It is @@ -56,76 +55,13 @@ pub struct ItemLocalId { .. } } rustc_data_structures::impl_stable_hash_via_hash!(ItemLocalId); +impl ItemLocalId { + /// Signal local id which should never be used. + pub const INVALID: ItemLocalId = ItemLocalId::MAX; +} /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_INDEX`. pub const CRATE_HIR_ID: HirId = HirId { owner: LocalDefId { local_def_index: CRATE_DEF_INDEX }, local_id: ItemLocalId::from_u32(0), }; - -/// N.B. This collection is currently unused, but will be used by #72015 and future PRs. -#[derive(Clone, Default, Debug, Encodable, Decodable)] -pub struct HirIdVec<T> { - map: IndexVec<LocalDefId, IndexVec<ItemLocalId, T>>, -} - -impl<T> HirIdVec<T> { - pub fn push_owner(&mut self, id: LocalDefId) { - self.map.ensure_contains_elem(id, IndexVec::new); - } - - pub fn push(&mut self, id: HirId, value: T) { - if id.local_id == ItemLocalId::from_u32(0) { - self.push_owner(id.owner); - } - let submap = &mut self.map[id.owner]; - let _ret_id = submap.push(value); - debug_assert_eq!(_ret_id, id.local_id); - } - - pub fn push_sparse(&mut self, id: HirId, value: T) - where - T: Default, - { - self.map.ensure_contains_elem(id.owner, IndexVec::new); - let submap = &mut self.map[id.owner]; - let i = id.local_id.index(); - let len = submap.len(); - if i >= len { - submap.extend(std::iter::repeat_with(T::default).take(i - len + 1)); - } - submap[id.local_id] = value; - } - - pub fn get(&self, id: HirId) -> Option<&T> { - self.map.get(id.owner)?.get(id.local_id) - } - - pub fn get_owner(&self, id: LocalDefId) -> &IndexVec<ItemLocalId, T> { - &self.map[id] - } - - pub fn iter(&self) -> impl Iterator<Item = &T> { - self.map.iter().flat_map(|la| la.iter()) - } - - pub fn iter_enumerated(&self) -> impl Iterator<Item = (HirId, &T)> { - self.map.iter_enumerated().flat_map(|(owner, la)| { - la.iter_enumerated().map(move |(local_id, attr)| (HirId { owner, local_id }, attr)) - }) - } -} - -impl<T> std::ops::Index<HirId> for HirIdVec<T> { - type Output = T; - - fn index(&self, id: HirId) -> &T { - &self.map[id.owner][id.local_id] - } -} - -impl<T> std::ops::IndexMut<HirId> for HirIdVec<T> { - fn index_mut(&mut self, id: HirId) -> &mut T { - &mut self.map[id.owner][id.local_id] - } -}
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 1ac2625..cff5437 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs
@@ -117,6 +117,14 @@ FnKind::Closure => None, } } + + pub fn constness(self) -> Constness { + self.header().map_or(Constness::NotConst, |header| header.constness) + } + + pub fn asyncness(self) -> IsAsync { + self.header().map_or(IsAsync::NotAsync, |header| header.asyncness) + } } /// An abstract representation of the HIR `rustc_middle::hir::map::Map`. @@ -130,6 +138,28 @@ fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir>; } +// Used when no map is actually available, forcing manual implementation of nested visitors. +impl Map<'hir> for ! { + fn find(&self, _: HirId) -> Option<Node<'hir>> { + unreachable!() + } + fn body(&self, _: BodyId) -> &'hir Body<'hir> { + unreachable!() + } + fn item(&self, _: ItemId) -> &'hir Item<'hir> { + unreachable!() + } + fn trait_item(&self, _: TraitItemId) -> &'hir TraitItem<'hir> { + unreachable!() + } + fn impl_item(&self, _: ImplItemId) -> &'hir ImplItem<'hir> { + unreachable!() + } + fn foreign_item(&self, _: ForeignItemId) -> &'hir ForeignItem<'hir> { + unreachable!() + } +} + /// An erased version of `Map<'hir>`, using dynamic dispatch. /// NOTE: This type is effectively only usable with `NestedVisitorMap::None`. pub struct ErasedMap<'hir>(&'hir dyn Map<'hir>);
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index f35353d..05659e9 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs
@@ -268,6 +268,7 @@ Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None; Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1); + GeneratorReturn, sym::generator_return, generator_return, Target::AssocTy, GenericRequirement::None; Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; @@ -281,18 +282,17 @@ // in the sense that a crate is not required to have it defined to use it, but a final product // is required to define it somewhere. Additionally, there are restrictions on crates that use // a weak lang item, but do not have it defined. - Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::None; + Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0); PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None; PanicStr, sym::panic_str, panic_str, Target::Fn, GenericRequirement::None; ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; - PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::None; + PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0); PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None; PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None; PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None; /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; - BeginPanicFmt, sym::begin_panic_fmt, begin_panic_fmt, Target::Fn, GenericRequirement::None; ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index f5ea044..93224d3 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs
@@ -2,10 +2,12 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html +#![feature(const_btree_new)] #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] #![feature(once_cell)] #![feature(min_specialization)] +#![feature(never_type)] #![recursion_limit = "256"] #[macro_use]
diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index b1f78a8..b300761 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs
@@ -2,6 +2,7 @@ use crate::def_id::DefId; use crate::hir::{self, HirId, PatKind}; use rustc_data_structures::stable_set::FxHashSet; +use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::Ident; use rustc_span::Span; @@ -143,4 +144,14 @@ }); result } + + /// If the pattern is `Some(<pat>)` from a desugared for loop, returns the inner pattern + pub fn for_loop_some(&self) -> Option<&Self> { + if self.span.desugaring_kind() == Some(DesugaringKind::ForLoop) { + if let hir::PatKind::Struct(_, [pat_field], _) = self.kind { + return Some(pat_field.pat); + } + } + None + } }
diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 9d5ef27..6e7b765 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs
@@ -1,8 +1,8 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use crate::hir::{ - BodyId, Expr, ForeignItem, ForeignItemId, ImplItem, ImplItemId, Item, ItemId, Mod, TraitItem, - TraitItemId, Ty, VisibilityKind, + AttributeMap, BodyId, Crate, Expr, ForeignItem, ForeignItemId, ImplItem, ImplItemId, Item, + ItemId, Mod, OwnerNodes, TraitCandidate, TraitItem, TraitItemId, Ty, VisibilityKind, }; use crate::hir_id::{HirId, ItemLocalId}; use rustc_span::def_id::DefPathHash; @@ -21,6 +21,7 @@ fn hash_hir_ty(&mut self, _: &Ty<'_>, hasher: &mut StableHasher); fn hash_hir_visibility_kind(&mut self, _: &VisibilityKind<'_>, hasher: &mut StableHasher); fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self, f: F); + fn hash_hir_trait_candidate(&mut self, _: &TraitCandidate, hasher: &mut StableHasher); } impl<HirCtx: crate::HashStableContext> ToStableHashKey<HirCtx> for HirId { @@ -209,3 +210,35 @@ }); } } + +impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<'tcx> { + fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { + // We ignore the `nodes` and `bodies` fields since these refer to information included in + // `hash` which is hashed in the collector and used for the crate hash. + let OwnerNodes { hash_including_bodies, hash_without_bodies: _, nodes: _, bodies: _ } = + *self; + hash_including_bodies.hash_stable(hcx, hasher); + } +} + +impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap<'tcx> { + fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { + // We ignore the `map` since it refers to information included in `hash` which is hashed in + // the collector and used for the crate hash. + let AttributeMap { hash, map: _ } = *self; + hash.hash_stable(hcx, hasher); + } +} + +impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Crate<'_> { + fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { + let Crate { owners: _, hir_hash } = self; + hir_hash.hash_stable(hcx, hasher) + } +} + +impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for TraitCandidate { + fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { + hcx.hash_hir_trait_candidate(self, hasher) + } +}
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 9196344..9c29271 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -15,7 +15,6 @@ use std::borrow::Cow; use std::cell::Cell; -use std::collections::BTreeMap; use std::vec; pub fn id_to_string(map: &dyn rustc_hir::intravisit::Map<'_>, hir_id: hir::HirId) -> String { @@ -69,7 +68,7 @@ pub struct State<'a> { pub s: pp::Printer, comments: Option<Comments<'a>>, - attrs: &'a BTreeMap<hir::HirId, &'a [ast::Attribute]>, + attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute], ann: &'a (dyn PpAnn + 'a), } @@ -146,17 +145,18 @@ /// it can scan the input text for comments to copy forward. pub fn print_crate<'a>( sm: &'a SourceMap, - krate: &hir::Crate<'_>, + krate: &hir::Mod<'_>, filename: FileName, input: String, + attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute], ann: &'a dyn PpAnn, ) -> String { - let mut s = State::new_from_input(sm, filename, input, &krate.attrs, ann); + let mut s = State::new_from_input(sm, filename, input, attrs, ann); // When printing the AST, we sometimes need to inject `#[no_std]` here. // Since you can't compile the HIR, it's not necessary. - s.print_mod(&krate.module(), s.attrs(hir::CRATE_HIR_ID)); + s.print_mod(krate, (*attrs)(hir::CRATE_HIR_ID)); s.print_remaining_comments(); s.s.eof() } @@ -166,7 +166,7 @@ sm: &'a SourceMap, filename: FileName, input: String, - attrs: &'a BTreeMap<hir::HirId, &[ast::Attribute]>, + attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute], ann: &'a dyn PpAnn, ) -> State<'a> { State { @@ -178,7 +178,7 @@ } fn attrs(&self, id: hir::HirId) -> &'a [ast::Attribute] { - self.attrs.get(&id).map_or(&[], |la| *la) + (self.attrs)(id) } } @@ -186,8 +186,7 @@ where F: FnOnce(&mut State<'_>), { - let mut printer = - State { s: pp::mk_printer(), comments: None, attrs: &BTreeMap::default(), ann }; + let mut printer = State { s: pp::mk_printer(), comments: None, attrs: &|_| &[], ann }; f(&mut printer); printer.s.eof() } @@ -1168,7 +1167,7 @@ self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals()) } - // Does `expr` need parenthesis when printed in a condition position? + // Does `expr` need parentheses when printed in a condition position? // // These cases need parens due to the parse error observed in #26461: `if return {}` // parses as the erroneous construct `if (return {})`, not `if (return) {}`.
diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index d42e2f7..571337a 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs
@@ -126,30 +126,36 @@ if attr.has_name(sym::rustc_if_this_changed) { let dep_node_interned = self.argument(attr); let dep_node = match dep_node_interned { - None => DepNode::from_def_path_hash(def_path_hash, DepKind::hir_owner), - Some(n) => match DepNode::from_label_string(&n.as_str(), def_path_hash) { - Ok(n) => n, - Err(()) => { - self.tcx.sess.span_fatal( - attr.span, - &format!("unrecognized DepNode variant {:?}", n), - ); + None => { + DepNode::from_def_path_hash(self.tcx, def_path_hash, DepKind::hir_owner) + } + Some(n) => { + match DepNode::from_label_string(self.tcx, &n.as_str(), def_path_hash) { + Ok(n) => n, + Err(()) => { + self.tcx.sess.span_fatal( + attr.span, + &format!("unrecognized DepNode variant {:?}", n), + ); + } } - }, + } }; self.if_this_changed.push((attr.span, def_id.to_def_id(), dep_node)); } else if attr.has_name(sym::rustc_then_this_would_need) { let dep_node_interned = self.argument(attr); let dep_node = match dep_node_interned { - Some(n) => match DepNode::from_label_string(&n.as_str(), def_path_hash) { - Ok(n) => n, - Err(()) => { - self.tcx.sess.span_fatal( - attr.span, - &format!("unrecognized DepNode variant {:?}", n), - ); + Some(n) => { + match DepNode::from_label_string(self.tcx, &n.as_str(), def_path_hash) { + Ok(n) => n, + Err(()) => { + self.tcx.sess.span_fatal( + attr.span, + &format!("unrecognized DepNode variant {:?}", n), + ); + } } - }, + } None => { self.tcx.sess.span_fatal(attr.span, "missing DepNode variant"); }
diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index f089cbc..dd3f8c9 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs
@@ -2,6 +2,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(in_band_lifetimes)] +#![feature(let_else)] #![feature(nll)] #![recursion_limit = "256"]
diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 5528638..b2eaf61 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs
@@ -15,7 +15,7 @@ use rustc_ast::{self as ast, Attribute, NestedMetaItem}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::Node as HirNode; @@ -302,18 +302,6 @@ out } - fn dep_nodes<'l>( - &self, - labels: &'l Labels, - def_id: DefId, - ) -> impl Iterator<Item = DepNode> + 'l { - let def_path_hash = self.tcx.def_path_hash(def_id); - labels.iter().map(move |label| match DepNode::from_label_string(label, def_path_hash) { - Ok(dep_node) => dep_node, - Err(()) => unreachable!("label: {}", label), - }) - } - fn dep_node_str(&self, dep_node: &DepNode) -> String { if let Some(def_id) = dep_node.extract_def_id(self.tcx) { format!("{:?}({})", dep_node.kind, self.tcx.def_path_str(def_id)) @@ -345,16 +333,19 @@ } fn check_item(&mut self, item_id: LocalDefId, item_span: Span) { + let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id()); for attr in self.tcx.get_attrs(item_id.to_def_id()).iter() { let assertion = match self.assertion_maybe(item_id, attr) { Some(a) => a, None => continue, }; self.checked_attrs.insert(attr.id); - for dep_node in self.dep_nodes(&assertion.clean, item_id.to_def_id()) { + for label in assertion.clean { + let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); } - for dep_node in self.dep_nodes(&assertion.dirty, item_id.to_def_id()) { + for label in assertion.dirty { + let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_dirty(item_span, dep_node); } }
diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index 572a4fc..392c5bd 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs
@@ -95,6 +95,12 @@ return; } + sess.prof.artifact_size( + &name.replace(' ', "_"), + path_buf.file_name().unwrap().to_string_lossy(), + encoder.position() as u64, + ); + debug!("save: data written to disk successfully"); }
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 2ed0539..c0137fc 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -139,9 +139,6 @@ pub fn staging_dep_graph_path(sess: &Session) -> PathBuf { in_incr_comp_dir_sess(sess, STAGING_DEP_GRAPH_FILENAME) } -pub fn dep_graph_path_from(incr_comp_session_dir: &Path) -> PathBuf { - in_incr_comp_dir(incr_comp_session_dir, DEP_GRAPH_FILENAME) -} pub fn work_products_path(sess: &Session) -> PathBuf { in_incr_comp_dir_sess(sess, WORK_PRODUCTS_FILENAME) @@ -241,9 +238,7 @@ // have already tried before. let source_directory = find_source_directory(&crate_dir, &source_directories_already_tried); - let source_directory = if let Some(dir) = source_directory { - dir - } else { + let Some(source_directory) = source_directory else { // There's nowhere to copy from, we're done debug!( "no source directory found. Continuing with empty session \ @@ -397,15 +392,14 @@ // We acquire a shared lock on the lock file of the directory, so that // nobody deletes it out from under us while we are reading from it. let lock_file_path = lock_file_path(source_dir); - let _lock = if let Ok(lock) = flock::Lock::new( + + // not exclusive + let Ok(_lock) = flock::Lock::new( &lock_file_path, false, // don't wait, false, // don't create false, - ) { - // not exclusive - lock - } else { + ) else { // Could not acquire the lock, don't try to copy from here return Err(()); };
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 4d38556..9c6e2ae 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -6,6 +6,7 @@ use rustc_middle::ty::OnDiskCache; use rustc_serialize::opaque::Decoder; use rustc_serialize::Decodable; +use rustc_session::config::IncrementalStateAssertion; use rustc_session::Session; use std::path::Path; @@ -16,6 +17,7 @@ type WorkProductMap = FxHashMap<WorkProductId, WorkProduct>; +#[derive(Debug)] pub enum LoadResult<T> { Ok { data: T }, DataOutOfDate, @@ -24,6 +26,26 @@ impl<T: Default> LoadResult<T> { pub fn open(self, sess: &Session) -> T { + // Check for errors when using `-Zassert-incremental-state` + match (sess.opts.assert_incr_state, &self) { + (Some(IncrementalStateAssertion::NotLoaded), LoadResult::Ok { .. }) => { + sess.fatal( + "We asserted that the incremental cache should not be loaded, \ + but it was loaded.", + ); + } + ( + Some(IncrementalStateAssertion::Loaded), + LoadResult::Error { .. } | LoadResult::DataOutOfDate, + ) => { + sess.fatal( + "We asserted that an existing incremental cache directory should \ + be successfully loaded, but it was not.", + ); + } + _ => {} + }; + match self { LoadResult::Error { message } => { sess.warn(&message); @@ -33,7 +55,7 @@ if let Err(err) = delete_all_session_dir_contents(sess) { sess.err(&format!( "Failed to delete invalidated or incompatible \ - incremental compilation session directory contents `{}`: {}.", + incremental compilation session directory contents `{}`: {}.", dep_graph_path(sess).display(), err )); @@ -105,7 +127,7 @@ // Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`. // Fortunately, we just checked that this isn't the case. - let path = dep_graph_path_from(&sess.incr_comp_session_dir()); + let path = dep_graph_path(&sess); let report_incremental_info = sess.opts.debugging_opts.incremental_info; let expected_hash = sess.opts.dep_tracking_hash(false);
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 573124c..08f13d4 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs
@@ -4,7 +4,7 @@ use std::iter; use std::marker::PhantomData; use std::mem; -use std::ops::{BitAnd, BitAndAssign, BitOrAssign, Not, Range, Shl}; +use std::ops::{BitAnd, BitAndAssign, BitOrAssign, Bound, Not, Range, RangeBounds, Shl}; use std::slice; use rustc_macros::{Decodable, Encodable}; @@ -22,6 +22,29 @@ fn intersect(&mut self, other: &Rhs) -> bool; } +#[inline] +fn inclusive_start_end<T: Idx>( + range: impl RangeBounds<T>, + domain: usize, +) -> Option<(usize, usize)> { + // Both start and end are inclusive. + let start = match range.start_bound().cloned() { + Bound::Included(start) => start.index(), + Bound::Excluded(start) => start.index() + 1, + Bound::Unbounded => 0, + }; + let end = match range.end_bound().cloned() { + Bound::Included(end) => end.index(), + Bound::Excluded(end) => end.index().checked_sub(1)?, + Bound::Unbounded => domain - 1, + }; + assert!(end < domain); + if start > end { + return None; + } + Some((start, end)) +} + macro_rules! bit_relations_inherent_impls { () => { /// Sets `self = self | other` and returns `true` if `self` changed @@ -151,6 +174,33 @@ new_word != word } + #[inline] + pub fn insert_range(&mut self, elems: impl RangeBounds<T>) { + let Some((start, end)) = inclusive_start_end(elems, self.domain_size) else { + return; + }; + + let (start_word_index, start_mask) = word_index_and_mask(start); + let (end_word_index, end_mask) = word_index_and_mask(end); + + // Set all words in between start and end (exclusively of both). + for word_index in (start_word_index + 1)..end_word_index { + self.words[word_index] = !0; + } + + if start_word_index != end_word_index { + // Start and end are in different words, so we handle each in turn. + // + // We set all leading bits. This includes the start_mask bit. + self.words[start_word_index] |= !(start_mask - 1); + // And all trailing bits (i.e. from 0..=end) in the end word, + // including the end. + self.words[end_word_index] |= end_mask | end_mask - 1; + } else { + self.words[start_word_index] |= end_mask | (end_mask - start_mask); + } + } + /// Sets all bits to true. pub fn insert_all(&mut self) { for word in &mut self.words { @@ -227,6 +277,36 @@ not_already } + fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> { + let (start, end) = inclusive_start_end(range, self.domain_size)?; + let (start_word_index, _) = word_index_and_mask(start); + let (end_word_index, end_mask) = word_index_and_mask(end); + + let end_word = self.words[end_word_index] & (end_mask | (end_mask - 1)); + if end_word != 0 { + let pos = max_bit(end_word) + WORD_BITS * end_word_index; + if start <= pos { + return Some(T::new(pos)); + } + } + + // We exclude end_word_index from the range here, because we don't want + // to limit ourselves to *just* the last word: the bits set it in may be + // after `end`, so it may not work out. + if let Some(offset) = + self.words[start_word_index..end_word_index].iter().rposition(|&w| w != 0) + { + let word_idx = start_word_index + offset; + let start_word = self.words[word_idx]; + let pos = max_bit(start_word) + WORD_BITS * word_idx; + if start <= pos { + return Some(T::new(pos)); + } + } + + None + } + bit_relations_inherent_impls! {} } @@ -635,6 +715,16 @@ self.elems.iter() } + fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> { + let mut last_leq = None; + for e in self.iter() { + if range.contains(e) { + last_leq = Some(*e); + } + } + last_leq + } + bit_relations_inherent_impls! {} } @@ -709,6 +799,16 @@ } } + /// Returns the previous element present in the bitset from `elem`, + /// inclusively of elem. That is, will return `Some(elem)` if elem is in the + /// bitset. + pub fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> { + match self { + HybridBitSet::Sparse(sparse) => sparse.last_set_in(range), + HybridBitSet::Dense(dense) => dense.last_set_in(range), + } + } + pub fn insert(&mut self, elem: T) -> bool { // No need to check `elem` against `self.domain_size` here because all // the match cases check it, one way or another. @@ -734,6 +834,41 @@ } } + pub fn insert_range(&mut self, elems: impl RangeBounds<T>) { + // No need to check `elem` against `self.domain_size` here because all + // the match cases check it, one way or another. + let start = match elems.start_bound().cloned() { + Bound::Included(start) => start.index(), + Bound::Excluded(start) => start.index() + 1, + Bound::Unbounded => 0, + }; + let end = match elems.end_bound().cloned() { + Bound::Included(end) => end.index() + 1, + Bound::Excluded(end) => end.index(), + Bound::Unbounded => self.domain_size() - 1, + }; + let len = if let Some(l) = end.checked_sub(start) { + l + } else { + return; + }; + match self { + HybridBitSet::Sparse(sparse) if sparse.len() + len < SPARSE_MAX => { + // The set is sparse and has space for `elems`. + for elem in start..end { + sparse.insert(T::new(elem)); + } + } + HybridBitSet::Sparse(sparse) => { + // The set is sparse and full. Convert to a dense set. + let mut dense = sparse.to_dense(); + dense.insert_range(elems); + *self = HybridBitSet::Dense(dense); + } + HybridBitSet::Dense(dense) => dense.insert_range(elems), + } + } + pub fn insert_all(&mut self) { let domain_size = self.domain_size(); match self { @@ -990,8 +1125,9 @@ pub fn insert_all_into_row(&mut self, row: R) { assert!(row.index() < self.num_rows); let (start, end) = self.range(row); - for word in self.words[start..end].iter_mut() { - *word = !0; + let words = &mut self.words[..]; + for index in start..end { + words[index] = !0; } self.clear_excess_bits(row); } @@ -1143,7 +1279,7 @@ /// Iterates through all the columns set to true in a given row of /// the matrix. - pub fn iter(&self, row: R) -> impl Iterator<Item = C> + '_ { + pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a { self.row(row).into_iter().flat_map(|r| r.iter()) } @@ -1204,6 +1340,11 @@ (word_index, mask) } +#[inline] +fn max_bit(word: Word) -> usize { + WORD_BITS - 1 - word.leading_zeros() as usize +} + /// Integral type used to represent the bit set. pub trait FiniteBitSetTy: BitAnd<Output = Self>
diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index aebc6d0..e2b0730 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs
@@ -370,6 +370,101 @@ } } +#[test] +fn dense_insert_range() { + #[track_caller] + fn check<R>(domain: usize, range: R) + where + R: RangeBounds<usize> + Clone + IntoIterator<Item = usize> + std::fmt::Debug, + { + let mut set = BitSet::new_empty(domain); + set.insert_range(range.clone()); + for i in set.iter() { + assert!(range.contains(&i)); + } + for i in range.clone() { + assert!(set.contains(i), "{} in {:?}, inserted {:?}", i, set, range); + } + } + check(300, 10..10); + check(300, WORD_BITS..WORD_BITS * 2); + check(300, WORD_BITS - 1..WORD_BITS * 2); + check(300, WORD_BITS - 1..WORD_BITS); + check(300, 10..100); + check(300, 10..30); + check(300, 0..5); + check(300, 0..250); + check(300, 200..250); + + check(300, 10..=10); + check(300, WORD_BITS..=WORD_BITS * 2); + check(300, WORD_BITS - 1..=WORD_BITS * 2); + check(300, WORD_BITS - 1..=WORD_BITS); + check(300, 10..=100); + check(300, 10..=30); + check(300, 0..=5); + check(300, 0..=250); + check(300, 200..=250); + + for i in 0..WORD_BITS * 2 { + for j in i..WORD_BITS * 2 { + check(WORD_BITS * 2, i..j); + check(WORD_BITS * 2, i..=j); + check(300, i..j); + check(300, i..=j); + } + } +} + +#[test] +fn dense_last_set_before() { + fn easy(set: &BitSet<usize>, needle: impl RangeBounds<usize>) -> Option<usize> { + let mut last_leq = None; + for e in set.iter() { + if needle.contains(&e) { + last_leq = Some(e); + } + } + last_leq + } + + #[track_caller] + fn cmp(set: &BitSet<usize>, needle: impl RangeBounds<usize> + Clone + std::fmt::Debug) { + assert_eq!( + set.last_set_in(needle.clone()), + easy(set, needle.clone()), + "{:?} in {:?}", + needle, + set + ); + } + let mut set = BitSet::new_empty(300); + cmp(&set, 50..=50); + set.insert(WORD_BITS); + cmp(&set, WORD_BITS..=WORD_BITS); + set.insert(WORD_BITS - 1); + cmp(&set, 0..=WORD_BITS - 1); + cmp(&set, 0..=5); + cmp(&set, 10..100); + set.insert(100); + cmp(&set, 100..110); + cmp(&set, 99..100); + cmp(&set, 99..=100); + + for i in 0..=WORD_BITS * 2 { + for j in i..=WORD_BITS * 2 { + for k in 0..WORD_BITS * 2 { + let mut set = BitSet::new_empty(300); + cmp(&set, i..j); + cmp(&set, i..=j); + set.insert(k); + cmp(&set, i..j); + cmp(&set, i..=j); + } + } + } +} + /// Merge dense hybrid set into empty sparse hybrid set. #[bench] fn union_hybrid_sparse_empty_to_dense(b: &mut Bencher) {
diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index a72a27e..5149322 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs
@@ -3,7 +3,9 @@ #![feature(extend_one)] #![feature(iter_zip)] #![feature(min_specialization)] +#![feature(step_trait)] #![feature(test)] +#![feature(let_else)] pub mod bit_set; pub mod vec;
diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index 69578e8..55ccfd0 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs
@@ -118,38 +118,54 @@ } impl $type { + /// Maximum value the index can take, as a `u32`. $v const MAX_AS_U32: u32 = $max; + /// Maximum value the index can take. $v const MAX: Self = Self::from_u32($max); + /// Creates a new index from a given `usize`. + /// + /// # Panics + /// + /// Will panic if `value` exceeds `MAX`. #[inline] $v const fn from_usize(value: usize) -> Self { - #[cfg(not(bootstrap))] assert!(value <= ($max as usize)); - #[cfg(bootstrap)] - [()][(value > ($max as usize)) as usize]; + // SAFETY: We just checked that `value <= max`. unsafe { Self::from_u32_unchecked(value as u32) } } + /// Creates a new index from a given `u32`. + /// + /// # Panics + /// + /// Will panic if `value` exceeds `MAX`. #[inline] $v const fn from_u32(value: u32) -> Self { - #[cfg(not(bootstrap))] assert!(value <= $max); - #[cfg(bootstrap)] - [()][(value > $max) as usize]; + // SAFETY: We just checked that `value <= max`. unsafe { Self::from_u32_unchecked(value) } } + /// Creates a new index from a given `u32`. + /// + /// # Safety + /// + /// The provided value must be less than or equal to the maximum value for the newtype. + /// Providing a value outside this range is undefined due to layout restrictions. + /// + /// Prefer using `from_u32`. #[inline] $v const unsafe fn from_u32_unchecked(value: u32) -> Self { Self { private: value } } - /// Extracts the value of this index as an integer. + /// Extracts the value of this index as a `usize`. #[inline] $v const fn index(self) -> usize { self.as_usize() @@ -634,15 +650,18 @@ } #[inline] - pub fn drain<R: RangeBounds<usize>>(&mut self, range: R) -> impl Iterator<Item = T> + '_ { + pub fn drain<'a, R: RangeBounds<usize>>( + &'a mut self, + range: R, + ) -> impl Iterator<Item = T> + 'a { self.raw.drain(range) } #[inline] - pub fn drain_enumerated<R: RangeBounds<usize>>( - &mut self, + pub fn drain_enumerated<'a, R: RangeBounds<usize>>( + &'a mut self, range: R, - ) -> impl Iterator<Item = (I, T)> + '_ { + ) -> impl Iterator<Item = (I, T)> + 'a { self.raw.drain(range).enumerate().map(|(n, t)| (I::new(n), t)) }
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 2296cc6..5b4a9d9 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -108,7 +108,7 @@ let tcx = self.tcx; // Select everything, returning errors. - let true_errors = fulfill_cx.select_where_possible(self).err().unwrap_or_else(Vec::new); + let true_errors = fulfill_cx.select_where_possible(self); debug!("true_errors = {:#?}", true_errors); if !true_errors.is_empty() { @@ -118,7 +118,7 @@ } // Anything left unselected *now* must be an ambiguity. - let ambig_errors = fulfill_cx.select_all_or_error(self).err().unwrap_or_else(Vec::new); + let ambig_errors = fulfill_cx.select_all_or_error(self); debug!("ambig_errors = {:#?}", ambig_errors); let region_obligations = self.take_registered_region_obligations();
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 3f54247..09bfb32 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -866,6 +866,7 @@ Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } + #[tracing::instrument(level = "debug", skip(self))] fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { debug_assert_eq!(t, _t); debug!("ConstInferUnifier: t={:?}", t); @@ -941,6 +942,7 @@ } } + #[tracing::instrument(level = "debug", skip(self))] fn consts( &mut self, c: &'tcx ty::Const<'tcx>, @@ -951,29 +953,38 @@ match c.val { ty::ConstKind::Infer(InferConst::Var(vid)) => { - let mut inner = self.infcx.inner.borrow_mut(); - let variable_table = &mut inner.const_unification_table(); - // Check if the current unification would end up // unifying `target_vid` with a const which contains // an inference variable which is unioned with `target_vid`. // // Not doing so can easily result in stack overflows. - if variable_table.unioned(self.target_vid, vid) { + if self + .infcx + .inner + .borrow_mut() + .const_unification_table() + .unioned(self.target_vid, vid) + { return Err(TypeError::CyclicConst(c)); } - let var_value = variable_table.probe_value(vid); + let var_value = + self.infcx.inner.borrow_mut().const_unification_table().probe_value(vid); match var_value.val { ConstVariableValue::Known { value: u } => self.consts(u, u), ConstVariableValue::Unknown { universe } => { if self.for_universe.can_name(universe) { Ok(c) } else { - let new_var_id = variable_table.new_key(ConstVarValue { - origin: var_value.origin, - val: ConstVariableValue::Unknown { universe: self.for_universe }, - }); + let new_var_id = + self.infcx.inner.borrow_mut().const_unification_table().new_key( + ConstVarValue { + origin: var_value.origin, + val: ConstVariableValue::Unknown { + universe: self.for_universe, + }, + }, + ); Ok(self.tcx().mk_const_var(new_var_id, c.ty)) } }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 126c25f..85226e6 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -310,6 +310,34 @@ err } +/// Structurally compares two types, modulo any inference variables. +/// +/// Returns `true` if two types are equal, or if one type is an inference variable compatible +/// with the other type. A TyVar inference type is compatible with any type, and an IntVar or +/// FloatVar inference type are compatible with themselves or their concrete types (Int and +/// Float types, respectively). When comparing two ADTs, these rules apply recursively. +pub fn same_type_modulo_infer(a: Ty<'tcx>, b: Ty<'ctx>) -> bool { + match (&a.kind(), &b.kind()) { + (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => { + if did_a != did_b { + return false; + } + + substs_a.types().zip(substs_b.types()).all(|(a, b)| same_type_modulo_infer(a, b)) + } + (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_))) + | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Int(_) | &ty::Infer(ty::InferTy::IntVar(_))) + | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) + | ( + &ty::Infer(ty::InferTy::FloatVar(_)), + &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), + ) + | (&ty::Infer(ty::InferTy::TyVar(_)), _) + | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true, + _ => a == b, + } +} + impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_region_errors(&self, errors: &Vec<RegionResolutionError<'tcx>>) { debug!("report_region_errors(): {} errors to start", errors.len()); @@ -386,21 +414,6 @@ self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit(); } - - RegionResolutionError::MemberConstraintFailure { - hidden_ty, - member_region, - span, - } => { - let hidden_ty = self.resolve_vars_if_possible(hidden_ty); - unexpected_hidden_region_diagnostic( - self.tcx, - span, - hidden_ty, - member_region, - ) - .emit(); - } } } } @@ -438,8 +451,7 @@ RegionResolutionError::GenericBoundFailure(..) => true, RegionResolutionError::ConcreteFailure(..) | RegionResolutionError::SubSupConflict(..) - | RegionResolutionError::UpperBoundUniverseConflict(..) - | RegionResolutionError::MemberConstraintFailure { .. } => false, + | RegionResolutionError::UpperBoundUniverseConflict(..) => false, }; let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { @@ -454,7 +466,6 @@ RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(), RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), - RegionResolutionError::MemberConstraintFailure { span, .. } => span, }); errors } @@ -1483,7 +1494,7 @@ let mut returned_async_output_error = false; for &sp in values { if sp.is_desugaring(DesugaringKind::Async) && !returned_async_output_error { - if &[sp] != err.span.primary_spans() { + if [sp] != err.span.primary_spans() { let mut span: MultiSpan = sp.into(); span.push_span_label( sp, @@ -1684,11 +1695,23 @@ } _ => exp_found, }; - debug!("exp_found {:?} terr {:?}", exp_found, terr); + debug!("exp_found {:?} terr {:?} cause.code {:?}", exp_found, terr, cause.code); if let Some(exp_found) = exp_found { - self.suggest_as_ref_where_appropriate(span, &exp_found, diag); - self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); - self.suggest_await_on_expect_found(cause, span, &exp_found, diag); + let should_suggest_fixes = if let ObligationCauseCode::Pattern { root_ty, .. } = + &cause.code + { + // Skip if the root_ty of the pattern is not the same as the expected_ty. + // If these types aren't equal then we've probably peeled off a layer of arrays. + same_type_modulo_infer(self.resolve_vars_if_possible(*root_ty), exp_found.expected) + } else { + true + }; + + if should_suggest_fixes { + self.suggest_as_ref_where_appropriate(span, &exp_found, diag); + self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); + self.suggest_await_on_expect_found(cause, span, &exp_found, diag); + } } // In some (most?) cases cause.body_id points to actual body, but in some cases @@ -1721,13 +1744,7 @@ if let ty::Opaque(def_id, substs) = ty.kind() { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); // Future::Output - let item_def_id = self - .tcx - .associated_items(future_trait) - .in_definition_order() - .next() - .unwrap() - .def_id; + let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; let bounds = self.tcx.explicit_item_bounds(*def_id); @@ -1784,7 +1801,7 @@ self.get_impl_future_output_ty(exp_found.expected), self.get_impl_future_output_ty(exp_found.found), ) { - (Some(exp), Some(found)) if ty::TyS::same_type(exp, found) => match &cause.code { + (Some(exp), Some(found)) if same_type_modulo_infer(exp, found) => match &cause.code { ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => { diag.multipart_suggestion( "consider `await`ing on both `Future`s", @@ -1816,32 +1833,39 @@ diag.help("consider `await`ing on both `Future`s"); } }, - (_, Some(ty)) if ty::TyS::same_type(exp_found.expected, ty) => { - let span = match cause.code { - // scrutinee's span - ObligationCauseCode::Pattern { span: Some(span), .. } => span, - _ => exp_span, - }; + (_, Some(ty)) if same_type_modulo_infer(exp_found.expected, ty) => { diag.span_suggestion_verbose( - span.shrink_to_hi(), + exp_span.shrink_to_hi(), "consider `await`ing on the `Future`", ".await".to_string(), Applicability::MaybeIncorrect, ); } - (Some(ty), _) if ty::TyS::same_type(ty, exp_found.found) => { - let span = match cause.code { - // scrutinee's span - ObligationCauseCode::Pattern { span: Some(span), .. } => span, - _ => exp_span, - }; - diag.span_suggestion_verbose( - span.shrink_to_hi(), - "consider `await`ing on the `Future`", - ".await".to_string(), - Applicability::MaybeIncorrect, - ); - } + (Some(ty), _) if same_type_modulo_infer(ty, exp_found.found) => match cause.code { + ObligationCauseCode::Pattern { span: Some(span), .. } + | ObligationCauseCode::IfExpression(box IfExpressionCause { then: span, .. }) => { + diag.span_suggestion_verbose( + span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await".to_string(), + Applicability::MaybeIncorrect, + ); + } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + ref prior_arms, + .. + }) => { + diag.multipart_suggestion_verbose( + "consider `await`ing on the `Future`", + prior_arms + .iter() + .map(|arm| (arm.shrink_to_hi(), ".await".to_string())) + .collect(), + Applicability::MaybeIncorrect, + ); + } + _ => {} + }, _ => {} } } @@ -1867,7 +1891,7 @@ .iter() .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) .map(|field| (field.ident.name, field.ty(self.tcx, expected_substs))) - .find(|(_, ty)| ty::TyS::same_type(ty, exp_found.found)) + .find(|(_, ty)| same_type_modulo_infer(ty, exp_found.found)) { if let ObligationCauseCode::Pattern { span: Some(span), .. } = cause.code { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { @@ -1932,7 +1956,7 @@ | (_, ty::Infer(_)) | (ty::Param(_), _) | (ty::Infer(_), _) => {} - _ if ty::TyS::same_type(exp_ty, found_ty) => {} + _ if same_type_modulo_infer(exp_ty, found_ty) => {} _ => show_suggestion = false, }; } @@ -2129,10 +2153,19 @@ None }, self.tcx.generics_of(owner.to_def_id()), + hir.span(hir_id), ) }); + + let span = match generics { + // This is to get around the trait identity obligation, that has a `DUMMY_SP` as signal + // for other diagnostics, so we need to recover it here. + Some((_, _, node)) if span.is_dummy() => node, + _ => span, + }; + let type_param_span = match (generics, bound_kind) { - (Some((_, ref generics)), GenericKind::Param(ref param)) => { + (Some((_, ref generics, _)), GenericKind::Param(ref param)) => { // Account for the case where `param` corresponds to `Self`, // which doesn't have the expected type argument. if !(generics.has_self && param.index == 0) { @@ -2169,7 +2202,7 @@ }; let new_lt = generics .as_ref() - .and_then(|(parent_g, g)| { + .and_then(|(parent_g, g, _)| { let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char)); let mut lts_names = g .params @@ -2191,7 +2224,7 @@ .unwrap_or("'lt".to_string()); let add_lt_sugg = generics .as_ref() - .and_then(|(_, g)| g.params.first()) + .and_then(|(_, g, _)| g.params.first()) .and_then(|param| param.def_id.as_local()) .map(|def_id| { ( @@ -2207,14 +2240,12 @@ if let Some(SubregionOrigin::CompareImplMethodObligation { span, - item_name, impl_item_def_id, trait_item_def_id, }) = origin { return self.report_extra_impl_obligation( span, - item_name, impl_item_def_id, trait_item_def_id, &format!("`{}: {}`", bound_kind, sub), @@ -2547,7 +2578,7 @@ infer::MiscVariable(_) => String::new(), infer::PatternRegion(_) => " for pattern".to_string(), infer::AddrOfRegion(_) => " for borrow expression".to_string(), - infer::Autoref(_, _) => " for autoref".to_string(), + infer::Autoref(_) => " for autoref".to_string(), infer::Coercion(_) => " for automatic coercion".to_string(), infer::LateBoundRegion(_, br, infer::FnCall) => { format!(" for lifetime parameter {}in function call", br_string(br))
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index e00003f..a7e019a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -5,13 +5,12 @@ use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat}; +use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, MatchSource, Pat}; use rustc_middle::hir::map::Map; use rustc_middle::infer::unify_key::ConstVariableOriginKind; use rustc_middle::ty::print::Print; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt}; -use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::kw; use rustc_span::Span; use std::borrow::Cow; @@ -26,6 +25,7 @@ found_closure: Option<&'tcx Expr<'tcx>>, found_method_call: Option<&'tcx Expr<'tcx>>, found_exact_method_call: Option<&'tcx Expr<'tcx>>, + found_for_loop_iter: Option<&'tcx Expr<'tcx>>, found_use_diagnostic: Option<UseDiagnostic<'tcx>>, } @@ -41,6 +41,7 @@ found_closure: None, found_method_call: None, found_exact_method_call: None, + found_for_loop_iter: None, found_use_diagnostic: None, } } @@ -111,6 +112,15 @@ } fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Match(scrutinee, [_, arm], MatchSource::ForLoopDesugar) = expr.kind { + if let Some(pat) = arm.pat.for_loop_some() { + if let Some(ty) = self.node_ty_contains_target(pat.hir_id) { + self.found_for_loop_iter = Some(scrutinee); + self.found_node_ty = Some(ty); + return; + } + } + } if let ExprKind::MethodCall(_, call_span, exprs, _) = expr.kind { if call_span == self.target_span && Some(self.target) @@ -643,10 +653,7 @@ let msg = if let Some(simple_ident) = pattern.simple_ident() { match pattern.span.desugaring_kind() { None => format!("consider giving `{}` {}", simple_ident, suffix), - Some(DesugaringKind::ForLoop(_)) => { - "the element type for this iterator is not specified".to_string() - } - _ => format!("this needs {}", suffix), + Some(_) => format!("this needs {}", suffix), } } else { format!("consider giving this pattern {}", suffix) @@ -719,6 +726,11 @@ // = note: type must be known at this point self.annotate_method_call(segment, e, &mut err); } + } else if let Some(scrutinee) = local_visitor.found_for_loop_iter { + err.span_label( + scrutinee.span, + "the element type for this iterator is not specified".to_string(), + ); } // Instead of the following: // error[E0282]: type annotations needed
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index 1b35c40..ac57796 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
@@ -7,7 +7,10 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::SubregionOrigin; -use rustc_errors::{struct_span_err, ErrorReported}; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::{GenericParamKind, Ty}; +use rustc_middle::ty::Region; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when both the concerned regions are anonymous. @@ -160,11 +163,13 @@ } }; - let mut e = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); + let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); - e.span_label(span_1, main_label); - e.span_label(span_2, String::new()); - e.span_label(span, span_label); + err.span_label(span_1, main_label); + err.span_label(span_2, String::new()); + err.span_label(span, span_label); + + self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err); if let Some(t) = future_return_type { let snip = self @@ -178,14 +183,87 @@ (_, "") => None, _ => Some(s), }) - .unwrap_or("{unnamed_type}".to_string()); + .unwrap_or_else(|| "{unnamed_type}".to_string()); - e.span_label( + err.span_label( t.span, &format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip), ); } - e.emit(); + err.emit(); Some(ErrorReported) } + + fn suggest_adding_lifetime_params( + &self, + sub: Region<'tcx>, + ty_sup: &Ty<'_>, + ty_sub: &Ty<'_>, + err: &mut DiagnosticBuilder<'_>, + ) { + if let ( + hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, + hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, + ) = (ty_sub, ty_sup) + { + if lifetime_sub.name.is_elided() && lifetime_sup.name.is_elided() { + if let Some(anon_reg) = self.tcx().is_suitable_region(sub) { + let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id); + if let hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Fn(_, ref generics, ..), + .. + }) = self.tcx().hir().get(hir_id) + { + let (suggestion_param_name, introduce_new) = generics + .params + .iter() + .find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) + .and_then(|p| self.tcx().sess.source_map().span_to_snippet(p.span).ok()) + .map(|name| (name, false)) + .unwrap_or_else(|| ("'a".to_string(), true)); + + let mut suggestions = vec![ + if let hir::LifetimeName::Underscore = lifetime_sub.name { + (lifetime_sub.span, suggestion_param_name.clone()) + } else { + ( + lifetime_sub.span.shrink_to_hi(), + suggestion_param_name.clone() + " ", + ) + }, + if let hir::LifetimeName::Underscore = lifetime_sup.name { + (lifetime_sup.span, suggestion_param_name.clone()) + } else { + ( + lifetime_sup.span.shrink_to_hi(), + suggestion_param_name.clone() + " ", + ) + }, + ]; + + if introduce_new { + let new_param_suggestion = match &generics.params { + [] => (generics.span, format!("<{}>", suggestion_param_name)), + [first, ..] => ( + first.span.shrink_to_lo(), + format!("{}, ", suggestion_param_name), + ), + }; + + suggestions.push(new_param_suggestion); + } + + err.multipart_suggestion( + "consider introducing a named lifetime parameter", + suggestions, + Applicability::MaybeIncorrect, + ); + err.note( + "each elided lifetime in input position becomes a distinct lifetime", + ); + } + } + } + } + } }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs index 0878f85..eb1c80e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
@@ -3,8 +3,6 @@ use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; -use rustc_hir::intravisit::Visitor; -use rustc_hir::FnRetTy; use rustc_middle::ty; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { @@ -48,19 +46,24 @@ return None; // inapplicable }; + // Suggesting to add a `'static` lifetime to a parameter is nearly always incorrect, + // and can steer users down the wrong path. + if *named == ty::ReStatic { + return None; + } + debug!("try_report_named_anon_conflict: named = {:?}", named); debug!("try_report_named_anon_conflict: anon_param_info = {:?}", anon_param_info); debug!("try_report_named_anon_conflict: region_info = {:?}", region_info); - let (param, new_ty, new_ty_span, br, is_first, scope_def_id, is_impl_item) = ( - anon_param_info.param, - anon_param_info.param_ty, - anon_param_info.param_ty_span, - anon_param_info.bound_region, - anon_param_info.is_first, - region_info.def_id, - region_info.is_impl_item, - ); + let param = anon_param_info.param; + let new_ty = anon_param_info.param_ty; + let new_ty_span = anon_param_info.param_ty_span; + let br = anon_param_info.bound_region; + let is_first = anon_param_info.is_first; + let scope_def_id = region_info.def_id; + let is_impl_item = region_info.is_impl_item; + match br { ty::BrAnon(_) => {} _ => { @@ -75,26 +78,10 @@ return None; } - if let Some((_, fndecl)) = find_anon_type(self.tcx(), anon, &br) { - if self.is_self_anon(is_first, scope_def_id) { - return None; - } - - if let FnRetTy::Return(ty) = &fndecl.output { - let mut v = ty::TraitObjectVisitor(vec![], self.tcx().hir()); - v.visit_ty(ty); - - debug!("try_report_named_anon_conflict: ret ty {:?}", ty); - if sub == &ty::ReStatic - && v.0.into_iter().any(|t| t.span.desugaring_kind().is_none()) - { - // If the failure is due to a `'static` requirement coming from a `dyn` or - // `impl` Trait that *isn't* caused by `async fn` desugaring, handle this case - // better in `static_impl_trait`. - debug!("try_report_named_anon_conflict: impl Trait + 'static"); - return None; - } - } + if find_anon_type(self.tcx(), anon, &br).is_some() + && self.is_self_anon(is_first, scope_def_id) + { + return None; } let (error_var, span_label_var) = match param.pat.simple_ident() { @@ -114,16 +101,12 @@ ); diag.span_label(span, format!("lifetime `{}` required", named)); - // Suggesting `'static` is nearly always incorrect, and can steer users - // down the wrong path. - if *named != ty::ReStatic { - diag.span_suggestion( - new_ty_span, - &format!("add explicit lifetime `{}` to {}", named, span_label_var), - new_ty.to_string(), - Applicability::Unspecified, - ); - } + diag.span_suggestion( + new_ty_span, + &format!("add explicit lifetime `{}` to {}", named, span_label_var), + new_ty.to_string(), + Applicability::Unspecified, + ); Some(diag) }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 7fba6a8..2aaebed 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -192,14 +192,16 @@ ObligationCauseCode::MatchImpl(parent, ..) => &parent.code, _ => &cause.code, }; - if let ObligationCauseCode::ItemObligation(item_def_id) = *code { + if let (ObligationCauseCode::ItemObligation(item_def_id), None) = + (code, override_error_code) + { // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static` // lifetime as above, but called using a fully-qualified path to the method: // `Foo::qux(bar)`. let mut v = TraitObjectVisitor(FxHashSet::default()); v.visit_ty(param.param_ty); if let Some((ident, self_ty)) = - self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0) + self.get_impl_ident_and_self_ty_from_trait(*item_def_id, &v.0) { if self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) { override_error_code = Some(ident); @@ -263,9 +265,7 @@ match fn_return.kind { TyKind::OpaqueDef(item_id, _) => { let item = tcx.hir().item(item_id); - let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind { - opaque - } else { + let ItemKind::OpaqueTy(opaque) = &item.kind else { return; };
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index ea9d0ea..cfa7921 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
@@ -9,10 +9,13 @@ use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::print::RegionHighlightMode; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor}; + use rustc_span::{MultiSpan, Span, Symbol}; +use std::ops::ControlFlow; + impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`. pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorReported> { @@ -51,12 +54,16 @@ { if let SubregionOrigin::CompareImplTypeObligation { span, - item_name, impl_item_def_id, trait_item_def_id, } = origin { - self.emit_associated_type_err(span, item_name, impl_item_def_id, trait_item_def_id); + self.emit_associated_type_err( + span, + self.infcx.tcx.item_name(impl_item_def_id), + impl_item_def_id, + trait_item_def_id, + ); return Some(ErrorReported); } } @@ -69,6 +76,47 @@ .tcx() .sess .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); + + // Mark all unnamed regions in the type with a number. + // This diagnostic is called in response to lifetime errors, so be informative. + struct HighlightBuilder<'tcx> { + highlight: RegionHighlightMode, + tcx: TyCtxt<'tcx>, + counter: usize, + } + + impl HighlightBuilder<'tcx> { + fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode { + let mut builder = + HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1, tcx }; + builder.visit_ty(ty); + builder.highlight + } + } + + impl<'tcx> ty::fold::TypeVisitor<'tcx> for HighlightBuilder<'tcx> { + fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { + Some(self.tcx) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + if !r.has_name() && self.counter <= 3 { + self.highlight.highlighting_region(r, self.counter); + self.counter += 1; + } + r.super_visit_with(self) + } + } + + let expected_highlight = HighlightBuilder::build(self.tcx(), expected); + let expected = self + .infcx + .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight)) + .name; + let found_highlight = HighlightBuilder::build(self.tcx(), found); + let found = + self.infcx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; + err.span_label(sp, &format!("found `{}`", found)); err.span_label(trait_sp, &format!("expected `{}`", expected)); @@ -96,15 +144,8 @@ ); } - if let Some((expected, found)) = - self.infcx.expected_found_str_ty(ExpectedFound { expected, found }) - { - // Highlighted the differences when showing the "expected/found" note. - err.note_expected_found(&"", expected, &"", found); - } else { - // This fallback shouldn't be necessary, but let's keep it in just in case. - err.note(&format!("expected `{}`\n found `{}`", expected, found)); - } + err.note(&format!("expected `{}`\n found `{}`", expected, found)); + err.span_help( type_param_span, "the lifetime requirements from the `impl` do not correspond to the requirements in \
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index 8dcdd4b..90bc5b3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
@@ -143,9 +143,8 @@ // similar to the asyncness fn in rustc_ty_utils::ty let hir_id = self.tcx().hir().local_def_id_to_hir_id(local_def_id); let node = self.tcx().hir().get(hir_id); - let fn_like = rustc_middle::hir::map::blocks::FnLikeNode::from_node(node)?; - - Some(fn_like.asyncness()) + let fn_kind = node.fn_kind()?; + Some(fn_kind.asyncness()) } // Here, we check for the case where the anonymous region
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 5f99a23..6600c18 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -53,9 +53,6 @@ infer::RelateObjectBound(span) => { label_or_note(span, "...so that it can be closed over into an object"); } - infer::CallReturn(span) => { - label_or_note(span, "...so that return value is valid for the call"); - } infer::DataBorrowed(ty, span) => { label_or_note( span, @@ -281,23 +278,6 @@ ); err } - infer::CallReturn(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0482, - "lifetime of return value does not outlive the function call" - ); - note_and_explain_region( - self.tcx, - &mut err, - "the return value is only valid for ", - sup, - "", - None, - ); - err - } infer::DataBorrowed(ty, span) => { let mut err = struct_span_err!( self.tcx.sess, @@ -350,30 +330,21 @@ ); err } - infer::CompareImplMethodObligation { - span, - item_name, - impl_item_def_id, - trait_item_def_id, - } => self.report_extra_impl_obligation( - span, - item_name, - impl_item_def_id, - trait_item_def_id, - &format!("`{}: {}`", sup, sub), - ), - infer::CompareImplTypeObligation { - span, - item_name, - impl_item_def_id, - trait_item_def_id, - } => self.report_extra_impl_obligation( - span, - item_name, - impl_item_def_id, - trait_item_def_id, - &format!("`{}: {}`", sup, sub), - ), + infer::CompareImplMethodObligation { span, impl_item_def_id, trait_item_def_id } => { + self.report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{}: {}`", sup, sub), + ) + } + infer::CompareImplTypeObligation { span, impl_item_def_id, trait_item_def_id } => self + .report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{}: {}`", sup, sub), + ), } }
diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index 728dc2d..4814b65 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs
@@ -66,8 +66,6 @@ /// follows. If we know that `r_b: 'static`, then this function /// will return true, even though we don't know anything that /// directly relates `r_a` and `r_b`. - /// - /// Also available through the `FreeRegionRelations` trait below. pub fn sub_free_regions( &self, tcx: TyCtxt<'tcx>, @@ -131,27 +129,6 @@ } } -/// The NLL region handling code represents free region relations in a -/// slightly different way; this trait allows functions to be abstract -/// over which version is in use. -pub trait FreeRegionRelations<'tcx> { - /// Tests whether `r_a <= r_b`. Both must be free regions or - /// `'static`. - fn sub_free_regions( - &self, - tcx: TyCtxt<'tcx>, - shorter: ty::Region<'tcx>, - longer: ty::Region<'tcx>, - ) -> bool; -} - -impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> { - fn sub_free_regions(&self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { - // invoke the "inherent method" - self.sub_free_regions(tcx, r_a, r_b) - } -} - impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> { type Lifted = FreeRegionMap<'tcx>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<FreeRegionMap<'tcx>> {
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 869fd22..4c9dcab 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
@@ -2,7 +2,6 @@ use crate::infer::region_constraints::Constraint; use crate::infer::region_constraints::GenericKind; -use crate::infer::region_constraints::MemberConstraint; use crate::infer::region_constraints::RegionConstraintData; use crate::infer::region_constraints::VarInfos; use crate::infer::region_constraints::VerifyBound; @@ -20,7 +19,6 @@ use rustc_middle::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar}; use rustc_middle::ty::{Region, RegionVid}; -use rustc_span::Span; use std::fmt; /// This function performs lexical region resolution given a complete @@ -28,13 +26,13 @@ /// iteration to find region values which satisfy all constraints, /// assuming such values can be found. It returns the final values of /// all the variables as well as a set of errors that must be reported. +#[instrument(level = "debug", skip(region_rels, var_infos, data))] pub fn resolve<'tcx>( region_rels: &RegionRelations<'_, 'tcx>, var_infos: VarInfos, data: RegionConstraintData<'tcx>, mode: RegionckMode, ) -> (LexicalRegionResolutions<'tcx>, Vec<RegionResolutionError<'tcx>>) { - debug!("RegionConstraintData: resolve_regions()"); let mut errors = vec![]; let mut resolver = LexicalResolver { region_rels, var_infos, data }; match mode { @@ -109,11 +107,6 @@ SubregionOrigin<'tcx>, // cause of the constraint Region<'tcx>, // the placeholder `'b` ), - - /// Indicates a failure of a `MemberConstraint`. These arise during - /// impl trait processing explicitly -- basically, the impl trait's hidden type - /// included some region that it was not supposed to. - MemberConstraintFailure { span: Span, hidden_ty: Ty<'tcx>, member_region: Region<'tcx> }, } struct RegionAndOrigin<'tcx> { @@ -150,12 +143,7 @@ let graph = self.construct_graph(); self.expand_givens(&graph); - loop { - self.expansion(&mut var_data); - if !self.enforce_member_constraints(&graph, &mut var_data) { - break; - } - } + self.expansion(&mut var_data); self.collect_errors(&mut var_data, errors); self.collect_var_errors(&var_data, &graph, errors); var_data @@ -233,120 +221,6 @@ } } - /// Enforce all member constraints and return true if anything - /// changed. See `enforce_member_constraint` for more details. - fn enforce_member_constraints( - &self, - graph: &RegionGraph<'tcx>, - var_values: &mut LexicalRegionResolutions<'tcx>, - ) -> bool { - // Note: we don't use the `any` combinator because we don't - // want to stop at the first constraint that makes a change. - let mut any_changed = false; - for member_constraint in &self.data.member_constraints { - any_changed |= self.enforce_member_constraint(graph, member_constraint, var_values); - } - any_changed - } - - /// Enforce a constraint like - /// - /// ``` - /// 'r member of ['c...] - /// ``` - /// - /// We look for all choice regions from the list `'c...` that: - /// - /// (a) are greater than the current value of `'r` (which is a lower bound) - /// - /// and - /// - /// (b) are compatible with the upper bounds of `'r` that we can - /// find by traversing the graph. - /// - /// From that list, we look for a *minimal* option `'c_min`. If we - /// find one, then we can enforce that `'r: 'c_min`. - fn enforce_member_constraint( - &self, - graph: &RegionGraph<'tcx>, - member_constraint: &MemberConstraint<'tcx>, - var_values: &mut LexicalRegionResolutions<'tcx>, - ) -> bool { - debug!("enforce_member_constraint(member_constraint={:#?})", member_constraint); - - // The constraint is some inference variable (`vid`) which - // must be equal to one of the options. - let member_vid = match member_constraint.member_region { - ty::ReVar(vid) => *vid, - _ => return false, - }; - - // The current value of `vid` is a lower bound LB -- i.e., we - // know that `LB <= vid` must be true. - let member_lower_bound: ty::Region<'tcx> = match var_values.value(member_vid) { - VarValue::ErrorValue => return false, - VarValue::Value(r) => r, - }; - - // Find all the "upper bounds" -- that is, each region `b` such that - // `r0 <= b` must hold. - let (member_upper_bounds, ..) = - self.collect_bounding_regions(graph, member_vid, OUTGOING, None); - - // Get an iterator over the *available choice* -- that is, - // each choice region `c` where `lb <= c` and `c <= ub` for all the - // upper bounds `ub`. - debug!("enforce_member_constraint: upper_bounds={:#?}", member_upper_bounds); - let mut options = member_constraint.choice_regions.iter().filter(|option| { - self.sub_concrete_regions(member_lower_bound, option) - && member_upper_bounds - .iter() - .all(|upper_bound| self.sub_concrete_regions(option, upper_bound.region)) - }); - - // If there is more than one option, we only make a choice if - // there is a single *least* choice -- i.e., some available - // region that is `<=` all the others. - let mut least_choice: ty::Region<'tcx> = match options.next() { - Some(&r) => r, - None => return false, - }; - debug!("enforce_member_constraint: least_choice={:?}", least_choice); - for &option in options { - debug!("enforce_member_constraint: option={:?}", option); - if !self.sub_concrete_regions(least_choice, option) { - if self.sub_concrete_regions(option, least_choice) { - debug!("enforce_member_constraint: new least choice"); - least_choice = option; - } else { - debug!("enforce_member_constraint: no least choice"); - return false; - } - } - } - - // (#72087) Different `ty::Regions` can be known to be equal, for - // example, we know that `'a` and `'static` are equal in a function - // with a parameter of type `&'static &'a ()`. - // - // When we have two equal regions like this `expansion` will use - // `lub_concrete_regions` to pick a canonical representative. The same - // choice is needed here so that we don't end up in a cycle of - // `expansion` changing the region one way and the code here changing - // it back. - let lub = self.lub_concrete_regions(least_choice, member_lower_bound); - debug!( - "enforce_member_constraint: final least choice = {:?}\nlub = {:?}", - least_choice, lub - ); - if lub != member_lower_bound { - *var_values.value_mut(member_vid) = VarValue::Value(least_choice); - true - } else { - false - } - } - fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) { let mut constraints = IndexVec::from_elem_n(Vec::new(), var_values.values.len()); let mut changes = Vec::new(); @@ -461,6 +335,7 @@ } /// True if `a <= b`, but not defined over inference variables. + #[instrument(level = "trace", skip(self))] fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool { let tcx = self.tcx(); let sub_free_regions = |r1, r2| self.region_rels.free_regions.sub_free_regions(tcx, r1, r2); @@ -492,6 +367,7 @@ /// /// Neither `a` nor `b` may be an inference variable (hence the /// term "concrete regions"). + #[instrument(level = "trace", skip(self))] fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { let r = match (a, b) { (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { @@ -562,13 +438,14 @@ /// After expansion is complete, go and check upper bounds (i.e., /// cases where the region cannot grow larger than a fixed point) /// and check that they are satisfied. + #[instrument(skip(self, var_data, errors))] fn collect_errors( &self, var_data: &mut LexicalRegionResolutions<'tcx>, errors: &mut Vec<RegionResolutionError<'tcx>>, ) { for (constraint, origin) in &self.data.constraints { - debug!("collect_errors: constraint={:?} origin={:?}", constraint, origin); + debug!(?constraint, ?origin); match *constraint { Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => { // Expansion will ensure that these constraints hold. Ignore. @@ -580,7 +457,7 @@ } debug!( - "collect_errors: region error at {:?}: \ + "region error at {:?}: \ cannot verify that {:?} <= {:?}", origin, sub, sup ); @@ -606,7 +483,7 @@ // collect them later. if !self.sub_concrete_regions(a_region, b_region) { debug!( - "collect_errors: region error at {:?}: \ + "region error at {:?}: \ cannot verify that {:?}={:?} <= {:?}", origin, a_vid, a_region, b_region ); @@ -616,23 +493,6 @@ } } - // Check that all member constraints are satisfied. - for member_constraint in &self.data.member_constraints { - let member_region = var_data.normalize(self.tcx(), member_constraint.member_region); - let choice_regions = member_constraint - .choice_regions - .iter() - .map(|&choice_region| var_data.normalize(self.tcx(), choice_region)); - if !choice_regions.clone().any(|choice_region| member_region == choice_region) { - let span = self.tcx().def_span(member_constraint.opaque_type_def_id); - errors.push(RegionResolutionError::MemberConstraintFailure { - span, - hidden_ty: member_constraint.hidden_ty, - member_region, - }); - } - } - for verify in &self.data.verifys { debug!("collect_errors: verify={:?}", verify); let sub = var_data.normalize(self.tcx(), verify.region);
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 18836d5..2fd01c2 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -95,9 +95,10 @@ /// This is used so that the region values inferred by HIR region solving are /// not exposed, and so that we can avoid doing work in HIR typeck that MIR /// typeck will also do. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Default)] pub enum RegionckMode { /// The default mode: report region errors, don't erase regions. + #[default] Solve, /// Erase the results of region after solving. Erase { @@ -108,12 +109,6 @@ }, } -impl Default for RegionckMode { - fn default() -> Self { - RegionckMode::Solve - } -} - impl RegionckMode { /// Indicates that the MIR borrowck will repeat these region /// checks, so we should ignore errors if NLL is (unconditionally) @@ -417,26 +412,13 @@ /// (&'a &'b T) where a >= b ReferenceOutlivesReferent(Ty<'tcx>, Span), - /// Region in return type of invoked fn must enclose call - CallReturn(Span), - /// Comparing the signature and requirements of an impl method against /// the containing trait. - CompareImplMethodObligation { - span: Span, - item_name: Symbol, - impl_item_def_id: DefId, - trait_item_def_id: DefId, - }, + CompareImplMethodObligation { span: Span, impl_item_def_id: DefId, trait_item_def_id: DefId }, /// Comparing the signature and requirements of an impl associated type /// against the containing trait - CompareImplTypeObligation { - span: Span, - item_name: Symbol, - impl_item_def_id: DefId, - trait_item_def_id: DefId, - }, + CompareImplTypeObligation { span: Span, impl_item_def_id: DefId, trait_item_def_id: DefId }, } // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger. @@ -472,7 +454,7 @@ AddrOfRegion(Span), /// Regions created as part of an autoref of a method receiver - Autoref(Span, ty::AssocItem), + Autoref(Span), /// Regions created as part of an automatic coercion Coercion(Span), @@ -1255,16 +1237,16 @@ self.tainted_by_errors_flag.set(true) } - /// Process the region constraints and report any errors that + /// Process the region constraints and return any any errors that /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_vars_if_possible` as well as `fully_resolve`. - pub fn resolve_regions_and_report_errors( + pub fn resolve_regions( &self, region_context: DefId, outlives_env: &OutlivesEnvironment<'tcx>, mode: RegionckMode, - ) { + ) -> Vec<RegionResolutionError<'tcx>> { let (var_infos, data) = { let mut inner = self.inner.borrow_mut(); let inner = &mut *inner; @@ -1290,6 +1272,21 @@ let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); + errors + } + + /// Process the region constraints and report any errors that + /// result. After this, no more unification operations should be + /// done -- or the compiler will panic -- but it is legal to use + /// `resolve_vars_if_possible` as well as `fully_resolve`. + pub fn resolve_regions_and_report_errors( + &self, + region_context: DefId, + outlives_env: &OutlivesEnvironment<'tcx>, + mode: RegionckMode, + ) { + let errors = self.resolve_regions(region_context, outlives_env, mode); + if !self.is_tainted_by_errors() { // As a heuristic, just skip reporting region errors // altogether if other errors have been reported while @@ -1803,7 +1800,6 @@ ReborrowUpvar(a, _) => a, DataBorrowed(_, a) => a, ReferenceOutlivesReferent(_, a) => a, - CallReturn(a) => a, CompareImplMethodObligation { span, .. } => span, CompareImplTypeObligation { span, .. } => span, } @@ -1819,23 +1815,19 @@ } traits::ObligationCauseCode::CompareImplMethodObligation { - item_name, impl_item_def_id, trait_item_def_id, } => SubregionOrigin::CompareImplMethodObligation { span: cause.span, - item_name, impl_item_def_id, trait_item_def_id, }, traits::ObligationCauseCode::CompareImplTypeObligation { - item_name, impl_item_def_id, trait_item_def_id, } => SubregionOrigin::CompareImplTypeObligation { span: cause.span, - item_name, impl_item_def_id, trait_item_def_id, }, @@ -1851,7 +1843,7 @@ MiscVariable(a) | PatternRegion(a) | AddrOfRegion(a) - | Autoref(a, _) + | Autoref(a) | Coercion(a) | EarlyBoundRegion(a, ..) | LateBoundRegion(a, ..)
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index d0883f2..e2e07f2 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -1,8 +1,17 @@ +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::{InferCtxt, InferOk}; +use crate::traits; +use rustc_data_structures::sync::Lrc; use rustc_data_structures::vec_map::VecMap; use rustc_hir as hir; -use rustc_middle::ty::{OpaqueTypeKey, Ty}; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::fold::BottomUpFolder; +use rustc_middle::ty::subst::{GenericArgKind, Subst}; +use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_span::Span; +use std::ops::ControlFlow; + pub type OpaqueTypeMap<'tcx> = VecMap<OpaqueTypeKey<'tcx>, OpaqueTypeDecl<'tcx>>; /// Information about the opaque types whose values we @@ -45,3 +54,584 @@ /// The origin of the opaque type. pub origin: hir::OpaqueTyOrigin, } + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + /// Replaces all opaque types in `value` with fresh inference variables + /// and creates appropriate obligations. For example, given the input: + /// + /// impl Iterator<Item = impl Debug> + /// + /// this method would create two type variables, `?0` and `?1`. It would + /// return the type `?0` but also the obligations: + /// + /// ?0: Iterator<Item = ?1> + /// ?1: Debug + /// + /// Moreover, it returns an `OpaqueTypeMap` that would map `?0` to + /// info about the `impl Iterator<..>` type and `?1` to info about + /// the `impl Debug` type. + /// + /// # Parameters + /// + /// - `parent_def_id` -- the `DefId` of the function in which the opaque type + /// is defined + /// - `body_id` -- the body-id with which the resulting obligations should + /// be associated + /// - `param_env` -- the in-scope parameter environment to be used for + /// obligations + /// - `value` -- the value within which we are instantiating opaque types + /// - `value_span` -- the span where the value came from, used in error reporting + pub fn instantiate_opaque_types<T: TypeFoldable<'tcx>>( + &self, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + value: T, + value_span: Span, + ) -> InferOk<'tcx, T> { + debug!( + "instantiate_opaque_types(value={:?}, body_id={:?}, \ + param_env={:?}, value_span={:?})", + value, body_id, param_env, value_span, + ); + let mut instantiator = + Instantiator { infcx: self, body_id, param_env, value_span, obligations: vec![] }; + let value = instantiator.instantiate_opaque_types_in_map(value); + InferOk { value, obligations: instantiator.obligations } + } + + /// Given the map `opaque_types` containing the opaque + /// `impl Trait` types whose underlying, hidden types are being + /// inferred, this method adds constraints to the regions + /// appearing in those underlying hidden types to ensure that they + /// at least do not refer to random scopes within the current + /// function. These constraints are not (quite) sufficient to + /// guarantee that the regions are actually legal values; that + /// final condition is imposed after region inference is done. + /// + /// # The Problem + /// + /// Let's work through an example to explain how it works. Assume + /// the current function is as follows: + /// + /// ```text + /// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) + /// ``` + /// + /// Here, we have two `impl Trait` types whose values are being + /// inferred (the `impl Bar<'a>` and the `impl + /// Bar<'b>`). Conceptually, this is sugar for a setup where we + /// define underlying opaque types (`Foo1`, `Foo2`) and then, in + /// the return type of `foo`, we *reference* those definitions: + /// + /// ```text + /// type Foo1<'x> = impl Bar<'x>; + /// type Foo2<'x> = impl Bar<'x>; + /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } + /// // ^^^^ ^^ + /// // | | + /// // | substs + /// // def_id + /// ``` + /// + /// As indicating in the comments above, each of those references + /// is (in the compiler) basically a substitution (`substs`) + /// applied to the type of a suitable `def_id` (which identifies + /// `Foo1` or `Foo2`). + /// + /// Now, at this point in compilation, what we have done is to + /// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with + /// fresh inference variables C1 and C2. We wish to use the values + /// of these variables to infer the underlying types of `Foo1` and + /// `Foo2`. That is, this gives rise to higher-order (pattern) unification + /// constraints like: + /// + /// ```text + /// for<'a> (Foo1<'a> = C1) + /// for<'b> (Foo1<'b> = C2) + /// ``` + /// + /// For these equation to be satisfiable, the types `C1` and `C2` + /// can only refer to a limited set of regions. For example, `C1` + /// can only refer to `'static` and `'a`, and `C2` can only refer + /// to `'static` and `'b`. The job of this function is to impose that + /// constraint. + /// + /// Up to this point, C1 and C2 are basically just random type + /// inference variables, and hence they may contain arbitrary + /// regions. In fact, it is fairly likely that they do! Consider + /// this possible definition of `foo`: + /// + /// ```text + /// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { + /// (&*x, &*y) + /// } + /// ``` + /// + /// Here, the values for the concrete types of the two impl + /// traits will include inference variables: + /// + /// ```text + /// &'0 i32 + /// &'1 i32 + /// ``` + /// + /// Ordinarily, the subtyping rules would ensure that these are + /// sufficiently large. But since `impl Bar<'a>` isn't a specific + /// type per se, we don't get such constraints by default. This + /// is where this function comes into play. It adds extra + /// constraints to ensure that all the regions which appear in the + /// inferred type are regions that could validly appear. + /// + /// This is actually a bit of a tricky constraint in general. We + /// want to say that each variable (e.g., `'0`) can only take on + /// values that were supplied as arguments to the opaque type + /// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in + /// scope. We don't have a constraint quite of this kind in the current + /// region checker. + /// + /// # The Solution + /// + /// We generally prefer to make `<=` constraints, since they + /// integrate best into the region solver. To do that, we find the + /// "minimum" of all the arguments that appear in the substs: that + /// is, some region which is less than all the others. In the case + /// of `Foo1<'a>`, that would be `'a` (it's the only choice, after + /// all). Then we apply that as a least bound to the variables + /// (e.g., `'a <= '0`). + /// + /// In some cases, there is no minimum. Consider this example: + /// + /// ```text + /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } + /// ``` + /// + /// Here we would report a more complex "in constraint", like `'r + /// in ['a, 'b, 'static]` (where `'r` is some region appearing in + /// the hidden type). + /// + /// # Constrain regions, not the hidden concrete type + /// + /// Note that generating constraints on each region `Rc` is *not* + /// the same as generating an outlives constraint on `Tc` iself. + /// For example, if we had a function like this: + /// + /// ```rust + /// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { + /// (x, y) + /// } + /// + /// // Equivalent to: + /// type FooReturn<'a, T> = impl Foo<'a>; + /// fn foo<'a, T>(..) -> FooReturn<'a, T> { .. } + /// ``` + /// + /// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` + /// is an inference variable). If we generated a constraint that + /// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- + /// but this is not necessary, because the opaque type we + /// create will be allowed to reference `T`. So we only generate a + /// constraint that `'0: 'a`. + /// + /// # The `free_region_relations` parameter + /// + /// The `free_region_relations` argument is used to find the + /// "minimum" of the regions supplied to a given opaque type. + /// It must be a relation that can answer whether `'a <= 'b`, + /// where `'a` and `'b` are regions that appear in the "substs" + /// for the opaque type references (the `<'a>` in `Foo1<'a>`). + /// + /// Note that we do not impose the constraints based on the + /// generic regions from the `Foo1` definition (e.g., `'x`). This + /// is because the constraints we are imposing here is basically + /// the concern of the one generating the constraining type C1, + /// which is the current function. It also means that we can + /// take "implied bounds" into account in some cases: + /// + /// ```text + /// trait SomeTrait<'a, 'b> { } + /// fn foo<'a, 'b>(_: &'a &'b u32) -> impl SomeTrait<'a, 'b> { .. } + /// ``` + /// + /// Here, the fact that `'b: 'a` is known only because of the + /// implied bounds from the `&'a &'b u32` parameter, and is not + /// "inherent" to the opaque type definition. + /// + /// # Parameters + /// + /// - `opaque_types` -- the map produced by `instantiate_opaque_types` + /// - `free_region_relations` -- something that can be used to relate + /// the free regions (`'a`) that appear in the impl trait. + #[instrument(level = "debug", skip(self))] + pub fn constrain_opaque_type( + &self, + opaque_type_key: OpaqueTypeKey<'tcx>, + opaque_defn: &OpaqueTypeDecl<'tcx>, + ) { + let def_id = opaque_type_key.def_id; + + let tcx = self.tcx; + + let concrete_ty = self.resolve_vars_if_possible(opaque_defn.concrete_ty); + + debug!(?concrete_ty); + + let first_own_region = match opaque_defn.origin { + hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::AsyncFn => { + // We lower + // + // 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>. + // + // For these types we only iterate over `'l0..lm` below. + tcx.generics_of(def_id).parent_count + } + // These opaque type inherit all lifetime parameters from their + // parent, so we have to check them all. + hir::OpaqueTyOrigin::TyAlias => 0, + }; + + // For a case like `impl Foo<'a, 'b>`, we would generate a constraint + // `'r in ['a, 'b, 'static]` for each region `'r` that appears in the + // hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`). + // + // `conflict1` and `conflict2` are the two region bounds that we + // detected which were unrelated. They are used for diagnostics. + + // Create the set of choice regions: each region in the hidden + // type can be equal to any of the region parameters of the + // opaque type definition. + let choice_regions: Lrc<Vec<ty::Region<'tcx>>> = Lrc::new( + opaque_type_key.substs[first_own_region..] + .iter() + .filter_map(|arg| match arg.unpack() { + GenericArgKind::Lifetime(r) => Some(r), + GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, + }) + .chain(std::iter::once(self.tcx.lifetimes.re_static)) + .collect(), + ); + + concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + tcx: self.tcx, + op: |r| { + self.member_constraint( + opaque_type_key.def_id, + opaque_defn.definition_span, + concrete_ty, + r, + &choice_regions, + ) + }, + }); + } +} + +// Visitor that requires that (almost) all regions in the type visited outlive +// `least_region`. We cannot use `push_outlives_components` because regions in +// closure signatures are not included in their outlives components. We need to +// ensure all regions outlive the given bound so that we don't end up with, +// say, `ReVar` appearing in a return type and causing ICEs when other +// functions end up with region constraints involving regions from other +// functions. +// +// We also cannot use `for_each_free_region` because for closures it includes +// the regions parameters from the enclosing item. +// +// We ignore any type parameters because impl trait values are assumed to +// capture all the in-scope type parameters. +struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP> { + tcx: TyCtxt<'tcx>, + op: OP, +} + +impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> +where + OP: FnMut(ty::Region<'tcx>), +{ + fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { + Some(self.tcx) + } + + fn visit_binder<T: TypeFoldable<'tcx>>( + &mut self, + t: &ty::Binder<'tcx, T>, + ) -> ControlFlow<Self::BreakTy> { + t.as_ref().skip_binder().visit_with(self); + ControlFlow::CONTINUE + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + match *r { + // ignore bound regions, keep visiting + ty::ReLateBound(_, _) => ControlFlow::CONTINUE, + _ => { + (self.op)(r); + ControlFlow::CONTINUE + } + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + // We're only interested in types involving regions + if !ty.flags().intersects(ty::TypeFlags::HAS_POTENTIAL_FREE_REGIONS) { + return ControlFlow::CONTINUE; + } + + match ty.kind() { + ty::Closure(_, ref substs) => { + // Skip lifetime parameters of the enclosing item(s) + + substs.as_closure().tupled_upvars_ty().visit_with(self); + substs.as_closure().sig_as_fn_ptr_ty().visit_with(self); + } + + ty::Generator(_, ref substs, _) => { + // Skip lifetime parameters of the enclosing item(s) + // Also skip the witness type, because that has no free regions. + + substs.as_generator().tupled_upvars_ty().visit_with(self); + substs.as_generator().return_ty().visit_with(self); + substs.as_generator().yield_ty().visit_with(self); + substs.as_generator().resume_ty().visit_with(self); + } + _ => { + ty.super_visit_with(self); + } + } + + ControlFlow::CONTINUE + } +} + +struct Instantiator<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + value_span: Span, + obligations: Vec<traits::PredicateObligation<'tcx>>, +} + +impl<'a, 'tcx> Instantiator<'a, 'tcx> { + fn instantiate_opaque_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T { + let tcx = self.infcx.tcx; + value.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |ty| { + if ty.references_error() { + return tcx.ty_error(); + } else if let ty::Opaque(def_id, substs) = ty.kind() { + // Check that this is `impl Trait` type is + // declared by `parent_def_id` -- i.e., one whose + // value we are inferring. At present, this is + // always true during the first phase of + // type-check, but not always true later on during + // NLL. Once we support named opaque types more fully, + // this same scenario will be able to arise during all phases. + // + // Here is an example using type alias `impl Trait` + // that indicates the distinction we are checking for: + // + // ```rust + // mod a { + // pub type Foo = impl Iterator; + // pub fn make_foo() -> Foo { .. } + // } + // + // mod b { + // fn foo() -> a::Foo { a::make_foo() } + // } + // ``` + // + // Here, the return type of `foo` references an + // `Opaque` indeed, but not one whose value is + // presently being inferred. You can get into a + // similar situation with closure return types + // today: + // + // ```rust + // fn foo() -> impl Iterator { .. } + // fn bar() { + // let x = || foo(); // returns the Opaque assoc with `foo` + // } + // ``` + if let Some(def_id) = def_id.as_local() { + let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let parent_def_id = self.infcx.defining_use_anchor; + let def_scope_default = || { + let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id); + parent_def_id == tcx.hir().local_def_id(opaque_parent_hir_id) + }; + let (in_definition_scope, origin) = + match tcx.hir().expect_item(opaque_hir_id).kind { + // Anonymous `impl Trait` + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + impl_trait_fn: Some(parent), + origin, + .. + }) => (parent == parent_def_id.to_def_id(), origin), + // Named `type Foo = impl Bar;` + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + impl_trait_fn: None, + origin, + .. + }) => ( + may_define_opaque_type(tcx, parent_def_id, opaque_hir_id), + origin, + ), + _ => (def_scope_default(), hir::OpaqueTyOrigin::TyAlias), + }; + if in_definition_scope { + let opaque_type_key = + OpaqueTypeKey { def_id: def_id.to_def_id(), substs }; + return self.fold_opaque_ty(ty, opaque_type_key, origin); + } + + debug!( + "instantiate_opaque_types_in_map: \ + encountered opaque outside its definition scope \ + def_id={:?}", + def_id, + ); + } + } + + ty + }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }) + } + + #[instrument(skip(self), level = "debug")] + fn fold_opaque_ty( + &mut self, + ty: Ty<'tcx>, + opaque_type_key: OpaqueTypeKey<'tcx>, + origin: hir::OpaqueTyOrigin, + ) -> Ty<'tcx> { + let infcx = self.infcx; + let tcx = infcx.tcx; + let OpaqueTypeKey { def_id, substs } = opaque_type_key; + + // Use the same type variable if the exact same opaque type appears more + // than once in the return type (e.g., if it's passed to a type alias). + if let Some(opaque_defn) = infcx.inner.borrow().opaque_types.get(&opaque_type_key) { + debug!("re-using cached concrete type {:?}", opaque_defn.concrete_ty.kind()); + return opaque_defn.concrete_ty; + } + + let ty_var = infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: self.value_span, + }); + + // Ideally, we'd get the span where *this specific `ty` came + // from*, but right now we just use the span from the overall + // value being folded. In simple cases like `-> impl Foo`, + // these are the same span, but not in cases like `-> (impl + // Foo, impl Bar)`. + let definition_span = self.value_span; + + { + let mut infcx = self.infcx.inner.borrow_mut(); + infcx.opaque_types.insert( + OpaqueTypeKey { def_id, substs }, + OpaqueTypeDecl { opaque_type: ty, definition_span, concrete_ty: ty_var, origin }, + ); + infcx.opaque_types_vars.insert(ty_var, ty); + } + + debug!("generated new type inference var {:?}", ty_var.kind()); + + let item_bounds = tcx.explicit_item_bounds(def_id); + + self.obligations.reserve(item_bounds.len()); + for (predicate, _) in item_bounds { + debug!(?predicate); + let predicate = predicate.subst(tcx, substs); + debug!(?predicate); + + // We can't normalize associated types from `rustc_infer`, but we can eagerly register inference variables for them. + let predicate = predicate.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |ty| match ty.kind() { + ty::Projection(projection_ty) => infcx.infer_projection( + self.param_env, + *projection_ty, + traits::ObligationCause::misc(self.value_span, self.body_id), + 0, + &mut self.obligations, + ), + _ => ty, + }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }); + debug!(?predicate); + + if let ty::PredicateKind::Projection(projection) = predicate.kind().skip_binder() { + if projection.ty.references_error() { + // No point on adding these obligations since there's a type error involved. + return tcx.ty_error(); + } + } + // Change the predicate to refer to the type variable, + // which will be the concrete type instead of the opaque type. + // This also instantiates nested instances of `impl Trait`. + let predicate = self.instantiate_opaque_types_in_map(predicate); + + let cause = + traits::ObligationCause::new(self.value_span, self.body_id, traits::OpaqueType); + + // Require that the predicate holds for the concrete type. + debug!(?predicate); + self.obligations.push(traits::Obligation::new(cause, self.param_env, predicate)); + } + + ty_var + } +} + +/// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `def_id`. +/// +/// Example: +/// ```rust +/// pub mod foo { +/// pub mod bar { +/// pub trait Bar { .. } +/// +/// pub type Baz = impl Bar; +/// +/// fn f1() -> Baz { .. } +/// } +/// +/// fn f2() -> bar::Baz { .. } +/// } +/// ``` +/// +/// Here, `def_id` is the `LocalDefId` of the defining use of the opaque type (e.g., `f1` or `f2`), +/// and `opaque_hir_id` is the `HirId` of the definition of the opaque type `Baz`. +/// For the above example, this function returns `true` for `f1` and `false` for `f2`. +fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: LocalDefId, opaque_hir_id: hir::HirId) -> bool { + let mut hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + // Named opaque types can be defined by any siblings or children of siblings. + let scope = tcx.hir().get_defining_scope(opaque_hir_id); + // We walk up the node tree until we hit the root or the scope of the opaque type. + while hir_id != scope && hir_id != hir::CRATE_HIR_ID { + hir_id = tcx.hir().get_parent_item(hir_id); + } + // Syntactically, we are allowed to define the concrete type if: + let res = hir_id == scope; + trace!( + "may_define_opaque_type(def={:?}, opaque_node={:?}) = {}", + tcx.hir().find(hir_id), + tcx.hir().get(opaque_hir_id), + res + ); + res +}
diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 9e04773..3947282 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs
@@ -99,7 +99,7 @@ /// function. We can then add implied bounds and the like from the /// closure arguments into the environment -- these should only /// apply in the closure body, so once we exit, we invoke - /// `pop_snapshot_post_closure` to remove them. + /// `pop_snapshot_post_typeck_child` to remove them. /// /// Example: /// @@ -129,12 +129,12 @@ /// seems like it'd be readily fixed if we wanted. There are /// similar leaks around givens that seem equally suspicious, to /// be honest. --nmatsakis - pub fn push_snapshot_pre_closure(&self) -> usize { + pub fn push_snapshot_pre_typeck_child(&self) -> usize { self.region_bound_pairs_accum.len() } - /// See `push_snapshot_pre_closure`. - pub fn pop_snapshot_post_closure(&mut self, len: usize) { + /// See `push_snapshot_pre_typeck_child`. + pub fn pop_snapshot_post_typeck_child(&mut self, len: usize) { self.region_bound_pairs_accum.truncate(len); }
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index a4cfadd..e4b407e 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs
@@ -15,8 +15,10 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(box_patterns)] +#![feature(derive_default_enum)] #![feature(extend_one)] #![feature(iter_zip)] +#![feature(let_else)] #![feature(never_type)] #![feature(in_band_lifetimes)] #![feature(control_flow_enum)]
diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index dce4a87..152a395 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs
@@ -46,30 +46,25 @@ obligation: PredicateObligation<'tcx>, ); - fn select_all_or_error( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec<FulfillmentError<'tcx>>>; + fn select_all_or_error(&mut self, infcx: &InferCtxt<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>>; fn select_all_with_constness_or_error( &mut self, infcx: &InferCtxt<'_, 'tcx>, _constness: hir::Constness, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { + ) -> Vec<FulfillmentError<'tcx>> { self.select_all_or_error(infcx) } - fn select_where_possible( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec<FulfillmentError<'tcx>>>; + fn select_where_possible(&mut self, infcx: &InferCtxt<'_, 'tcx>) + -> Vec<FulfillmentError<'tcx>>; // FIXME(fee1-dead) this should not provide a default body for chalk as chalk should be updated fn select_with_constness_where_possible( &mut self, infcx: &InferCtxt<'_, 'tcx>, _constness: hir::Constness, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { + ) -> Vec<FulfillmentError<'tcx>> { self.select_where_possible(infcx) }
diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 9dbfa3a..c1f302e 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -6,7 +6,6 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::Symbol; use rustc_span::{MultiSpan, Span}; use std::fmt; use std::iter; @@ -15,8 +14,7 @@ pub fn report_extra_impl_obligation( &self, error_span: Span, - item_name: Symbol, - _impl_item_def_id: DefId, + impl_item_def_id: DefId, trait_item_def_id: DefId, requirement: &dyn fmt::Display, ) -> DiagnosticBuilder<'tcx> { @@ -27,6 +25,7 @@ if let Some(trait_item_span) = self.tcx.hir().span_if_local(trait_item_def_id) { let span = self.tcx.sess.source_map().guess_head_span(trait_item_span); + let item_name = self.tcx.item_name(impl_item_def_id); err.span_label(span, format!("definition of `{}` from trait", item_name)); }
diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index e1d6982..e8622b3 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs
@@ -10,7 +10,7 @@ use rustc_hir as hir; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::{self, Const, Ty}; +use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_span::Span; pub use self::FulfillmentErrorCode::*; @@ -55,6 +55,20 @@ pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>; pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>; +impl PredicateObligation<'tcx> { + /// Flips the polarity of the inner predicate. + /// + /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. + pub fn flip_polarity(&self, tcx: TyCtxt<'tcx>) -> Option<PredicateObligation<'tcx>> { + Some(PredicateObligation { + cause: self.cause.clone(), + param_env: self.param_env, + predicate: self.predicate.flip_polarity(tcx)?, + recursion_depth: self.recursion_depth, + }) + } +} + // `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(PredicateObligation<'_>, 32); @@ -129,6 +143,10 @@ } impl<'tcx> TraitObligation<'tcx> { + pub fn polarity(&self) -> ty::ImplPolarity { + self.predicate.skip_binder().polarity + } + pub fn self_ty(&self) -> ty::Binder<'tcx, Ty<'tcx>> { self.predicate.map_bound(|p| p.self_ty()) }
diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index c4a2ece..b48ca3b 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs
@@ -70,6 +70,7 @@ } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { - self.predicate.visit_with(visitor) + self.predicate.visit_with(visitor)?; + self.param_env.visit_with(visitor) } }
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index c839f82..92f74af 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs
@@ -5,6 +5,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_middle::ty::{self, ToPredicate, TyCtxt, WithConstness}; use rustc_span::symbol::Ident; +use rustc_span::Span; pub fn anonymize_predicate<'tcx>( tcx: TyCtxt<'tcx>, @@ -97,6 +98,22 @@ elaborate_obligations(tcx, obligations) } +pub fn elaborate_predicates_with_span<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: impl Iterator<Item = (ty::Predicate<'tcx>, Span)>, +) -> Elaborator<'tcx> { + let obligations = predicates + .map(|(predicate, span)| { + predicate_obligation( + predicate, + ty::ParamEnv::empty(), + ObligationCause::dummy_with_span(span), + ) + }) + .collect(); + elaborate_obligations(tcx, obligations) +} + pub fn elaborate_obligations<'tcx>( tcx: TyCtxt<'tcx>, mut obligations: Vec<PredicateObligation<'tcx>>,
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 0861bd2..2904b3f 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs
@@ -36,9 +36,10 @@ pub(crate) input_path: Option<PathBuf>, pub(crate) output_dir: Option<PathBuf>, pub(crate) output_file: Option<PathBuf>, + pub(crate) temps_dir: Option<PathBuf>, pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>, pub(crate) override_queries: - Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::Providers)>, + Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::ExternProviders)>, } impl Compiler { @@ -57,6 +58,9 @@ pub fn output_file(&self) -> &Option<PathBuf> { &self.output_file } + pub fn temps_dir(&self) -> &Option<PathBuf> { + &self.temps_dir + } pub fn register_lints(&self) -> &Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>> { &self.register_lints } @@ -65,7 +69,14 @@ sess: &Session, attrs: &[ast::Attribute], ) -> OutputFilenames { - util::build_output_filenames(&self.input, &self.output_dir, &self.output_file, attrs, sess) + util::build_output_filenames( + &self.input, + &self.output_dir, + &self.output_file, + &self.temps_dir, + attrs, + sess, + ) } } @@ -75,7 +86,10 @@ let cfg = cfgspecs .into_iter() .map(|s| { - let sess = ParseSess::with_silent_emitter(); + let sess = ParseSess::with_silent_emitter(Some(format!( + "this error occurred on the command line: `--cfg={}`", + s + ))); let filename = FileName::cfg_spec_source_code(&s); let mut parser = new_parser_from_source_str(&sess, filename, s.to_string()); @@ -152,7 +166,7 @@ /// /// The second parameter is local providers and the third parameter is external providers. pub override_queries: - Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::Providers)>, + Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::ExternProviders)>, /// This is a callback from the driver that is called to create a codegen backend. pub make_codegen_backend: @@ -183,6 +197,8 @@ ); } + let temps_dir = sess.opts.debugging_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o)); + let compiler = Compiler { sess, codegen_backend, @@ -190,6 +206,7 @@ input_path: config.input_path, output_dir: config.output_dir, output_file: config.output_file, + temps_dir, register_lints: config.register_lints, override_queries: config.override_queries, };
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index bcfa0ef..d3917df 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs
@@ -19,7 +19,7 @@ use rustc_metadata::{encode_metadata, EncodedMetadata}; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; -use rustc_middle::ty::query::Providers; +use rustc_middle::ty::query::{ExternProviders, Providers}; use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt}; use rustc_mir_build as mir_build; use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_attr}; @@ -35,7 +35,7 @@ use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::FileName; +use rustc_span::{FileName, MultiSpan}; use rustc_trait_selection::traits; use rustc_typeck as typeck; use tempfile::Builder as TempFileBuilder; @@ -47,7 +47,7 @@ use std::io::{self, BufWriter, Write}; use std::lazy::SyncLazy; use std::marker::PhantomPinned; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::pin::Pin; use std::rc::Rc; use std::{env, fs, iter}; @@ -450,6 +450,19 @@ }); } + // Gate identifiers containing invalid Unicode codepoints that were recovered during lexing. + sess.parse_sess.bad_unicode_identifiers.with_lock(|identifiers| { + let mut identifiers: Vec<_> = identifiers.drain().collect(); + identifiers.sort_by_key(|&(key, _)| key); + for (ident, mut spans) in identifiers.into_iter() { + spans.sort(); + sess.diagnostic().span_err( + MultiSpan::from(spans), + &format!("identifiers cannot contain emoji: `{}`", ident), + ); + } + }); + Ok(krate) } @@ -536,7 +549,7 @@ None } -fn output_contains_path(output_paths: &[PathBuf], input_path: &PathBuf) -> bool { +fn output_contains_path(output_paths: &[PathBuf], input_path: &Path) -> bool { let input_path = input_path.canonicalize().ok(); if input_path.is_none() { return false; @@ -552,7 +565,7 @@ check_output(output_paths, check) } -fn escape_dep_filename(filename: &String) -> String { +fn escape_dep_filename(filename: &str) -> String { // Apparently clang and gcc *only* escape spaces: // https://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 filename.replace(" ", "\\ ") @@ -692,6 +705,7 @@ &compiler.input, &compiler.output_dir, &compiler.output_file, + &compiler.temps_dir, &krate.attrs, sess, ); @@ -722,6 +736,13 @@ } } + if let Some(ref dir) = compiler.temps_dir { + if fs::create_dir_all(dir).is_err() { + sess.err("failed to find or create the directory specified by `--temps-dir`"); + return Err(ErrorReported); + } + } + write_out_deps(sess, boxed_resolver, &outputs, &output_paths); let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo) @@ -764,8 +785,8 @@ *providers }); -pub static DEFAULT_EXTERN_QUERY_PROVIDERS: SyncLazy<Providers> = SyncLazy::new(|| { - let mut extern_providers = *DEFAULT_QUERY_PROVIDERS; +pub static DEFAULT_EXTERN_QUERY_PROVIDERS: SyncLazy<ExternProviders> = SyncLazy::new(|| { + let mut extern_providers = ExternProviders::default(); rustc_metadata::provide_extern(&mut extern_providers); rustc_codegen_ssa::provide_extern(&mut extern_providers); extern_providers @@ -816,7 +837,6 @@ codegen_backend.provide(&mut local_providers); let mut extern_providers = *DEFAULT_EXTERN_QUERY_PROVIDERS; - codegen_backend.provide(&mut extern_providers); codegen_backend.provide_extern(&mut extern_providers); if let Some(callback) = compiler.override_queries { @@ -838,6 +858,7 @@ dep_graph, queries.on_disk_cache.as_ref().map(OnDiskCache::as_dyn), queries.as_dyn(), + rustc_query_impl::query_callbacks(arena), crate_name, outputs, )
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 844e5ab..6147311 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs
@@ -5,7 +5,9 @@ use rustc_session::config::InstrumentCoverage; use rustc_session::config::Strip; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; -use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; +use rustc_session::config::{ + rustc_optgroups, ErrorOutputType, ExternLocation, LocationDetail, Options, Passes, +}; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; use rustc_session::config::{ Externs, OutputType, OutputTypes, SymbolManglingVersion, WasiExecModel, @@ -18,7 +20,9 @@ use rustc_span::symbol::sym; use rustc_span::SourceFileHashAlgorithm; use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy}; -use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, TlsModel}; +use rustc_target::spec::{ + RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, +}; use std::collections::{BTreeMap, BTreeSet}; use std::iter::FromIterator; @@ -549,6 +553,7 @@ untracked!(remark, Passes::Some(vec![String::from("pass1"), String::from("pass2")])); untracked!(rpath, true); untracked!(save_temps, true); + untracked!(strip, Strip::Debuginfo); macro_rules! tracked { ($name: ident, $non_default_value: expr) => { @@ -633,6 +638,7 @@ // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. // This list is in alphabetical order. + untracked!(assert_incr_state, Some(String::from("loaded"))); untracked!(ast_json, true); untracked!(ast_json_noexpand, true); untracked!(borrowck, String::from("other")); @@ -682,7 +688,7 @@ untracked!(self_profile_events, Some(vec![String::new()])); untracked!(span_debug, true); untracked!(span_free_formats, true); - untracked!(strip, Strip::Debuginfo); + untracked!(temps_dir, Some(String::from("abc"))); untracked!(terminal_width, Some(80)); untracked!(threads, 99); untracked!(time, true); @@ -709,8 +715,8 @@ // This list is in alphabetical order. tracked!(allow_features, Some(vec![String::from("lang_items")])); tracked!(always_encode_mir, true); - tracked!(assume_incomplete_release, true); tracked!(asm_comments, true); + tracked!(assume_incomplete_release, true); tracked!(binary_dep_depinfo, true); tracked!(chalk, true); tracked!(codegen_backend, Some("abc".to_string())); @@ -720,19 +726,19 @@ tracked!(dep_info_omit_d_target, true); tracked!(dual_proc_macros, true); tracked!(fewer_names, Some(true)); - tracked!(force_overflow_checks, Some(true)); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99))); tracked!(function_sections, Some(false)); tracked!(human_readable_cgu_names, true); tracked!(inline_in_all_cgus, Some(true)); tracked!(inline_mir, Some(true)); - tracked!(inline_mir_threshold, Some(123)); tracked!(inline_mir_hint_threshold, Some(123)); + tracked!(inline_mir_threshold, Some(123)); tracked!(instrument_coverage, Some(InstrumentCoverage::All)); tracked!(instrument_mcount, true); tracked!(link_only, true); tracked!(llvm_plugins, vec![String::from("plugin_name")]); + tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); tracked!(merge_functions, Some(MergeFunctions::Disabled)); tracked!(mir_emit_retag, true); tracked!(mir_opt_level, Some(4)); @@ -741,11 +747,13 @@ tracked!(new_llvm_pass_manager, Some(true)); tracked!(no_generate_arange_section, true); tracked!(no_link, true); + tracked!(no_unique_section_names, true); tracked!(no_profiler_runtime, true); tracked!(osx_rpath_install_name, true); tracked!(panic_abort_tests, true); tracked!(panic_in_drop, PanicStrategy::Abort); tracked!(partially_uninit_const_threshold, Some(123)); + tracked!(pick_stable_methods_before_any_unstable, false); tracked!(plt, Some(true)); tracked!(polonius, true); tracked!(precise_enum_drop_elaboration, false); @@ -757,7 +765,6 @@ tracked!(relax_elf_relocations, Some(true)); tracked!(relro_level, Some(RelroLevel::Full)); tracked!(remap_cwd_prefix, Some(PathBuf::from("abc"))); - tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc"))); tracked!(report_delayed_bugs, true); tracked!(sanitizer, SanitizerSet::ADDRESS); tracked!(sanitizer_memory_track_origins, 2); @@ -765,15 +772,17 @@ tracked!(saturating_float_casts, Some(true)); tracked!(share_generics, Some(true)); tracked!(show_span, Some(String::from("abc"))); + tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc"))); tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1)); + tracked!(stack_protector, StackProtector::All); tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0)); tracked!(teach, true); tracked!(thinlto, Some(true)); tracked!(thir_unsafeck, true); - tracked!(tune_cpu, Some(String::from("abc"))); tracked!(tls_model, Some(TlsModel::GeneralDynamic)); tracked!(trap_unreachable, Some(false)); tracked!(treat_err_as_bug, NonZeroUsize::new(1)); + tracked!(tune_cpu, Some(String::from("abc"))); tracked!(unleash_the_miri_inside_of_you, true); tracked!(use_ctors_section, Some(true)); tracked!(verify_llvm_ir, true);
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index cffb087..04e183a 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs
@@ -604,6 +604,7 @@ input: &Input, odir: &Option<PathBuf>, ofile: &Option<PathBuf>, + temps_dir: &Option<PathBuf>, attrs: &[ast::Attribute], sess: &Session, ) -> OutputFilenames { @@ -626,6 +627,7 @@ dirpath, stem, None, + temps_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), ) @@ -654,6 +656,7 @@ out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(), out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(), ofile, + temps_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), ) @@ -776,7 +779,7 @@ fn visit_item_kind(&mut self, i: &mut ast::ItemKind) { let is_const = match i { ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, - ast::ItemKind::Fn(box ast::FnKind(_, ref sig, _, _)) => Self::is_sig_const(sig), + ast::ItemKind::Fn(box ast::Fn { ref sig, .. }) => Self::is_sig_const(sig), _ => false, }; self.run(is_const, |s| noop_visit_item_kind(i, s)) @@ -785,7 +788,7 @@ fn flat_map_trait_item(&mut self, i: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { let is_const = match i.kind { ast::AssocItemKind::Const(..) => true, - ast::AssocItemKind::Fn(box ast::FnKind(_, ref sig, _, _)) => Self::is_sig_const(sig), + ast::AssocItemKind::Fn(box ast::Fn { ref sig, .. }) => Self::is_sig_const(sig), _ => false, }; self.run(is_const, |s| noop_flat_map_assoc_item(i, s))
diff --git a/compiler/rustc_lexer/Cargo.toml b/compiler/rustc_lexer/Cargo.toml index 60c146f..35af110 100644 --- a/compiler/rustc_lexer/Cargo.toml +++ b/compiler/rustc_lexer/Cargo.toml
@@ -17,6 +17,7 @@ # Note that this crate purposefully does not depend on other rustc crates [dependencies] unicode-xid = "0.2.0" +unic-emoji-char = "0.9.0" [dev-dependencies] expect-test = "1.0"
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index b64a891c..44b002f 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs
@@ -64,6 +64,8 @@ /// "ident" or "continue" /// At this step keywords are also considered identifiers. Ident, + /// Like the above, but containing invalid unicode codepoints. + InvalidIdent, /// "r#ident" RawIdent, /// An unknown prefix like `foo#`, `foo'`, `foo"`. Note that only the @@ -411,6 +413,10 @@ let kind = Str { terminated }; Literal { kind, suffix_start } } + // Identifier starting with an emoji. Only lexed for graceful error recovery. + c if !c.is_ascii() && unic_emoji_char::is_emoji(c) => { + self.fake_ident_or_unknown_prefix() + } _ => Unknown, }; Token::new(token_kind, self.len_consumed()) @@ -492,10 +498,28 @@ // we see a prefix here, it is definitely an unknown prefix. match self.first() { '#' | '"' | '\'' => UnknownPrefix, + c if !c.is_ascii() && unic_emoji_char::is_emoji(c) => { + self.fake_ident_or_unknown_prefix() + } _ => Ident, } } + fn fake_ident_or_unknown_prefix(&mut self) -> TokenKind { + // Start is already eaten, eat the rest of identifier. + self.eat_while(|c| { + unicode_xid::UnicodeXID::is_xid_continue(c) + || (!c.is_ascii() && unic_emoji_char::is_emoji(c)) + || c == '\u{200d}' + }); + // Known prefixes must have been handled earlier. So if + // we see a prefix here, it is definitely an unknown prefix. + match self.first() { + '#' | '"' | '\'' => UnknownPrefix, + _ => InvalidIdent, + } + } + fn number(&mut self, first_digit: char) -> LiteralKind { debug_assert!('0' <= self.prev() && self.prev() <= '9'); let mut base = Base::Decimal;
diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index 804dc65..d789237 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs
@@ -329,7 +329,7 @@ callback(start..end, Err(EscapeError::MultipleSkippedLinesWarning)); } let tail = &tail[first_non_space..]; - if let Some(c) = tail.chars().next() { + if let Some(c) = tail.chars().nth(0) { // For error reporting, we would like the span to contain the character that was not // skipped. The +1 is necessary to account for the leading \ that started the escape. let end = start + first_non_space + c.len_utf8() + 1;
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index d147148..d8883b0 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs
@@ -134,9 +134,8 @@ Applicability::MachineApplicable, ); if self.for_expr_span == expr.span { - let expr_span = expr.span.ctxt().outer_expn_data().call_site; diag.span_suggestion( - receiver_arg.span.shrink_to_hi().to(expr_span.shrink_to_hi()), + receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), "or remove `.into_iter()` to iterate by value", String::new(), Applicability::MaybeIncorrect,
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 57c1c8f..f2e4e70 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs
@@ -32,8 +32,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; -use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType}; -use rustc_feature::{GateIssue, Stability}; +use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; @@ -369,12 +368,12 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { match it.kind { - ast::ItemKind::Trait(box ast::TraitKind(_, ast::Unsafe::Yes(_), ..)) => self + ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => self .report_unsafe(cx, it.span, |lint| { lint.build("declaration of an `unsafe` trait").emit() }), - ast::ItemKind::Impl(box ast::ImplKind { unsafety: ast::Unsafe::Yes(_), .. }) => self + ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => self .report_unsafe(cx, it.span, |lint| { lint.build("implementation of an `unsafe` trait").emit() }), @@ -657,6 +656,24 @@ return; } + // If the method is an impl for an item with docs_hidden, don't doc. + if method_context(cx, impl_item.hir_id()) == MethodLateContext::PlainImpl { + let parent = cx.tcx.hir().get_parent_did(impl_item.hir_id()); + let impl_ty = cx.tcx.type_of(parent); + let outerdef = match impl_ty.kind() { + ty::Adt(def, _) => Some(def.did), + ty::Foreign(def_id) => Some(*def_id), + _ => None, + }; + let is_hidden = match outerdef { + Some(id) => cx.tcx.is_doc_hidden(id), + None => false, + }; + if is_hidden { + return; + } + } + let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id()); self.check_missing_docs_attrs(cx, impl_item.def_id, impl_item.span, article, desc); } @@ -903,7 +920,7 @@ // This is a hard error in future editions; avoid linting and erroring return; } - if let ast::AssocItemKind::Fn(box FnKind(_, ref sig, _, _)) = it.kind { + if let ast::AssocItemKind::Fn(box Fn { ref sig, .. }) = it.kind { for arg in sig.decl.inputs.iter() { if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { if ident.name == kw::Empty { @@ -941,7 +958,7 @@ pub struct DeprecatedAttr { // This is not free to compute, so we want to keep it around, rather than // compute it for every attribute. - depr_attrs: Vec<&'static (Symbol, AttributeType, AttributeTemplate, AttributeGate)>, + depr_attrs: Vec<&'static BuiltinAttribute>, } impl_lint_pass!(DeprecatedAttr => []); @@ -972,14 +989,14 @@ impl EarlyLintPass for DeprecatedAttr { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { - for &&(n, _, _, ref g) in &self.depr_attrs { - if attr.ident().map(|ident| ident.name) == Some(n) { + for BuiltinAttribute { name, gate, .. } in &self.depr_attrs { + if attr.ident().map(|ident| ident.name) == Some(*name) { if let &AttributeGate::Gated( Stability::Deprecated(link, suggestion), name, reason, _, - ) = g + ) = gate { let msg = format!("use of deprecated attribute `{}`: {}. See {}", name, reason, link); @@ -3112,18 +3129,13 @@ false } - if let rustc_hir::ExprKind::Unary(ref un_op, ref expr_deref) = expr.kind { - if let rustc_hir::UnOp::Deref = un_op { - if is_null_ptr(cx, expr_deref) { - cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| { - let mut err = lint.build("dereferencing a null pointer"); - err.span_label( - expr.span, - "this code causes undefined behavior when executed", - ); - err.emit(); - }); - } + if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind { + if is_null_ptr(cx, expr_deref) { + cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| { + let mut err = lint.build("dereferencing a null pointer"); + err.span_label(expr.span, "this code causes undefined behavior when executed"); + err.emit(); + }); } } } @@ -3178,7 +3190,7 @@ let snippet = template_snippet.as_str(); if let Some(pos) = snippet.find(needle) { let end = pos - + &snippet[pos..] + + snippet[pos..] .find(|c| c == ':') .unwrap_or(snippet[pos..].len() - 1); let inner = InnerSpan::new(pos, end);
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 6fd0a5b..4c936de 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs
@@ -16,9 +16,9 @@ use self::TargetLint::*; -use crate::hidden_unicode_codepoints::UNICODE_TEXT_FLOW_CHARS; use crate::levels::{is_known_lint_tool, LintLevelsBuilder}; use crate::passes::{EarlyLintPassObject, LateLintPassObject}; +use ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync; @@ -602,7 +602,7 @@ let spans: Vec<_> = content .char_indices() .filter_map(|(i, c)| { - UNICODE_TEXT_FLOW_CHARS.contains(&c).then(|| { + TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { let lo = span.lo() + BytePos(2 + i as u32); (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) })
diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index 1bcdcb8..fde84be 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
@@ -1,4 +1,5 @@ use crate::{EarlyContext, EarlyLintPass, LintContext}; +use ast::util::unicode::{contains_text_flow_control_chars, TEXT_FLOW_CONTROL_CHARS}; use rustc_ast as ast; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_span::{BytePos, Span, Symbol}; @@ -37,11 +38,6 @@ declare_lint_pass!(HiddenUnicodeCodepoints => [TEXT_DIRECTION_CODEPOINT_IN_LITERAL]); -crate const UNICODE_TEXT_FLOW_CHARS: &[char] = &[ - '\u{202A}', '\u{202B}', '\u{202D}', '\u{202E}', '\u{2066}', '\u{2067}', '\u{2068}', '\u{202C}', - '\u{2069}', -]; - impl HiddenUnicodeCodepoints { fn lint_text_direction_codepoint( &self, @@ -57,7 +53,7 @@ .as_str() .char_indices() .filter_map(|(i, c)| { - UNICODE_TEXT_FLOW_CHARS.contains(&c).then(|| { + TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { let lo = span.lo() + BytePos(i as u32 + padding); (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) }) @@ -131,7 +127,7 @@ impl EarlyLintPass for HiddenUnicodeCodepoints { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { if let ast::AttrKind::DocComment(_, comment) = attr.kind { - if comment.as_str().contains(UNICODE_TEXT_FLOW_CHARS) { + if contains_text_flow_control_chars(&comment.as_str()) { self.lint_text_direction_codepoint(cx, comment, attr.span, 0, false, "doc comment"); } } @@ -142,7 +138,7 @@ let (text, span, padding) = match &expr.kind { ast::ExprKind::Lit(ast::Lit { token, kind, span }) => { let text = token.symbol; - if !text.as_str().contains(UNICODE_TEXT_FLOW_CHARS) { + if !contains_text_flow_control_chars(&text.as_str()) { return; } let padding = match kind {
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 50a0d21..c64a67b 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs
@@ -101,33 +101,31 @@ fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx>) { match &ty.kind { - TyKind::Path(qpath) => { - if let QPath::Resolved(_, path) = qpath { - if let Some(last) = path.segments.iter().last() { - if lint_ty_kind_usage(cx, last) { - cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| { - lint.build("usage of `ty::TyKind`") - .help("try using `Ty` instead") - .emit(); - }) - } else { - if ty.span.from_expansion() { - return; - } - if let Some(t) = is_ty_or_ty_ctxt(cx, ty) { - if path.segments.len() > 1 { - cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| { - lint.build(&format!("usage of qualified `ty::{}`", t)) - .span_suggestion( - path.span, - "try using it unqualified", - t, - // The import probably needs to be changed - Applicability::MaybeIncorrect, - ) - .emit(); - }) - } + TyKind::Path(QPath::Resolved(_, path)) => { + if let Some(last) = path.segments.iter().last() { + if lint_ty_kind_usage(cx, last) { + cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| { + lint.build("usage of `ty::TyKind`") + .help("try using `Ty` instead") + .emit(); + }) + } else { + if ty.span.from_expansion() { + return; + } + if let Some(t) = is_ty_or_ty_ctxt(cx, ty) { + if path.segments.len() > 1 { + cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| { + lint.build(&format!("usage of qualified `ty::{}`", t)) + .span_suggestion( + path.span, + "try using it unqualified", + t, + // The import probably needs to be changed + Applicability::MaybeIncorrect, + ) + .emit(); + }) } } } @@ -169,37 +167,30 @@ } fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, ty: &Ty<'_>) -> Option<String> { - if let TyKind::Path(qpath) = &ty.kind { - if let QPath::Resolved(_, path) = qpath { - match path.res { - Res::Def(_, def_id) => { - if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(def_id) - { - return Some(format!( - "{}{}", - name, - gen_args(path.segments.last().unwrap()) - )); - } + if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind { + match path.res { + Res::Def(_, def_id) => { + if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(def_id) { + return Some(format!("{}{}", name, gen_args(path.segments.last().unwrap()))); } - // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait. - Res::SelfTy(None, Some((did, _))) => { - if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() { - if let Some(name @ (sym::Ty | sym::TyCtxt)) = - cx.tcx.get_diagnostic_name(adt.did) - { - // NOTE: This path is currently unreachable as `Ty<'tcx>` is - // defined as a type alias meaning that `impl<'tcx> Ty<'tcx>` - // is not actually allowed. - // - // I(@lcnr) still kept this branch in so we don't miss this - // if we ever change it in the future. - return Some(format!("{}<{}>", name, substs[0])); - } - } - } - _ => (), } + // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait. + Res::SelfTy(None, Some((did, _))) => { + if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() { + if let Some(name @ (sym::Ty | sym::TyCtxt)) = + cx.tcx.get_diagnostic_name(adt.did) + { + // NOTE: This path is currently unreachable as `Ty<'tcx>` is + // defined as a type alias meaning that `impl<'tcx> Ty<'tcx>` + // is not actually allowed. + // + // I(@lcnr) still kept this branch in so we don't miss this + // if we ever change it in the future. + return Some(format!("{}<{}>", name, substs[0])); + } + } + } + _ => (), } } @@ -238,8 +229,7 @@ impl EarlyLintPass for LintPassImpl { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let ast::ItemKind::Impl(box ast::ImplKind { of_trait: Some(lint_pass), .. }) = &item.kind - { + if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind { if let Some(last) = lint_pass.path.segments.last() { if last.ident.name == sym::LintPass { let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index f6514dd..507b442 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs
@@ -30,7 +30,7 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(crate_visibility_modifier)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(iter_order_by)] #![feature(iter_zip)] #![feature(never_type)]
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 48b955e..da1edcf 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs
@@ -670,7 +670,7 @@ /// /// ### Explanation /// - /// The parenthesis are not needed, and should be removed. This is the + /// The parentheses are not needed, and should be removed. This is the /// preferred style for writing these expressions. pub(super) UNUSED_PARENS, Warn,
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 8f4f54d..c1a53c3 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -1959,7 +1959,7 @@ /// [issue #50504]: https://github.com/rust-lang/rust/issues/50504 /// [future-incompatible]: ../index.md#future-incompatible-lints pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, - Warn, + Deny, "detects proc macro derives using inaccessible names from parent modules", @future_incompatible = FutureIncompatibleInfo { reference: "issue #83583 <https://github.com/rust-lang/rust/issues/83583>", @@ -3256,7 +3256,7 @@ /// [issue #83125]: https://github.com/rust-lang/rust/issues/83125 /// [future-incompatible]: ../index.md#future-incompatible-lints pub PROC_MACRO_BACK_COMPAT, - Warn, + Deny, "detects usage of old versions of certain proc-macro crates", @future_incompatible = FutureIncompatibleInfo { reference: "issue #83125 <https://github.com/rust-lang/rust/issues/83125>",
diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 36a6d2c..943ce58 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs
@@ -288,7 +288,7 @@ let path = PathBuf::from(s); println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); if target.contains("windows") { - println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname); + println!("cargo:rustc-link-lib=static:-bundle={}", stdcppname); } else { println!("cargo:rustc-link-lib=static={}", stdcppname); } @@ -302,6 +302,6 @@ // Libstdc++ depends on pthread which Rust doesn't link on MinGW // since nothing else requires it. if target.contains("windows-gnu") { - println!("cargo:rustc-link-lib=static-nobundle=pthread"); + println!("cargo:rustc-link-lib=static:-bundle=pthread"); } }
diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 35cca04..8cd2bd1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
@@ -98,10 +98,7 @@ extern "C" void LLVMRustCoverageWriteFuncSectionNameToString(LLVMModuleRef M, RustStringRef Str) { -#if LLVM_VERSION_GE(11, 0) WriteSectionNameToString(M, IPSK_covfun, Str); -// else do nothing; the `Version` check will abort codegen on the Rust side -#endif } extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) { @@ -111,9 +108,5 @@ } extern "C" uint32_t LLVMRustCoverageMappingVersion() { -#if LLVM_VERSION_GE(11, 0) return coverage::CovMapVersion::Version4; -#else - return coverage::CovMapVersion::Version3; -#endif }
diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index 0b1b68d..e2ce7da 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
@@ -18,7 +18,6 @@ #include "llvm/Support/Host.h" #include "llvm/Support/Memory.h" #include "llvm/Support/SourceMgr.h" -#include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" @@ -80,6 +79,9 @@ InaccessibleMemOnly = 27, SanitizeHWAddress = 28, WillReturn = 29, + StackProtectReq = 30, + StackProtectStrong = 31, + StackProtect = 32, }; typedef struct OpaqueRustString *RustStringRef;
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 87f423f..4f77db8 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -21,6 +21,11 @@ #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" +#if LLVM_VERSION_LT(14, 0) +#include "llvm/Support/TargetRegistry.h" +#else +#include "llvm/MC/TargetRegistry.h" +#endif #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" @@ -49,10 +54,6 @@ DEFINE_STDCXX_CONVERSION_FUNCTIONS(Pass, LLVMPassRef) DEFINE_STDCXX_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) -#if LLVM_VERSION_LT(11, 0) -DEFINE_STDCXX_CONVERSION_FUNCTIONS(PassManagerBuilder, - LLVMPassManagerBuilderRef) -#endif extern "C" void LLVMInitializePasses() { PassRegistry &Registry = *PassRegistry::getPassRegistry(); @@ -74,6 +75,10 @@ /* ProcName */ "rustc"); } +extern "C" void LLVMTimeTraceProfilerFinishThread() { + timeTraceProfilerFinishThread(); +} + extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) { StringRef FN(FileName); std::error_code EC; @@ -461,6 +466,7 @@ LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat, bool FunctionSections, bool DataSections, + bool UniqueSectionNames, bool TrapUnreachable, bool Singlethread, bool AsmComments, @@ -490,6 +496,7 @@ } Options.DataSections = DataSections; Options.FunctionSections = FunctionSections; + Options.UniqueSectionNames = UniqueSectionNames; Options.MCOptions.AsmVerbose = AsmComments; Options.MCOptions.PreserveAsmComments = AsmComments; Options.MCOptions.ABIName = ABIStr; @@ -680,7 +687,6 @@ PassInstrumentationCallbacks& PIC, void* LlvmSelfProfiler, LLVMRustSelfProfileBeforePassCallback BeforePassCallback, LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { -#if LLVM_VERSION_GE(12, 0) PIC.registerBeforeNonSkippedPassCallback([LlvmSelfProfiler, BeforePassCallback]( StringRef Pass, llvm::Any Ir) { std::string PassName = Pass.str(); @@ -698,25 +704,6 @@ [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, const PreservedAnalyses &Preserved) { AfterPassCallback(LlvmSelfProfiler); }); -#else - PIC.registerBeforePassCallback([LlvmSelfProfiler, BeforePassCallback]( - StringRef Pass, llvm::Any Ir) { - std::string PassName = Pass.str(); - std::string IrName = LLVMRustwrappedIrGetName(Ir); - BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str()); - return true; - }); - - PIC.registerAfterPassCallback( - [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) { - AfterPassCallback(LlvmSelfProfiler); - }); - - PIC.registerAfterPassInvalidatedCallback( - [LlvmSelfProfiler, AfterPassCallback](StringRef Pass) { - AfterPassCallback(LlvmSelfProfiler); - }); -#endif PIC.registerBeforeAnalysisCallback([LlvmSelfProfiler, BeforePassCallback]( StringRef Pass, llvm::Any Ir) { @@ -777,22 +764,13 @@ PTO.LoopInterleaving = UnrollLoops; PTO.LoopVectorization = LoopVectorize; PTO.SLPVectorization = SLPVectorize; -#if LLVM_VERSION_GE(12, 0) PTO.MergeFunctions = MergeFunctions; -#else - // MergeFunctions is not supported by NewPM in older LLVM versions. - (void) MergeFunctions; -#endif // FIXME: We may want to expose this as an option. bool DebugPassManager = false; PassInstrumentationCallbacks PIC; -#if LLVM_VERSION_GE(12, 0) StandardInstrumentations SI(DebugPassManager); -#else - StandardInstrumentations SI; -#endif SI.registerCallbacks(PIC); if (LlvmSelfProfiler){ @@ -816,18 +794,14 @@ PGOOptions::NoCSAction, DebugInfoForProfiling); } -#if LLVM_VERSION_GE(12, 0) && !LLVM_VERSION_GE(13,0) - PassBuilder PB(DebugPassManager, TM, PTO, PGOOpt, &PIC); -#else - PassBuilder PB(TM, PTO, PGOOpt, &PIC); -#endif - #if LLVM_VERSION_GE(13, 0) + PassBuilder PB(TM, PTO, PGOOpt, &PIC); LoopAnalysisManager LAM; FunctionAnalysisManager FAM; CGSCCAnalysisManager CGAM; ModuleAnalysisManager MAM; #else + PassBuilder PB(DebugPassManager, TM, PTO, PGOOpt, &PIC); LoopAnalysisManager LAM(DebugPassManager); FunctionAnalysisManager FAM(DebugPassManager); CGSCCAnalysisManager CGAM(DebugPassManager); @@ -852,13 +826,8 @@ // PassBuilder does not create a pipeline. std::vector<std::function<void(ModulePassManager &, OptimizationLevel)>> PipelineStartEPCallbacks; -#if LLVM_VERSION_GE(11, 0) std::vector<std::function<void(ModulePassManager &, OptimizationLevel)>> OptimizerLastEPCallbacks; -#else - std::vector<std::function<void(FunctionPassManager &, OptimizationLevel)>> - OptimizerLastEPCallbacks; -#endif if (VerifyIR) { PipelineStartEPCallbacks.push_back( @@ -891,7 +860,6 @@ SanitizerOptions->SanitizeMemoryTrackOrigins, SanitizerOptions->SanitizeMemoryRecover, /*CompileKernel=*/false); -#if LLVM_VERSION_GE(11, 0) OptimizerLastEPCallbacks.push_back( [Options](ModulePassManager &MPM, OptimizationLevel Level) { #if LLVM_VERSION_GE(14, 0) @@ -902,22 +870,9 @@ MPM.addPass(createModuleToFunctionPassAdaptor(MemorySanitizerPass(Options))); } ); -#else - PipelineStartEPCallbacks.push_back( - [Options](ModulePassManager &MPM, OptimizationLevel Level) { - MPM.addPass(MemorySanitizerPass(Options)); - } - ); - OptimizerLastEPCallbacks.push_back( - [Options](FunctionPassManager &FPM, OptimizationLevel Level) { - FPM.addPass(MemorySanitizerPass(Options)); - } - ); -#endif } if (SanitizerOptions->SanitizeThread) { -#if LLVM_VERSION_GE(11, 0) OptimizerLastEPCallbacks.push_back( [](ModulePassManager &MPM, OptimizationLevel Level) { #if LLVM_VERSION_GE(14, 0) @@ -928,63 +883,31 @@ MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); } ); -#else - PipelineStartEPCallbacks.push_back( - [](ModulePassManager &MPM, OptimizationLevel Level) { - MPM.addPass(ThreadSanitizerPass()); - } - ); - OptimizerLastEPCallbacks.push_back( - [](FunctionPassManager &FPM, OptimizationLevel Level) { - FPM.addPass(ThreadSanitizerPass()); - } - ); -#endif } if (SanitizerOptions->SanitizeAddress) { -#if LLVM_VERSION_GE(11, 0) OptimizerLastEPCallbacks.push_back( [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>()); +#if LLVM_VERSION_GE(14, 0) + AddressSanitizerOptions opts = AddressSanitizerOptions{ + /*CompileKernel=*/false, + SanitizerOptions->SanitizeAddressRecover, + /*UseAfterScope=*/true, + AsanDetectStackUseAfterReturnMode::Runtime, + }; + MPM.addPass(ModuleAddressSanitizerPass(opts)); +#else MPM.addPass(ModuleAddressSanitizerPass( /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); -#if LLVM_VERSION_GE(14, 0) - AddressSanitizerOptions opts(/*CompileKernel=*/false, - SanitizerOptions->SanitizeAddressRecover, - /*UseAfterScope=*/true, - AsanDetectStackUseAfterReturnMode::Runtime); - MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass(opts))); -#else MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass( /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, /*UseAfterScope=*/true))); #endif } ); -#else - PipelineStartEPCallbacks.push_back( - [&](ModulePassManager &MPM, OptimizationLevel Level) { - MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>()); - } - ); - OptimizerLastEPCallbacks.push_back( - [SanitizerOptions](FunctionPassManager &FPM, OptimizationLevel Level) { - FPM.addPass(AddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, - /*UseAfterScope=*/true)); - } - ); - PipelineStartEPCallbacks.push_back( - [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { - MPM.addPass(ModuleAddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); - } - ); -#endif } if (SanitizerOptions->SanitizeHWAddress) { -#if LLVM_VERSION_GE(11, 0) OptimizerLastEPCallbacks.push_back( [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { #if LLVM_VERSION_GE(14, 0) @@ -998,14 +921,6 @@ #endif } ); -#else - PipelineStartEPCallbacks.push_back( - [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { - MPM.addPass(HWAddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover)); - } - ); -#endif } } @@ -1020,7 +935,6 @@ // At the same time, the LTO pipelines do support O0 and using them is required. bool IsLTO = OptStage == LLVMRustOptStage::ThinLTO || OptStage == LLVMRustOptStage::FatLTO; if (OptLevel == OptimizationLevel::O0 && !IsLTO) { -#if LLVM_VERSION_GE(12, 0) for (const auto &C : PipelineStartEPCallbacks) PB.registerPipelineStartEPCallback(C); for (const auto &C : OptimizerLastEPCallbacks) @@ -1028,40 +942,9 @@ // Pass false as we manually schedule ThinLTOBufferPasses below. MPM = PB.buildO0DefaultPipeline(OptLevel, /* PreLinkLTO */ false); -#else - for (const auto &C : PipelineStartEPCallbacks) - C(MPM, OptLevel); - -# if LLVM_VERSION_GE(11, 0) - for (const auto &C : OptimizerLastEPCallbacks) - C(MPM, OptLevel); -# else - if (!OptimizerLastEPCallbacks.empty()) { - FunctionPassManager FPM(DebugPassManager); - for (const auto &C : OptimizerLastEPCallbacks) - C(FPM, OptLevel); - MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); - } -# endif - - MPM.addPass(AlwaysInlinerPass(EmitLifetimeMarkers)); - - if (PGOOpt) { - PB.addPGOInstrPassesForO0( - MPM, DebugPassManager, PGOOpt->Action == PGOOptions::IRInstr, - /*IsCS=*/false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile); - } -#endif } else { -#if LLVM_VERSION_GE(12, 0) for (const auto &C : PipelineStartEPCallbacks) PB.registerPipelineStartEPCallback(C); -#else - for (const auto &C : PipelineStartEPCallbacks) - PB.registerPipelineStartEPCallback([C, OptLevel](ModulePassManager &MPM) { - C(MPM, OptLevel); - }); -#endif if (OptStage != LLVMRustOptStage::PreLinkThinLTO) { for (const auto &C : OptimizerLastEPCallbacks) PB.registerOptimizerLastEPCallback(C); @@ -1072,7 +955,6 @@ MPM = PB.buildPerModuleDefaultPipeline(OptLevel, DebugPassManager); break; case LLVMRustOptStage::PreLinkThinLTO: -#if LLVM_VERSION_GE(12, 0) MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel); // The ThinLTOPreLink pipeline already includes ThinLTOBuffer passes. However, callback // passes may still run afterwards. This means we need to run the buffer passes again. @@ -1080,44 +962,20 @@ // before the RequiredLTOPreLinkPasses, in which case we can remove these hacks. if (OptimizerLastEPCallbacks.empty()) NeedThinLTOBufferPasses = false; -#else - MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); -#endif -#if LLVM_VERSION_GE(11, 0) for (const auto &C : OptimizerLastEPCallbacks) C(MPM, OptLevel); -#else - if (!OptimizerLastEPCallbacks.empty()) { - FunctionPassManager FPM(DebugPassManager); - for (const auto &C : OptimizerLastEPCallbacks) - C(FPM, OptLevel); - MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); - } -#endif break; case LLVMRustOptStage::PreLinkFatLTO: -#if LLVM_VERSION_GE(12, 0) MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel); NeedThinLTOBufferPasses = false; -#else - MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); -#endif break; case LLVMRustOptStage::ThinLTO: // FIXME: Does it make sense to pass the ModuleSummaryIndex? // It only seems to be needed for C++ specific optimizations. -#if LLVM_VERSION_GE(12, 0) MPM = PB.buildThinLTODefaultPipeline(OptLevel, nullptr); -#else - MPM = PB.buildThinLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); -#endif break; case LLVMRustOptStage::FatLTO: -#if LLVM_VERSION_GE(12, 0) MPM = PB.buildLTODefaultPipeline(OptLevel, nullptr); -#else - MPM = PB.buildLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); -#endif break; } } @@ -1547,7 +1405,6 @@ // `ProcessThinLTOModule` function. Here they're split up into separate steps // so rustc can save off the intermediate bytecode between each step. -#if LLVM_VERSION_GE(11, 0) static bool clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) { // When linking an ELF shared object, dso_local should be dropped. We @@ -1558,7 +1415,6 @@ Mod.getPIELevel() == PIELevel::Default; return ClearDSOLocalOnDeclarations; } -#endif extern "C" bool LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M, @@ -1566,12 +1422,8 @@ Module &Mod = *unwrap(M); TargetMachine &Target = *unwrap(TM); -#if LLVM_VERSION_GE(11, 0) bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); bool error = renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal); -#else - bool error = renameModuleForThinLTO(Mod, Data->Index); -#endif if (error) { LLVMRustSetLastError("renameModuleForThinLTO failed"); @@ -1640,12 +1492,8 @@ return MOrErr; }; -#if LLVM_VERSION_GE(11, 0) bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); FunctionImporter Importer(Data->Index, Loader, ClearDSOLocal); -#else - FunctionImporter Importer(Data->Index, Loader); -#endif Expected<bool> Result = Importer.importFunctions(Mod, ImportList); if (!Result) { LLVMRustSetLastError(toString(Result.takeError()).c_str());
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index b7b0524..bb6d42c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -124,8 +124,18 @@ extern "C" LLVMValueRef LLVMRustGetOrInsertGlobal(LLVMModuleRef M, const char *Name, size_t NameLen, LLVMTypeRef Ty) { + Module *Mod = unwrap(M); StringRef NameRef(Name, NameLen); - return wrap(unwrap(M)->getOrInsertGlobal(NameRef, unwrap(Ty))); + + // We don't use Module::getOrInsertGlobal because that returns a Constant*, + // which may either be the real GlobalVariable*, or a constant bitcast of it + // if our type doesn't match the original declaration. We always want the + // GlobalVariable* so we can access linkage, visibility, etc. + GlobalVariable *GV = Mod->getGlobalVariable(NameRef, true); + if (!GV) + GV = new GlobalVariable(*Mod, unwrap(Ty), false, + GlobalValue::ExternalLinkage, nullptr, NameRef); + return wrap(GV); } extern "C" LLVMValueRef @@ -203,6 +213,12 @@ return Attribute::SanitizeHWAddress; case WillReturn: return Attribute::WillReturn; + case StackProtectReq: + return Attribute::StackProtectReq; + case StackProtectStrong: + return Attribute::StackProtectStrong; + case StackProtect: + return Attribute::StackProtect; } report_fatal_error("bad AttributeKind"); } @@ -263,11 +279,7 @@ extern "C" void LLVMRustAddStructRetCallSiteAttr(LLVMValueRef Instr, unsigned Index, LLVMTypeRef Ty) { CallBase *Call = unwrap<CallBase>(Instr); -#if LLVM_VERSION_GE(12, 0) Attribute Attr = Attribute::getWithStructRetType(Call->getContext(), unwrap(Ty)); -#else - Attribute Attr = Attribute::get(Call->getContext(), Attribute::StructRet); -#endif AddAttribute(Call, Index, Attr); } @@ -311,11 +323,7 @@ extern "C" void LLVMRustAddStructRetAttr(LLVMValueRef Fn, unsigned Index, LLVMTypeRef Ty) { Function *F = unwrap<Function>(Fn); -#if LLVM_VERSION_GE(12, 0) Attribute Attr = Attribute::getWithStructRetType(F->getContext(), unwrap(Ty)); -#else - Attribute Attr = Attribute::get(F->getContext(), Attribute::StructRet); -#endif AddAttribute(F, Index, Attr); } @@ -681,10 +689,8 @@ return DIFile::ChecksumKind::CSK_MD5; case LLVMRustChecksumKind::SHA1: return DIFile::ChecksumKind::CSK_SHA1; -#if (LLVM_VERSION_MAJOR >= 11) case LLVMRustChecksumKind::SHA256: return DIFile::ChecksumKind::CSK_SHA256; -#endif default: report_fatal_error("bad ChecksumKind."); } @@ -999,14 +1005,9 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef Ty) { -#if LLVM_VERSION_GE(11, 0) bool IsDefault = false; // FIXME: should we ever set this true? return wrap(Builder->createTemplateTypeParameter( unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIType>(Ty), IsDefault)); -#else - return wrap(Builder->createTemplateTypeParameter( - unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIType>(Ty))); -#endif } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateNameSpace( @@ -1031,17 +1032,11 @@ LLVMRustDIBuilderCreateDebugLocation(unsigned Line, unsigned Column, LLVMMetadataRef ScopeRef, LLVMMetadataRef InlinedAt) { -#if LLVM_VERSION_GE(12, 0) MDNode *Scope = unwrapDIPtr<MDNode>(ScopeRef); DILocation *Loc = DILocation::get( Scope->getContext(), Line, Column, Scope, unwrapDIPtr<MDNode>(InlinedAt)); return wrap(Loc); -#else - DebugLoc debug_loc = DebugLoc::get(Line, Column, unwrapDIPtr<MDNode>(ScopeRef), - unwrapDIPtr<MDNode>(InlinedAt)); - return wrap(debug_loc.getAsMDNode()); -#endif } extern "C" int64_t LLVMRustDIBuilderCreateOpDeref() { @@ -1246,27 +1241,18 @@ return LLVMArrayTypeKind; case Type::PointerTyID: return LLVMPointerTypeKind; -#if LLVM_VERSION_GE(11, 0) case Type::FixedVectorTyID: return LLVMVectorTypeKind; -#else - case Type::VectorTyID: - return LLVMVectorTypeKind; -#endif case Type::X86_MMXTyID: return LLVMX86_MMXTypeKind; case Type::TokenTyID: return LLVMTokenTypeKind; -#if LLVM_VERSION_GE(11, 0) case Type::ScalableVectorTyID: return LLVMScalableVectorTypeKind; case Type::BFloatTyID: return LLVMBFloatTypeKind; -#endif -#if LLVM_VERSION_GE(12, 0) case Type::X86_AMXTyID: return LLVMX86_AMXTypeKind; -#endif } report_fatal_error("Unhandled TypeID."); } @@ -1724,23 +1710,15 @@ } extern "C" LLVMValueRef LLVMRustBuildVectorReduceFMin(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { -#if LLVM_VERSION_GE(12, 0) Instruction *I = unwrap(B)->CreateFPMinReduce(unwrap(Src)); I->setHasNoNaNs(NoNaN); return wrap(I); -#else - return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Src), NoNaN)); -#endif } extern "C" LLVMValueRef LLVMRustBuildVectorReduceFMax(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { -#if LLVM_VERSION_GE(12, 0) Instruction *I = unwrap(B)->CreateFPMaxReduce(unwrap(Src)); I->setHasNoNaNs(NoNaN); return wrap(I); -#else - return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Src), NoNaN)); -#endif } extern "C" LLVMValueRef
diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index 6493bd9..8476c2b 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs
@@ -1,5 +1,5 @@ #![feature(nll)] -#![feature(static_nobundle)] +#![feature(native_link_modifiers)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] // NOTE: This crate only exists to allow linking on mingw targets.
diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 00501718..6dbba27 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs
@@ -36,7 +36,7 @@ Storage(Type), /// Cache the query to disk if the `Expr` returns true. - Cache(Option<(IdentOrWild, IdentOrWild)>, Block), + Cache(Option<IdentOrWild>, Block), /// Custom code to load the query from disk. LoadCached(Ident, Ident, Block), @@ -55,6 +55,9 @@ /// Always evaluate the query, ignoring its dependencies EvalAlways(Ident), + + /// Use a separate query provider for local and extern crates + SeparateProvideExtern(Ident), } impl Parse for QueryModifier { @@ -87,9 +90,7 @@ let args; parenthesized!(args in input); let tcx = args.parse()?; - args.parse::<Token![,]>()?; - let value = args.parse()?; - Some((tcx, value)) + Some(tcx) } else { None }; @@ -120,6 +121,8 @@ Ok(QueryModifier::Anon(modifier)) } else if modifier == "eval_always" { Ok(QueryModifier::EvalAlways(modifier)) + } else if modifier == "separate_provide_extern" { + Ok(QueryModifier::SeparateProvideExtern(modifier)) } else { Err(Error::new(modifier.span(), "unknown query modifier")) } @@ -197,7 +200,7 @@ storage: Option<Type>, /// Cache the query to disk if the `Block` returns true. - cache: Option<(Option<(IdentOrWild, IdentOrWild)>, Block)>, + cache: Option<(Option<IdentOrWild>, Block)>, /// Custom code to load the query from disk. load_cached: Option<(Ident, Ident, Block)>, @@ -216,6 +219,9 @@ // Always evaluate the query, ignoring its dependencies eval_always: Option<Ident>, + + /// Use a separate query provider for local and extern crates + separate_provide_extern: Option<Ident>, } /// Process query modifiers into a struct, erroring on duplicates @@ -229,6 +235,7 @@ let mut no_hash = None; let mut anon = None; let mut eval_always = None; + let mut separate_provide_extern = None; for modifier in query.modifiers.0.drain(..) { match modifier { QueryModifier::LoadCached(tcx, id, block) => { @@ -319,6 +326,15 @@ } eval_always = Some(ident); } + QueryModifier::SeparateProvideExtern(ident) => { + if separate_provide_extern.is_some() { + panic!( + "duplicate modifier `separate_provide_extern` for query `{}`", + query.name + ); + } + separate_provide_extern = Some(ident); + } } } let desc = desc.unwrap_or_else(|| { @@ -334,6 +350,7 @@ no_hash, anon, eval_always, + separate_provide_extern, } } @@ -351,51 +368,30 @@ let try_load_from_disk = if let Some((tcx, id, block)) = modifiers.load_cached.as_ref() { // Use custom code to load the query from disk quote! { - #[inline] - fn try_load_from_disk( - #tcx: QueryCtxt<'tcx>, - #id: SerializedDepNodeIndex - ) -> Option<Self::Value> { - #block - } + const TRY_LOAD_FROM_DISK: Option<fn(QueryCtxt<$tcx>, SerializedDepNodeIndex) -> Option<Self::Value>> + = Some(|#tcx, #id| { #block }); } } else { // Use the default code to load the query from disk quote! { - #[inline] - fn try_load_from_disk( - tcx: QueryCtxt<'tcx>, - id: SerializedDepNodeIndex - ) -> Option<Self::Value> { - tcx.on_disk_cache().as_ref()?.try_load_query_result(*tcx, id) - } + const TRY_LOAD_FROM_DISK: Option<fn(QueryCtxt<$tcx>, SerializedDepNodeIndex) -> Option<Self::Value>> + = Some(|tcx, id| tcx.on_disk_cache().as_ref()?.try_load_query_result(*tcx, id)); } }; let tcx = args .as_ref() .map(|t| { - let t = &(t.0).0; - quote! { #t } - }) - .unwrap_or_else(|| quote! { _ }); - let value = args - .as_ref() - .map(|t| { - let t = &(t.1).0; + let t = &t.0; quote! { #t } }) .unwrap_or_else(|| quote! { _ }); // expr is a `Block`, meaning that `{ #expr }` gets expanded // to `{ { stmts... } }`, which triggers the `unused_braces` lint. quote! { - #[inline] #[allow(unused_variables, unused_braces)] - fn cache_on_disk( - #tcx: QueryCtxt<'tcx>, - #key: &Self::Key, - #value: Option<&Self::Value> - ) -> bool { + #[inline] + fn cache_on_disk(#tcx: TyCtxt<'tcx>, #key: &Self::Key) -> bool { #expr } @@ -405,7 +401,14 @@ if modifiers.load_cached.is_some() { panic!("load_cached modifier on query `{}` without a cache modifier", name); } - quote! {} + quote! { + #[inline] + fn cache_on_disk(_: TyCtxt<'tcx>, _: &Self::Key) -> bool { + false + } + + const TRY_LOAD_FROM_DISK: Option<fn(QueryCtxt<$tcx>, SerializedDepNodeIndex) -> Option<Self::Value>> = None; + } }; let (tcx, desc) = modifiers.desc; @@ -413,17 +416,17 @@ let desc = quote! { #[allow(unused_variables)] - fn describe(tcx: QueryCtxt<'tcx>, key: Self::Key) -> String { + fn describe(tcx: QueryCtxt<$tcx>, key: Self::Key) -> String { let (#tcx, #key) = (*tcx, key); ::rustc_middle::ty::print::with_no_trimmed_paths(|| format!(#desc).into()) } }; impls.extend(quote! { - impl<'tcx> QueryDescription<QueryCtxt<'tcx>> for queries::#name<'tcx> { + (#name<$tcx:tt>) => { #desc #cache - } + }; }); } @@ -478,6 +481,10 @@ if let Some(eval_always) = &modifiers.eval_always { attributes.push(quote! { (#eval_always) }); }; + // Pass on the separate_provide_extern modifier + if let Some(separate_provide_extern) = &modifiers.separate_provide_extern { + attributes.push(quote! { (#separate_provide_extern) }); + } // This uses the span of the query definition for the commas, // which can be important if we later encounter any ambiguity @@ -531,7 +538,7 @@ } #[macro_export] macro_rules! rustc_query_description { - () => { #query_description_stream } + #query_description_stream } }) }
diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 7bc669f..66e6b57 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs
@@ -247,13 +247,24 @@ }) .collect(); - let result = quote! { ::rustc_serialize::Encoder::emit_enum_variant( - __encoder, - #variant_name, - #variant_idx, - #field_idx, - |__encoder| { ::std::result::Result::Ok({ #encode_fields }) } - ) }; + let result = if field_idx != 0 { + quote! { + ::rustc_serialize::Encoder::emit_enum_variant( + __encoder, + #variant_name, + #variant_idx, + #field_idx, + |__encoder| { ::std::result::Result::Ok({ #encode_fields }) } + ) + } + } else { + quote! { + ::rustc_serialize::Encoder::emit_fieldless_enum_variant::<#variant_idx>( + __encoder, + #variant_name, + ) + } + }; variant_idx += 1; result });
diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs index c8959dc..80dcf99 100644 --- a/compiler/rustc_macros/src/session_diagnostic.rs +++ b/compiler/rustc_macros/src/session_diagnostic.rs
@@ -349,14 +349,14 @@ ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> { let field_binding = &info.binding.binding; - let option_ty = option_inner_ty(info.ty); + let option_ty = option_inner_ty(&info.ty); let generated_code = self.generate_non_option_field_code( attr, FieldInfo { vis: info.vis, binding: info.binding, - ty: option_ty.unwrap_or(info.ty), + ty: option_ty.unwrap_or(&info.ty), span: info.span, }, )?; @@ -388,7 +388,7 @@ let formatted_str = self.build_format(&s.value(), attr.span()); match name { "message" => { - if type_matches_path(info.ty, &["rustc_span", "Span"]) { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { quote! { #diag.set_span(*#field_binding); #diag.set_primary_message(#formatted_str); @@ -401,7 +401,7 @@ } } "label" => { - if type_matches_path(info.ty, &["rustc_span", "Span"]) { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { quote! { #diag.span_label(*#field_binding, #formatted_str); }
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 95b74fd..eb0a693 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs
@@ -29,6 +29,7 @@ use proc_macro::bridge::client::ProcMacro; use std::collections::BTreeMap; +use std::ops::Fn; use std::path::Path; use std::{cmp, env}; use tracing::{debug, info};
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 644b849..6cf0dd8 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs
@@ -2,6 +2,7 @@ #![feature(crate_visibility_modifier)] #![feature(drain_filter)] #![feature(in_band_lifetimes)] +#![feature(let_else)] #![feature(nll)] #![feature(once_cell)] #![feature(proc_macro_internals)]
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index bbd30c9..7cba16e 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs
@@ -236,7 +236,7 @@ use std::io::{Read, Result as IoResult, Write}; use std::path::{Path, PathBuf}; use std::{cmp, fmt, fs}; -use tracing::{debug, info, warn}; +use tracing::{debug, info}; #[derive(Clone)] crate struct CrateLocator<'a> { @@ -350,6 +350,7 @@ self.crate_rejections.via_kind.clear(); self.crate_rejections.via_version.clear(); self.crate_rejections.via_filename.clear(); + self.crate_rejections.via_invalid.clear(); } crate fn maybe_load_library_crate(&mut self) -> Result<Option<Library>, CrateError> { @@ -548,8 +549,18 @@ continue; } } - Err(err) => { - warn!("no metadata found: {}", err); + Err(MetadataError::LoadFailure(err)) => { + info!("no metadata found: {}", err); + // The file was present and created by the same compiler version, but we + // couldn't load it for some reason. Give a hard error instead of silently + // ignoring it, but only if we would have given an error anyway. + self.crate_rejections + .via_invalid + .push(CrateMismatch { path: lib, got: err }); + continue; + } + Err(err @ MetadataError::NotPresent(_)) => { + info!("no metadata found: {}", err); continue; } }; @@ -726,25 +737,28 @@ fn get_metadata_section( target: &Target, flavor: CrateFlavor, - filename: &Path, + filename: &'p Path, loader: &dyn MetadataLoader, -) -> Result<MetadataBlob, String> { +) -> Result<MetadataBlob, MetadataError<'p>> { if !filename.exists() { - return Err(format!("no such file: '{}'", filename.display())); + return Err(MetadataError::NotPresent(filename)); } let raw_bytes: MetadataRef = match flavor { - CrateFlavor::Rlib => loader.get_rlib_metadata(target, filename)?, + CrateFlavor::Rlib => { + loader.get_rlib_metadata(target, filename).map_err(MetadataError::LoadFailure)? + } CrateFlavor::Dylib => { - let buf = loader.get_dylib_metadata(target, filename)?; + let buf = + loader.get_dylib_metadata(target, filename).map_err(MetadataError::LoadFailure)?; // The header is uncompressed let header_len = METADATA_HEADER.len(); debug!("checking {} bytes of metadata-version stamp", header_len); let header = &buf[..cmp::min(header_len, buf.len())]; if header != METADATA_HEADER { - return Err(format!( - "incompatible metadata version found: '{}'", + return Err(MetadataError::LoadFailure(format!( + "invalid metadata version found: {}", filename.display() - )); + ))); } // Header is okay -> inflate the actual metadata @@ -756,17 +770,28 @@ match FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated) { Ok(_) => rustc_erase_owner!(OwningRef::new(inflated).map_owner_box()), Err(_) => { - return Err(format!("failed to decompress metadata: {}", filename.display())); + return Err(MetadataError::LoadFailure(format!( + "failed to decompress metadata: {}", + filename.display() + ))); } } } CrateFlavor::Rmeta => { // mmap the file, because only a small fraction of it is read. - let file = std::fs::File::open(filename) - .map_err(|_| format!("failed to open rmeta metadata: '{}'", filename.display()))?; + let file = std::fs::File::open(filename).map_err(|_| { + MetadataError::LoadFailure(format!( + "failed to open rmeta metadata: '{}'", + filename.display() + )) + })?; let mmap = unsafe { Mmap::map(file) }; - let mmap = mmap - .map_err(|_| format!("failed to mmap rmeta metadata: '{}'", filename.display()))?; + let mmap = mmap.map_err(|_| { + MetadataError::LoadFailure(format!( + "failed to mmap rmeta metadata: '{}'", + filename.display() + )) + })?; rustc_erase_owner!(OwningRef::new(mmap).map_owner_box()) } @@ -775,7 +800,10 @@ if blob.is_compatible() { Ok(blob) } else { - Err(format!("incompatible metadata version found: '{}'", filename.display())) + Err(MetadataError::LoadFailure(format!( + "invalid metadata version found: {}", + filename.display() + ))) } } @@ -854,6 +882,7 @@ via_kind: Vec<CrateMismatch>, via_version: Vec<CrateMismatch>, via_filename: Vec<CrateMismatch>, + via_invalid: Vec<CrateMismatch>, } /// Candidate rejection reasons collected during crate search. @@ -883,6 +912,24 @@ NonDylibPlugin(Symbol), } +enum MetadataError<'a> { + /// The file was missing. + NotPresent(&'a Path), + /// The file was present and invalid. + LoadFailure(String), +} + +impl fmt::Display for MetadataError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MetadataError::NotPresent(filename) => { + f.write_str(&format!("no such file: '{}'", filename.display())) + } + MetadataError::LoadFailure(msg) => f.write_str(msg), + } + } +} + impl CrateError { crate fn report(self, sess: &Session, span: Span, missing_core: bool) -> ! { let mut err = match self { @@ -1064,6 +1111,19 @@ } err.note(&msg); err + } else if !locator.crate_rejections.via_invalid.is_empty() { + let mut err = struct_span_err!( + sess, + span, + E0786, + "found invalid metadata files for crate `{}`{}", + crate_name, + add, + ); + for CrateMismatch { path: _, got } in locator.crate_rejections.via_invalid { + err.note(&got); + } + err } else { let mut err = struct_span_err!( sess,
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 2431b81..bd5cda1 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -309,7 +309,7 @@ .libs .iter() .filter_map(|lib| lib.name.as_ref()) - .any(|n| &n.as_str() == &lib.name); + .any(|n| n.as_str() == lib.name); if new_name.is_empty() { self.tcx.sess.err(&format!( "an empty renaming target was specified for library `{}`",
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 89bb579..b2c7818 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -472,9 +472,7 @@ let len = BytePos::decode(decoder)?; let hi = lo + len; - let sess = if let Some(sess) = decoder.sess { - sess - } else { + let Some(sess) = decoder.sess else { bug!("Cannot decode Span without Session.") }; @@ -1181,7 +1179,7 @@ let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id); let mut vis = self.get_visibility(ctor_def_id.index); - if ctor_def_id == def_id && vis == ty::Visibility::Public { + if ctor_def_id == def_id && vis.is_public() { // For non-exhaustive variants lower the constructor visibility to // within the crate. We only need this for fictive constructors, // for other constructors correct visibilities @@ -1200,8 +1198,8 @@ } } - if let EntryKind::Mod(data) = kind { - for exp in data.decode((self, sess)).reexports.decode((self, sess)) { + if let EntryKind::Mod(exports) = kind { + for exp in exports.decode((self, sess)) { match exp.res { Res::Def(DefKind::Macro(..), _) => {} _ if macros_only => continue, @@ -1221,10 +1219,11 @@ } fn module_expansion(&self, id: DefIndex, sess: &Session) -> ExpnId { - if let EntryKind::Mod(m) = self.kind(id) { - m.decode((self, sess)).expansion - } else { - panic!("Expected module, found {:?}", self.local_def_id(id)) + match self.kind(id) { + EntryKind::Mod(_) | EntryKind::Enum(_) | EntryKind::Trait(_) => { + self.get_expn_that_defined(id, sess) + } + _ => panic!("Expected module, found {:?}", self.local_def_id(id)), } }
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index e12f049..eeb0a77 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -10,7 +10,7 @@ use rustc_middle::hir::exports::Export; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::middle::stability::DeprecationEntry; -use rustc_middle::ty::query::Providers; +use rustc_middle::ty::query::{ExternProviders, Providers}; use rustc_middle::ty::{self, TyCtxt, Visibility}; use rustc_session::cstore::{CrateSource, CrateStore, ForeignModule}; use rustc_session::utils::NativeLibKind; @@ -26,7 +26,7 @@ macro_rules! provide { (<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $($name:ident => $compute:block)*) => { - pub fn provide_extern(providers: &mut Providers) { + pub fn provide_extern(providers: &mut ExternProviders) { $(fn $name<$lt>( $tcx: TyCtxt<$lt>, def_id_arg: ty::query::query_keys::$name<$lt>, @@ -51,7 +51,7 @@ $compute })* - *providers = Providers { + *providers = ExternProviders { $($name,)* ..*providers }; @@ -318,7 +318,7 @@ } let mut add_child = |bfs_queue: &mut VecDeque<_>, child: &Export, parent: DefId| { - if child.vis != ty::Visibility::Public { + if !child.vis.is_public() { return; }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 20f7b05..d46829c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -797,6 +797,7 @@ | DefKind::ConstParam | DefKind::LifetimeParam | DefKind::AnonConst + | DefKind::InlineConst | DefKind::GlobalAsm | DefKind::Closure | DefKind::Generator @@ -832,6 +833,7 @@ DefKind::Use | DefKind::LifetimeParam | DefKind::AnonConst + | DefKind::InlineConst | DefKind::GlobalAsm | DefKind::Closure | DefKind::Generator @@ -856,9 +858,11 @@ (true, mir_opt_base) } // Constants - DefKind::AnonConst | DefKind::AssocConst | DefKind::Static | DefKind::Const => { - (true, false) - } + DefKind::AnonConst + | DefKind::InlineConst + | DefKind::AssocConst + | DefKind::Static + | DefKind::Const => (true, false), // Full-fledged functions DefKind::AssocFn | DefKind::Fn => { let generics = tcx.generics_of(def_id); @@ -914,6 +918,7 @@ | DefKind::Use | DefKind::LifetimeParam | DefKind::AnonConst + | DefKind::InlineConst | DefKind::GlobalAsm | DefKind::Closure | DefKind::Generator @@ -939,6 +944,7 @@ | DefKind::AssocFn | DefKind::AssocConst | DefKind::AnonConst + | DefKind::InlineConst | DefKind::OpaqueTy | DefKind::Impl | DefKind::Field @@ -1086,11 +1092,11 @@ Lazy::empty() }; - let data = ModData { reexports, expansion: tcx.expn_that_defined(local_def_id) }; - - record!(self.tables.kind[def_id] <- EntryKind::Mod(self.lazy(data))); + record!(self.tables.kind[def_id] <- EntryKind::Mod(reexports)); if self.is_proc_macro { record!(self.tables.children[def_id] <- &[]); + // Encode this here because we don't do it in encode_def_ids. + record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); } else { record!(self.tables.children[def_id] <- md.item_ids.iter().map(|item_id| { item_id.def_id.local_def_index @@ -2187,5 +2193,8 @@ result[header + 2] = (pos >> 8) as u8; result[header + 3] = (pos >> 0) as u8; + // Record metadata size for self-profiling + tcx.prof.artifact_size("crate_metadata", "crate_metadata", result.len() as u64); + EncodedMetadata { raw_data: result } }
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 42855e9..4e09d23 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -346,7 +346,7 @@ Union(Lazy<VariantData>, ReprOptions), Fn(Lazy<FnData>), ForeignFn(Lazy<FnData>), - Mod(Lazy<ModData>), + Mod(Lazy<[Export]>), MacroDef(Lazy<MacroDef>), ProcMacro(MacroKind), Closure, @@ -365,12 +365,6 @@ struct RenderedConst(String); #[derive(MetadataEncodable, MetadataDecodable)] -struct ModData { - reexports: Lazy<[Export]>, - expansion: ExpnId, -} - -#[derive(MetadataEncodable, MetadataDecodable)] struct FnData { asyncness: hir::IsAsync, constness: hir::Constness,
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index d06c593..daeccde 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml
@@ -12,6 +12,7 @@ either = "1.5.0" gsgdt = "0.1.2" tracing = "0.1" +rustc-rayon = "0.3.1" rustc-rayon-core = "0.3.1" polonius-engine = "0.13.0" rustc_apfloat = { path = "../rustc_apfloat" }
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 962aea4..ee2e190 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs
@@ -1,43 +1,38 @@ -/// This declares a list of types which can be allocated by `Arena`. -/// -/// The `few` modifier will cause allocation to use the shared arena and recording the destructor. -/// This is faster and more memory efficient if there's only a few allocations of the type. -/// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is -/// faster and more memory efficient if there is lots of allocations. +/// This higher-order macro declares a list of types which can be allocated by `Arena`. /// /// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]` where `T` is the type /// listed. These impls will appear in the implement_ty_decoder! macro. #[macro_export] macro_rules! arena_types { - ($macro:path, $tcx:lifetime) => ( + ($macro:path) => ( $macro!([ [] layout: rustc_target::abi::Layout, - [] fn_abi: rustc_target::abi::call::FnAbi<$tcx, rustc_middle::ty::Ty<$tcx>>, + [] fn_abi: rustc_target::abi::call::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>, // AdtDef are interned and compared by address [] adt_def: rustc_middle::ty::AdtDef, - [] steal_thir: rustc_data_structures::steal::Steal<rustc_middle::thir::Thir<$tcx>>, - [] steal_mir: rustc_data_structures::steal::Steal<rustc_middle::mir::Body<$tcx>>, - [decode] mir: rustc_middle::mir::Body<$tcx>, + [] steal_thir: rustc_data_structures::steal::Steal<rustc_middle::thir::Thir<'tcx>>, + [] steal_mir: rustc_data_structures::steal::Steal<rustc_middle::mir::Body<'tcx>>, + [decode] mir: rustc_middle::mir::Body<'tcx>, [] steal_promoted: rustc_data_structures::steal::Steal< rustc_index::vec::IndexVec< rustc_middle::mir::Promoted, - rustc_middle::mir::Body<$tcx> + rustc_middle::mir::Body<'tcx> > >, [decode] promoted: rustc_index::vec::IndexVec< rustc_middle::mir::Promoted, - rustc_middle::mir::Body<$tcx> + rustc_middle::mir::Body<'tcx> >, - [decode] typeck_results: rustc_middle::ty::TypeckResults<$tcx>, + [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, [decode] borrowck_result: - rustc_middle::mir::BorrowCheckResult<$tcx>, + rustc_middle::mir::BorrowCheckResult<'tcx>, [decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult, [decode] code_region: rustc_middle::mir::coverage::CodeRegion, [] const_allocs: rustc_middle::mir::interpret::Allocation, // Required for the incremental on-disk cache - [few] mir_keys: rustc_hir::def_id::DefIdSet, + [] mir_keys: rustc_hir::def_id::DefIdSet, [] region_scope_tree: rustc_middle::middle::region::ScopeTree, [] dropck_outlives: rustc_middle::infer::canonical::Canonical<'tcx, @@ -77,26 +72,20 @@ rustc_middle::infer::canonical::Canonical<'tcx, rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Ty<'tcx>> >, - [few] all_traits: Vec<rustc_hir::def_id::DefId>, - [few] privacy_access_levels: rustc_middle::middle::privacy::AccessLevels, - [few] foreign_module: rustc_session::cstore::ForeignModule, - [few] foreign_modules: Vec<rustc_session::cstore::ForeignModule>, + [] all_traits: Vec<rustc_hir::def_id::DefId>, + [] privacy_access_levels: rustc_middle::middle::privacy::AccessLevels, + [] foreign_module: rustc_session::cstore::ForeignModule, + [] foreign_modules: Vec<rustc_session::cstore::ForeignModule>, [] upvars_mentioned: rustc_data_structures::fx::FxIndexMap<rustc_hir::HirId, rustc_hir::Upvar>, [] object_safety_violations: rustc_middle::traits::ObjectSafetyViolation, - [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<$tcx>, + [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<'tcx>, [] attribute: rustc_ast::Attribute, [] name_set: rustc_data_structures::fx::FxHashSet<rustc_span::symbol::Symbol>, [] hir_id_set: rustc_hir::HirIdSet, // Interned types - [] tys: rustc_middle::ty::TyS<$tcx>, - [] predicates: rustc_middle::ty::PredicateInner<$tcx>, - - // HIR query types - [few] indexed_hir: rustc_middle::hir::IndexedHir<$tcx>, - [few] hir_definitions: rustc_hir::definitions::Definitions, - [] hir_owner: rustc_middle::hir::Owner<$tcx>, - [] hir_owner_nodes: rustc_middle::hir::OwnerNodes<$tcx>, + [] tys: rustc_middle::ty::TyS<'tcx>, + [] predicates: rustc_middle::ty::PredicateInner<'tcx>, // Note that this deliberately duplicates items in the `rustc_hir::arena`, // since we need to allocate this type on both the `rustc_hir` arena @@ -106,8 +95,10 @@ // This is used to decode the &'tcx [Span] for InlineAsm's line_spans. [decode] span: rustc_span::Span, [decode] used_trait_imports: rustc_data_structures::fx::FxHashSet<rustc_hir::def_id::LocalDefId>, - ], $tcx); + + [] dep_kind: rustc_middle::dep_graph::DepKindStruct, + ]); ) } -arena_types!(rustc_arena::declare_arena, 'tcx); +arena_types!(rustc_arena::declare_arena);
diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 23d475a..f310001 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs
@@ -75,147 +75,73 @@ /// of the `DepKind`. Overall, this allows to implement `DepContext` using this manual /// jump table instead of large matches. pub struct DepKindStruct { - /// Whether the DepNode has parameters (query keys). - pub(super) has_params: bool, - /// Anonymous queries cannot be replayed from one compiler invocation to the next. /// When their result is needed, it is recomputed. They are useful for fine-grained /// dependency tracking, and caching within one compiler invocation. - pub(super) is_anon: bool, + pub is_anon: bool, /// Eval-always queries do not track their dependencies, and are always recomputed, even if /// their inputs have not changed since the last compiler invocation. The result is still /// cached within one compiler invocation. - pub(super) is_eval_always: bool, + pub is_eval_always: bool, /// Whether the query key can be recovered from the hashed fingerprint. /// See [DepNodeParams] trait for the behaviour of each key type. - // FIXME: Make this a simple boolean once DepNodeParams::fingerprint_style - // can be made a specialized associated const. - fingerprint_style: fn() -> FingerprintStyle, -} + pub fingerprint_style: FingerprintStyle, -impl std::ops::Deref for DepKind { - type Target = DepKindStruct; - fn deref(&self) -> &DepKindStruct { - &DEP_KINDS[*self as usize] - } + /// The red/green evaluation system will try to mark a specific DepNode in the + /// dependency graph as green by recursively trying to mark the dependencies of + /// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` + /// where we don't know if it is red or green and we therefore actually have + /// to recompute its value in order to find out. Since the only piece of + /// information that we have at that point is the `DepNode` we are trying to + /// re-evaluate, we need some way to re-run a query from just that. This is what + /// `force_from_dep_node()` implements. + /// + /// In the general case, a `DepNode` consists of a `DepKind` and an opaque + /// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint + /// is usually constructed by computing a stable hash of the query-key that the + /// `DepNode` corresponds to. Consequently, it is not in general possible to go + /// back from hash to query-key (since hash functions are not reversible). For + /// this reason `force_from_dep_node()` is expected to fail from time to time + /// because we just cannot find out, from the `DepNode` alone, what the + /// corresponding query-key is and therefore cannot re-run the query. + /// + /// The system deals with this case letting `try_mark_green` fail which forces + /// the root query to be re-evaluated. + /// + /// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. + /// Fortunately, we can use some contextual information that will allow us to + /// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we + /// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a + /// valid `DefPathHash`. Since we also always build a huge table that maps every + /// `DefPathHash` in the current codebase to the corresponding `DefId`, we have + /// everything we need to re-run the query. + /// + /// Take the `mir_promoted` query as an example. Like many other queries, it + /// just has a single parameter: the `DefId` of the item it will compute the + /// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` + /// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` + /// is actually a `DefPathHash`, and can therefore just look up the corresponding + /// `DefId` in `tcx.def_path_hash_to_def_id`. + pub force_from_dep_node: Option<fn(tcx: TyCtxt<'_>, dep_node: DepNode) -> bool>, + + /// Invoke a query to put the on-disk cached value in memory. + pub try_load_from_on_disk_cache: Option<fn(TyCtxt<'_>, DepNode)>, } impl DepKind { #[inline(always)] - pub fn fingerprint_style(&self) -> FingerprintStyle { + pub fn fingerprint_style(self, tcx: TyCtxt<'_>) -> FingerprintStyle { // Only fetch the DepKindStruct once. - let data: &DepKindStruct = &**self; + let data = tcx.query_kind(self); if data.is_anon { return FingerprintStyle::Opaque; } - - (data.fingerprint_style)() + data.fingerprint_style } } -// erase!() just makes tokens go away. It's used to specify which macro argument -// is repeated (i.e., which sub-expression of the macro we are in) but don't need -// to actually use any of the arguments. -macro_rules! erase { - ($x:tt) => {{}}; -} - -macro_rules! is_anon_attr { - (anon) => { - true - }; - ($attr:ident) => { - false - }; -} - -macro_rules! is_eval_always_attr { - (eval_always) => { - true - }; - ($attr:ident) => { - false - }; -} - -macro_rules! contains_anon_attr { - ($(($attr:ident $($attr_args:tt)* )),*) => ({$(is_anon_attr!($attr) | )* false}); -} - -macro_rules! contains_eval_always_attr { - ($(($attr:ident $($attr_args:tt)* )),*) => ({$(is_eval_always_attr!($attr) | )* false}); -} - -#[allow(non_upper_case_globals)] -pub mod dep_kind { - use super::*; - use crate::ty::query::query_keys; - use rustc_query_system::dep_graph::FingerprintStyle; - - // We use this for most things when incr. comp. is turned off. - pub const Null: DepKindStruct = DepKindStruct { - has_params: false, - is_anon: false, - is_eval_always: false, - - fingerprint_style: || FingerprintStyle::Unit, - }; - - pub const TraitSelect: DepKindStruct = DepKindStruct { - has_params: false, - is_anon: true, - is_eval_always: false, - - fingerprint_style: || FingerprintStyle::Unit, - }; - - pub const CompileCodegenUnit: DepKindStruct = DepKindStruct { - has_params: true, - is_anon: false, - is_eval_always: false, - - fingerprint_style: || FingerprintStyle::Opaque, - }; - - pub const CompileMonoItem: DepKindStruct = DepKindStruct { - has_params: true, - is_anon: false, - is_eval_always: false, - - fingerprint_style: || FingerprintStyle::Opaque, - }; - - macro_rules! define_query_dep_kinds { - ($( - [$($attrs:tt)*] - $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* - ,)*) => ( - $(pub const $variant: DepKindStruct = { - const has_params: bool = $({ erase!($tuple_arg_ty); true } |)* false; - const is_anon: bool = contains_anon_attr!($($attrs)*); - const is_eval_always: bool = contains_eval_always_attr!($($attrs)*); - - #[inline(always)] - fn fingerprint_style() -> rustc_query_system::dep_graph::FingerprintStyle { - <query_keys::$variant<'_> as DepNodeParams<TyCtxt<'_>>> - ::fingerprint_style() - } - - DepKindStruct { - has_params, - is_anon, - is_eval_always, - fingerprint_style, - } - };)* - ); - } - - rustc_dep_node_append!([define_query_dep_kinds!][]); -} - macro_rules! define_dep_nodes { (<$tcx:tt> $( @@ -225,12 +151,10 @@ ) => ( #[macro_export] macro_rules! make_dep_kind_array { - ($mod:ident) => {[ $(($mod::$variant),)* ]}; + ($mod:ident) => {[ $($mod::$variant()),* ]}; } - static DEP_KINDS: &[DepKindStruct] = &make_dep_kind_array!(dep_kind); - - /// This enum serves as an index into the `DEP_KINDS` array. + /// This enum serves as an index into arrays built by `make_dep_kind_array`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] #[allow(non_camel_case_types)] pub enum DepKind { @@ -296,7 +220,7 @@ /// Construct a DepNode from the given DepKind and DefPathHash. This /// method will assert that the given DepKind actually requires a /// single DefId/DefPathHash parameter. - fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> Self; + fn from_def_path_hash(tcx: TyCtxt<'_>, def_path_hash: DefPathHash, kind: DepKind) -> Self; /// Extracts the DefId corresponding to this DepNode. This will work /// if two conditions are met: @@ -311,7 +235,11 @@ fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId>; /// Used in testing - fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result<Self, ()>; + fn from_label_string( + tcx: TyCtxt<'_>, + label: &str, + def_path_hash: DefPathHash, + ) -> Result<Self, ()>; /// Used in testing fn has_label_string(label: &str) -> bool; @@ -321,8 +249,8 @@ /// Construct a DepNode from the given DepKind and DefPathHash. This /// method will assert that the given DepKind actually requires a /// single DefId/DefPathHash parameter. - fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> DepNode { - debug_assert!(kind.fingerprint_style() == FingerprintStyle::DefPathHash); + fn from_def_path_hash(tcx: TyCtxt<'_>, def_path_hash: DefPathHash, kind: DepKind) -> DepNode { + debug_assert!(kind.fingerprint_style(tcx) == FingerprintStyle::DefPathHash); DepNode { kind, hash: def_path_hash.0.into() } } @@ -337,31 +265,27 @@ /// refers to something from the previous compilation session that /// has been removed. fn extract_def_id(&self, tcx: TyCtxt<'tcx>) -> Option<DefId> { - if self.kind.fingerprint_style() == FingerprintStyle::DefPathHash { - Some( - tcx.on_disk_cache - .as_ref()? - .def_path_hash_to_def_id(tcx, DefPathHash(self.hash.into())), - ) + if self.kind.fingerprint_style(tcx) == FingerprintStyle::DefPathHash { + Some(tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into()))) } else { None } } /// Used in testing - fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result<DepNode, ()> { + fn from_label_string( + tcx: TyCtxt<'_>, + label: &str, + def_path_hash: DefPathHash, + ) -> Result<DepNode, ()> { let kind = dep_kind_from_label_string(label)?; - match kind.fingerprint_style() { + match kind.fingerprint_style(tcx) { FingerprintStyle::Opaque => Err(()), - FingerprintStyle::Unit => { - if !kind.has_params { - Ok(DepNode::new_no_params(kind)) - } else { - Err(()) - } + FingerprintStyle::Unit => Ok(DepNode::new_no_params(tcx, kind)), + FingerprintStyle::DefPathHash => { + Ok(DepNode::from_def_path_hash(tcx, def_path_hash, kind)) } - FingerprintStyle::DefPathHash => Ok(DepNode::from_def_path_hash(def_path_hash, kind)), } } @@ -377,10 +301,12 @@ FingerprintStyle::Unit } + #[inline(always)] fn to_fingerprint(&self, _: TyCtxt<'tcx>) -> Fingerprint { Fingerprint::ZERO } + #[inline(always)] fn recover(_: TyCtxt<'tcx>, _: &DepNode) -> Option<Self> { Some(()) } @@ -392,14 +318,17 @@ FingerprintStyle::DefPathHash } + #[inline(always)] fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { tcx.def_path_hash(*self).0 } + #[inline(always)] fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { tcx.def_path_str(*self) } + #[inline(always)] fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> { dep_node.extract_def_id(tcx) } @@ -411,14 +340,17 @@ FingerprintStyle::DefPathHash } + #[inline(always)] fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { self.to_def_id().to_fingerprint(tcx) } + #[inline(always)] fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { self.to_def_id().to_debug_str(tcx) } + #[inline(always)] fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> { dep_node.extract_def_id(tcx).map(|id| id.expect_local()) } @@ -430,15 +362,18 @@ FingerprintStyle::DefPathHash } + #[inline(always)] fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX }; def_id.to_fingerprint(tcx) } + #[inline(always)] fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { tcx.crate_name(*self).to_string() } + #[inline(always)] fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> { dep_node.extract_def_id(tcx).map(|id| id.krate) } @@ -453,6 +388,7 @@ // We actually would not need to specialize the implementation of this // method but it's faster to combine the hashes than to instantiate a full // hashing context and stable-hashing state. + #[inline(always)] fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { let (def_id_0, def_id_1) = *self; @@ -462,6 +398,7 @@ def_path_hash_0.0.combine(def_path_hash_1.0) } + #[inline(always)] fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { let (def_id_0, def_id_1) = *self; @@ -478,6 +415,7 @@ // We actually would not need to specialize the implementation of this // method but it's faster to combine the hashes than to instantiate a full // hashing context and stable-hashing state. + #[inline(always)] fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { let HirId { owner, local_id } = *self;
diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index cda9963..79d7ca3 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs
@@ -12,7 +12,7 @@ SerializedDepNodeIndex, WorkProduct, WorkProductId, }; -pub use dep_node::{label_strs, DepKind, DepNode, DepNodeExt}; +pub use dep_node::{label_strs, DepKind, DepKindStruct, DepNode, DepNodeExt}; crate use dep_node::{make_compile_codegen_unit, make_compile_mono_item}; pub type DepGraph = rustc_query_system::dep_graph::DepGraph<DepKind>; @@ -24,29 +24,8 @@ impl rustc_query_system::dep_graph::DepKind for DepKind { const NULL: Self = DepKind::Null; - #[inline(always)] - fn fingerprint_style(&self) -> rustc_query_system::dep_graph::FingerprintStyle { - DepKind::fingerprint_style(self) - } - - #[inline(always)] - fn is_eval_always(&self) -> bool { - self.is_eval_always - } - - #[inline(always)] - fn has_params(&self) -> bool { - self.has_params - } - fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", node.kind)?; - - if !node.kind.has_params && !node.kind.is_anon { - return Ok(()); - } - - write!(f, "(")?; + write!(f, "{:?}(", node.kind)?; ty::tls::with_opt(|opt_tcx| { if let Some(tcx) = opt_tcx { @@ -110,4 +89,51 @@ fn sess(&self) -> &Session { self.sess } + + #[inline(always)] + fn fingerprint_style(&self, kind: DepKind) -> rustc_query_system::dep_graph::FingerprintStyle { + kind.fingerprint_style(*self) + } + + #[inline(always)] + fn is_eval_always(&self, kind: DepKind) -> bool { + self.query_kind(kind).is_eval_always + } + + fn try_force_from_dep_node(&self, dep_node: DepNode) -> bool { + debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); + + // We must avoid ever having to call `force_from_dep_node()` for a + // `DepNode::codegen_unit`: + // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we + // would always end up having to evaluate the first caller of the + // `codegen_unit` query that *is* reconstructible. This might very well be + // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just + // to re-trigger calling the `codegen_unit` query with the right key. At + // that point we would already have re-done all the work we are trying to + // avoid doing in the first place. + // The solution is simple: Just explicitly call the `codegen_unit` query for + // each CGU, right after partitioning. This way `try_mark_green` will always + // hit the cache instead of having to go through `force_from_dep_node`. + // This assertion makes sure, we actually keep applying the solution above. + debug_assert!( + dep_node.kind != DepKind::codegen_unit, + "calling force_from_dep_node() on DepKind::codegen_unit" + ); + + let cb = self.query_kind(dep_node.kind); + if let Some(f) = cb.force_from_dep_node { + f(*self, dep_node); + true + } else { + false + } + } + + fn try_load_from_on_disk_cache(&self, dep_node: DepNode) { + let cb = self.query_kind(dep_node.kind); + if let Some(f) = cb.try_load_from_on_disk_cache { + f(*self, dep_node) + } + } }
diff --git a/compiler/rustc_middle/src/hir/map/blocks.rs b/compiler/rustc_middle/src/hir/map/blocks.rs deleted file mode 100644 index 8efec8e..0000000 --- a/compiler/rustc_middle/src/hir/map/blocks.rs +++ /dev/null
@@ -1,239 +0,0 @@ -//! This module provides a simplified abstraction for working with -//! code blocks identified by their integer `NodeId`. In particular, -//! it captures a common set of attributes that all "function-like -//! things" (represented by `FnLike` instances) share. For example, -//! all `FnLike` instances have a type signature (be it explicit or -//! inferred). And all `FnLike` instances have a body, i.e., the code -//! that is run when the function-like thing it represents is invoked. -//! -//! With the above abstraction in place, one can treat the program -//! text as a collection of blocks of code (and most such blocks are -//! nested within a uniquely determined `FnLike`), and users can ask -//! for the `Code` associated with a particular NodeId. - -use crate::hir::map::Map; -use rustc_hir as hir; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Expr, FnDecl, Node}; -use rustc_span::symbol::Ident; -use rustc_span::Span; - -/// An FnLikeNode is a Node that is like a fn, in that it has a decl -/// and a body (as well as a NodeId, a span, etc). -/// -/// More specifically, it is one of either: -/// -/// - A function item, -/// - A closure expr (i.e., an ExprKind::Closure), or -/// - The default implementation for a trait method. -/// -/// To construct one, use the `Code::from_node` function. -#[derive(Copy, Clone, Debug)] -pub struct FnLikeNode<'a> { - node: Node<'a>, -} - -/// MaybeFnLike wraps a method that indicates if an object -/// corresponds to some FnLikeNode. -trait MaybeFnLike { - fn is_fn_like(&self) -> bool; -} - -impl MaybeFnLike for hir::Item<'_> { - fn is_fn_like(&self) -> bool { - matches!(self.kind, hir::ItemKind::Fn(..)) - } -} - -impl MaybeFnLike for hir::ImplItem<'_> { - fn is_fn_like(&self) -> bool { - matches!(self.kind, hir::ImplItemKind::Fn(..)) - } -} - -impl MaybeFnLike for hir::TraitItem<'_> { - fn is_fn_like(&self) -> bool { - matches!(self.kind, hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_))) - } -} - -impl MaybeFnLike for hir::Expr<'_> { - fn is_fn_like(&self) -> bool { - matches!(self.kind, hir::ExprKind::Closure(..)) - } -} - -/// Carries either an FnLikeNode or an Expr, as these are the two -/// constructs that correspond to "code" (as in, something from which -/// we can construct a control-flow graph). -#[derive(Copy, Clone)] -pub enum Code<'a> { - FnLike(FnLikeNode<'a>), - Expr(&'a Expr<'a>), -} - -impl<'a> Code<'a> { - pub fn id(&self) -> hir::HirId { - match *self { - Code::FnLike(node) => node.id(), - Code::Expr(block) => block.hir_id, - } - } - - /// Attempts to construct a Code from presumed FnLike or Expr node input. - pub fn from_node(map: &Map<'a>, id: hir::HirId) -> Option<Code<'a>> { - match map.get(id) { - Node::Block(_) => { - // Use the parent, hopefully an expression node. - Code::from_node(map, map.get_parent_node(id)) - } - Node::Expr(expr) => Some(Code::Expr(expr)), - node => FnLikeNode::from_node(node).map(Code::FnLike), - } - } -} - -/// These are all the components one can extract from a fn item for -/// use when implementing FnLikeNode operations. -struct ItemFnParts<'a> { - ident: Ident, - decl: &'a hir::FnDecl<'a>, - header: hir::FnHeader, - vis: &'a hir::Visibility<'a>, - generics: &'a hir::Generics<'a>, - body: hir::BodyId, - id: hir::HirId, - span: Span, -} - -/// These are all the components one can extract from a closure expr -/// for use when implementing FnLikeNode operations. -struct ClosureParts<'a> { - decl: &'a FnDecl<'a>, - body: hir::BodyId, - id: hir::HirId, - span: Span, -} - -impl<'a> ClosureParts<'a> { - fn new(d: &'a FnDecl<'a>, b: hir::BodyId, id: hir::HirId, s: Span) -> Self { - ClosureParts { decl: d, body: b, id, span: s } - } -} - -impl<'a> FnLikeNode<'a> { - /// Attempts to construct a FnLikeNode from presumed FnLike node input. - pub fn from_node(node: Node<'_>) -> Option<FnLikeNode<'_>> { - let fn_like = match node { - Node::Item(item) => item.is_fn_like(), - Node::TraitItem(tm) => tm.is_fn_like(), - Node::ImplItem(it) => it.is_fn_like(), - Node::Expr(e) => e.is_fn_like(), - _ => false, - }; - fn_like.then_some(FnLikeNode { node }) - } - - pub fn body(self) -> hir::BodyId { - self.handle( - |i: ItemFnParts<'a>| i.body, - |_, _, _: &'a hir::FnSig<'a>, _, body: hir::BodyId, _| body, - |c: ClosureParts<'a>| c.body, - ) - } - - pub fn decl(self) -> &'a FnDecl<'a> { - self.handle( - |i: ItemFnParts<'a>| &*i.decl, - |_, _, sig: &'a hir::FnSig<'a>, _, _, _| &sig.decl, - |c: ClosureParts<'a>| c.decl, - ) - } - - pub fn span(self) -> Span { - self.handle( - |i: ItemFnParts<'_>| i.span, - |_, _, _: &'a hir::FnSig<'a>, _, _, span| span, - |c: ClosureParts<'_>| c.span, - ) - } - - pub fn id(self) -> hir::HirId { - self.handle( - |i: ItemFnParts<'_>| i.id, - |id, _, _: &'a hir::FnSig<'a>, _, _, _| id, - |c: ClosureParts<'_>| c.id, - ) - } - - pub fn constness(self) -> hir::Constness { - self.kind().header().map_or(hir::Constness::NotConst, |header| header.constness) - } - - pub fn asyncness(self) -> hir::IsAsync { - self.kind().header().map_or(hir::IsAsync::NotAsync, |header| header.asyncness) - } - - pub fn unsafety(self) -> hir::Unsafety { - self.kind().header().map_or(hir::Unsafety::Normal, |header| header.unsafety) - } - - pub fn kind(self) -> FnKind<'a> { - let item = |p: ItemFnParts<'a>| -> FnKind<'a> { - FnKind::ItemFn(p.ident, p.generics, p.header, p.vis) - }; - let closure = |_: ClosureParts<'a>| FnKind::Closure; - let method = - |_, ident: Ident, sig: &'a hir::FnSig<'a>, vis, _, _| FnKind::Method(ident, sig, vis); - self.handle(item, method, closure) - } - - fn handle<A, I, M, C>(self, item_fn: I, method: M, closure: C) -> A - where - I: FnOnce(ItemFnParts<'a>) -> A, - M: FnOnce( - hir::HirId, - Ident, - &'a hir::FnSig<'a>, - Option<&'a hir::Visibility<'a>>, - hir::BodyId, - Span, - ) -> A, - C: FnOnce(ClosureParts<'a>) -> A, - { - match self.node { - Node::Item(i) => match i.kind { - hir::ItemKind::Fn(ref sig, ref generics, block) => item_fn(ItemFnParts { - id: i.hir_id(), - ident: i.ident, - decl: &sig.decl, - body: block, - vis: &i.vis, - span: i.span, - header: sig.header, - generics, - }), - _ => bug!("item FnLikeNode that is not fn-like"), - }, - Node::TraitItem(ti) => match ti.kind { - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - method(ti.hir_id(), ti.ident, sig, None, body, ti.span) - } - _ => bug!("trait method FnLikeNode that is not fn-like"), - }, - Node::ImplItem(ii) => match ii.kind { - hir::ImplItemKind::Fn(ref sig, body) => { - method(ii.hir_id(), ii.ident, sig, Some(&ii.vis), body, ii.span) - } - _ => bug!("impl method FnLikeNode that is not fn-like"), - }, - Node::Expr(e) => match e.kind { - hir::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => { - closure(ClosureParts::new(&decl, block, e.hir_id, e.span)) - } - _ => bug!("expr FnLikeNode that is not fn-like"), - }, - _ => bug!("other FnLikeNode that is not fn-like"), - } - } -}
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index e6f56b0..d9d0781 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -1,6 +1,4 @@ -use self::collector::NodeCollector; - -use crate::hir::{AttributeMap, IndexedHir, ModuleItems, Owner}; +use crate::hir::{ModuleItems, Owner}; use crate::ty::TyCtxt; use rustc_ast as ast; use rustc_data_structures::fingerprint::Fingerprint; @@ -22,9 +20,6 @@ use rustc_target::spec::abi::Abi; use std::collections::VecDeque; -pub mod blocks; -mod collector; - fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> { match node { Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) @@ -166,8 +161,8 @@ pub fn items(&self) -> impl Iterator<Item = &'hir Item<'hir>> + 'hir { let krate = self.krate(); - krate.owners.iter().filter_map(|owner| match owner.as_ref()? { - OwnerNode::Item(item) => Some(*item), + krate.owners.iter().filter_map(|owner| match owner.as_ref()?.node() { + OwnerNode::Item(item) => Some(item), _ => None, }) } @@ -271,7 +266,15 @@ }; DefKind::Ctor(ctor_of, def::CtorKind::from_hir(variant_data)) } - Node::AnonConst(_) => DefKind::AnonConst, + Node::AnonConst(_) => { + let inline = match self.find(self.get_parent_node(hir_id)) { + Some(Node::Expr(&Expr { + kind: ExprKind::ConstBlock(ref anon_const), .. + })) if anon_const.hir_id == hir_id => true, + _ => false, + }; + if inline { DefKind::InlineConst } else { DefKind::AnonConst } + } Node::Field(_) => DefKind::Field, Node::Expr(expr) => match expr.kind { ExprKind::Closure(.., None) => DefKind::Closure, @@ -318,7 +321,7 @@ } pub fn get_parent_node(&self, hir_id: HirId) -> HirId { - self.find_parent_node(hir_id).unwrap_or(CRATE_HIR_ID) + self.find_parent_node(hir_id).unwrap() } /// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found. @@ -381,7 +384,7 @@ } pub fn body(&self, id: BodyId) -> &'hir Body<'hir> { - self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies.get(&id.hir_id.local_id).unwrap() + self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies[&id.hir_id.local_id] } pub fn fn_decl_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> { @@ -443,7 +446,7 @@ pub fn body_param_names(&self, id: BodyId) -> impl Iterator<Item = Ident> + 'hir { self.body(id).params.iter().map(|arg| match arg.pat.kind { PatKind::Binding(_, _, ident, _) => ident, - _ => Ident::new(kw::Empty, rustc_span::DUMMY_SP), + _ => Ident::empty(), }) } @@ -495,11 +498,35 @@ /// crate. If you would prefer to iterate over the bodies /// themselves, you can do `self.hir().krate().body_ids.iter()`. pub fn body_owners(self) -> impl Iterator<Item = LocalDefId> + 'hir { - self.krate().bodies.keys().map(move |&body_id| self.body_owner_def_id(body_id)) + self.krate() + .owners + .iter_enumerated() + .flat_map(move |(owner, owner_info)| { + let bodies = &owner_info.as_ref()?.nodes.bodies; + Some(bodies.iter().map(move |&(local_id, _)| { + let hir_id = HirId { owner, local_id }; + let body_id = BodyId { hir_id }; + self.body_owner_def_id(body_id) + })) + }) + .flatten() } pub fn par_body_owners<F: Fn(LocalDefId) + Sync + Send>(self, f: F) { - par_for_each_in(&self.krate().bodies, |(&body_id, _)| f(self.body_owner_def_id(body_id))); + use rustc_data_structures::sync::{par_iter, ParallelIterator}; + #[cfg(parallel_compiler)] + use rustc_rayon::iter::IndexedParallelIterator; + + par_iter(&self.krate().owners.raw).enumerate().for_each(|(owner, owner_info)| { + let owner = LocalDefId::new(owner); + if let Some(owner_info) = owner_info { + par_iter(owner_info.nodes.bodies.range(..)).for_each(|(local_id, _)| { + let hir_id = HirId { owner, local_id: *local_id }; + let body_id = BodyId { hir_id }; + f(self.body_owner_def_id(body_id)) + }) + } + }); } pub fn ty_param_owner(&self, id: HirId) -> HirId { @@ -551,9 +578,14 @@ /// Walks the attributes in a crate. pub fn walk_attributes(self, visitor: &mut impl Visitor<'hir>) { let krate = self.krate(); - for (&id, attrs) in krate.attrs.iter() { - for a in *attrs { - visitor.visit_attribute(id, a) + for (owner, info) in krate.owners.iter_enumerated() { + if let Some(info) = info { + for (local_id, attrs) in info.attrs.map.iter() { + let id = HirId { owner, local_id: *local_id }; + for a in *attrs { + visitor.visit_attribute(id, a) + } + } } } } @@ -572,7 +604,7 @@ { let krate = self.krate(); for owner in krate.owners.iter().filter_map(Option::as_ref) { - match 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), @@ -588,7 +620,7 @@ V: itemlikevisit::ParItemLikeVisitor<'hir> + Sync + Send, { let krate = self.krate(); - par_for_each_in(&krate.owners.raw, |owner| match owner.as_ref() { + par_for_each_in(&krate.owners.raw, |owner| match owner.as_ref().map(OwnerInfo::node) { Some(OwnerNode::Item(item)) => visitor.visit_item(item), Some(OwnerNode::ForeignItem(item)) => visitor.visit_foreign_item(item), Some(OwnerNode::ImplItem(item)) => visitor.visit_impl_item(item), @@ -839,21 +871,21 @@ pub fn expect_item(&self, id: HirId) -> &'hir Item<'hir> { match self.tcx.hir_owner(id.expect_owner()) { - Some(Owner { node: OwnerNode::Item(item) }) => item, + Some(Owner { node: OwnerNode::Item(item), .. }) => item, _ => bug!("expected item, found {}", self.node_to_string(id)), } } pub fn expect_impl_item(&self, id: HirId) -> &'hir ImplItem<'hir> { match self.tcx.hir_owner(id.expect_owner()) { - Some(Owner { node: OwnerNode::ImplItem(item) }) => item, + Some(Owner { node: OwnerNode::ImplItem(item), .. }) => item, _ => bug!("expected impl item, found {}", self.node_to_string(id)), } } pub fn expect_trait_item(&self, id: HirId) -> &'hir TraitItem<'hir> { match self.tcx.hir_owner(id.expect_owner()) { - Some(Owner { node: OwnerNode::TraitItem(item) }) => item, + Some(Owner { node: OwnerNode::TraitItem(item), .. }) => item, _ => bug!("expected trait item, found {}", self.node_to_string(id)), } } @@ -867,7 +899,7 @@ pub fn expect_foreign_item(&self, id: HirId) -> &'hir ForeignItem<'hir> { match self.tcx.hir_owner(id.expect_owner()) { - Some(Owner { node: OwnerNode::ForeignItem(item) }) => item, + Some(Owner { node: OwnerNode::ForeignItem(item), .. }) => item, _ => bug!("expected foreign item, found {}", self.node_to_string(id)), } } @@ -1032,42 +1064,10 @@ } } -pub(super) fn index_hir<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> &'tcx IndexedHir<'tcx> { - let _prof_timer = tcx.sess.prof.generic_activity("build_hir_map"); - - // We can access untracked state since we are an eval_always query. - let hcx = tcx.create_stable_hashing_context(); - let mut collector = NodeCollector::root( - tcx.sess, - &**tcx.arena, - tcx.untracked_crate, - &tcx.untracked_resolutions.definitions, - hcx, - ); - let top_mod = tcx.untracked_crate.module(); - collector.visit_mod(top_mod, top_mod.inner, CRATE_HIR_ID); - - let map = collector.finalize_and_compute_crate_hash(); - tcx.arena.alloc(map) -} - pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh { - assert_eq!(crate_num, LOCAL_CRATE); - - // We can access untracked state since we are an eval_always query. - let mut hcx = tcx.create_stable_hashing_context(); - - let mut hir_body_nodes: Vec<_> = tcx - .index_hir(()) - .map - .iter_enumerated() - .filter_map(|(def_id, hod)| { - let def_path_hash = tcx.untracked_resolutions.definitions.def_path_hash(def_id); - let hash = hod.as_ref()?.hash; - Some((def_path_hash, hash, def_id)) - }) - .collect(); - hir_body_nodes.sort_unstable_by_key(|bn| bn.0); + debug_assert_eq!(crate_num, LOCAL_CRATE); + let krate = tcx.hir_crate(()); + let hir_body_hash = krate.hir_hash; let upstream_crates = upstream_crates(tcx); @@ -1087,20 +1087,27 @@ source_file_names.sort_unstable(); + let mut hcx = tcx.create_stable_hashing_context(); let mut stable_hasher = StableHasher::new(); - for (def_path_hash, fingerprint, def_id) in hir_body_nodes.iter() { - def_path_hash.0.hash_stable(&mut hcx, &mut stable_hasher); - fingerprint.hash_stable(&mut hcx, &mut stable_hasher); - AttributeMap { map: &tcx.untracked_crate.attrs, prefix: *def_id } - .hash_stable(&mut hcx, &mut stable_hasher); - if tcx.sess.opts.debugging_opts.incremental_relative_spans { - let span = tcx.untracked_resolutions.definitions.def_span(*def_id); - debug_assert_eq!(span.parent(), None); - span.hash_stable(&mut hcx, &mut stable_hasher); - } - } + 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.untracked_resolutions.definitions; + let mut owner_spans: Vec<_> = krate + .owners + .iter_enumerated() + .filter_map(|(def_id, info)| { + let _ = info.as_ref()?; + let def_path_hash = definitions.def_path_hash(def_id); + let span = definitions.def_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);
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 5016c5c..95d7273b 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -8,28 +8,12 @@ use crate::ty::query::Providers; use crate::ty::TyCtxt; -use rustc_ast::Attribute; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::LocalDefId; use rustc_hir::*; -use rustc_index::vec::{Idx, IndexVec}; use rustc_query_system::ich::StableHashingContext; use rustc_span::DUMMY_SP; -use std::collections::BTreeMap; - -/// Result of HIR indexing. -#[derive(Debug)] -pub struct IndexedHir<'hir> { - /// Contents of the HIR owned by each definition. None for definitions that are not HIR owners. - // The `mut` comes from construction time, and is harmless since we only ever hand out - // immutable refs to IndexedHir. - map: IndexVec<LocalDefId, Option<&'hir mut OwnerNodes<'hir>>>, - /// Map from each owner to its parent's HirId inside another owner. - // This map is separate from `map` to eventually allow for per-owner indexing. - parenting: FxHashMap<LocalDefId, HirId>, -} /// Top-level HIR node for current owner. This only contains the node for which /// `HirId::local_id == 0`, and excludes bodies. @@ -39,85 +23,14 @@ #[derive(Copy, Clone, Debug)] pub struct Owner<'tcx> { node: OwnerNode<'tcx>, + hash_without_bodies: Fingerprint, } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Owner<'tcx> { + #[inline] fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let Owner { node } = self; - hcx.while_hashing_hir_bodies(false, |hcx| node.hash_stable(hcx, hasher)); - } -} - -/// HIR node coupled with its parent's id in the same HIR owner. -/// -/// The parent is trash when the node is a HIR owner. -#[derive(Clone, Debug)] -pub struct ParentedNode<'tcx> { - parent: ItemLocalId, - node: Node<'tcx>, -} - -#[derive(Debug)] -pub struct OwnerNodes<'tcx> { - /// Pre-computed hash of the full HIR. - hash: Fingerprint, - /// Full HIR for the current owner. - // The zeroth node's parent is trash, but is never accessed. - nodes: IndexVec<ItemLocalId, Option<ParentedNode<'tcx>>>, - /// Content of local bodies. - bodies: FxHashMap<ItemLocalId, &'tcx Body<'tcx>>, -} - -impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for OwnerNodes<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - // We ignore the `nodes` and `bodies` fields since these refer to information included in - // `hash` which is hashed in the collector and used for the crate hash. - let OwnerNodes { hash, nodes: _, bodies: _ } = *self; - hash.hash_stable(hcx, hasher); - } -} - -/// Attributes owner by a HIR owner. It is build as a slice inside the attributes map, restricted -/// to the nodes whose `HirId::owner` is `prefix`. -#[derive(Copy, Clone)] -pub struct AttributeMap<'tcx> { - map: &'tcx BTreeMap<HirId, &'tcx [Attribute]>, - prefix: LocalDefId, -} - -impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for AttributeMap<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let range = self.range(); - - range.clone().count().hash_stable(hcx, hasher); - for (key, value) in range { - key.hash_stable(hcx, hasher); - value.hash_stable(hcx, hasher); - } - } -} - -impl<'tcx> std::fmt::Debug for AttributeMap<'tcx> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("AttributeMap") - .field("prefix", &self.prefix) - .field("range", &&self.range().collect::<Vec<_>>()[..]) - .finish() - } -} - -impl<'tcx> AttributeMap<'tcx> { - fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] { - self.map.get(&HirId { owner: self.prefix, local_id: id }).copied().unwrap_or(&[]) - } - - fn range(&self) -> std::collections::btree_map::Range<'_, rustc_hir::HirId, &[Attribute]> { - let local_zero = ItemLocalId::from_u32(0); - let range = HirId { owner: self.prefix, local_id: local_zero }..HirId { - owner: LocalDefId { local_def_index: self.prefix.local_def_index + 1 }, - local_id: local_zero, - }; - self.map.range(range) + let Owner { node: _, hash_without_bodies } = self; + hash_without_bodies.hash_stable(hcx, hasher) } } @@ -149,21 +62,32 @@ hir.local_def_id(hir.get_module_parent_node(hir.local_def_id_to_hir_id(id))) }; providers.hir_crate = |tcx, ()| tcx.untracked_crate; - providers.index_hir = map::index_hir; providers.crate_hash = map::crate_hash; providers.hir_module_items = map::hir_module_items; providers.hir_owner = |tcx, id| { - let owner = tcx.index_hir(()).map[id].as_ref()?; - let node = owner.nodes[ItemLocalId::new(0)].as_ref().unwrap().node; - let node = node.as_owner().unwrap(); // Indexing must ensure it is an OwnerNode. - Some(Owner { node }) + let owner = tcx.hir_crate(()).owners[id].as_ref()?; + let node = owner.node(); + Some(Owner { node, hash_without_bodies: owner.nodes.hash_without_bodies }) }; - providers.hir_owner_nodes = |tcx, id| tcx.index_hir(()).map[id].as_deref(); + providers.hir_owner_nodes = |tcx, id| tcx.hir_crate(()).owners[id].as_ref().map(|i| &i.nodes); providers.hir_owner_parent = |tcx, id| { - let index = tcx.index_hir(()); - index.parenting.get(&id).copied().unwrap_or(CRATE_HIR_ID) + // Accessing the def_key is ok since its value is hashed as part of `id`'s DefPathHash. + let parent = tcx.untracked_resolutions.definitions.def_key(id).parent; + let parent = parent.map_or(CRATE_HIR_ID, |local_def_index| { + let def_id = LocalDefId { local_def_index }; + let mut parent_hir_id = + tcx.untracked_resolutions.definitions.local_def_id_to_hir_id(def_id); + if let Some(local_id) = + tcx.hir_crate(()).owners[parent_hir_id.owner].as_ref().unwrap().parenting.get(&id) + { + parent_hir_id.local_id = *local_id; + } + parent_hir_id + }); + parent }; - providers.hir_attrs = |tcx, id| AttributeMap { map: &tcx.untracked_crate.attrs, prefix: id }; + providers.hir_attrs = + |tcx, id| tcx.hir_crate(()).owners[id].as_ref().map_or(AttributeMap::EMPTY, |o| &o.attrs); providers.source_span = |tcx, def_id| tcx.resolutions(()).definitions.def_span(def_id); providers.def_span = |tcx, def_id| tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP); providers.fn_arg_names = |tcx, id| {
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index e41f5ad..9ce9f65 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs
@@ -30,6 +30,7 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(core_intrinsics)] +#![feature(derive_default_enum)] #![feature(discriminant_kind)] #![feature(exhaustive_patterns)] #![feature(if_let_guard)] @@ -39,6 +40,7 @@ #![feature(new_uninit)] #![feature(nll)] #![feature(once_cell)] +#![feature(let_else)] #![feature(min_specialization)] #![feature(trusted_len)] #![feature(in_band_lifetimes)]
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 1eba299..881b142 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs
@@ -248,8 +248,12 @@ (Level::Warn, None) => sess.struct_warn(""), (Level::ForceWarn, Some(span)) => sess.struct_span_force_warn(span, ""), (Level::ForceWarn, None) => sess.struct_force_warn(""), - (Level::Deny | Level::Forbid, Some(span)) => sess.struct_span_err(span, ""), - (Level::Deny | Level::Forbid, None) => sess.struct_err(""), + (Level::Deny | Level::Forbid, Some(span)) => { + let mut builder = sess.diagnostic().struct_err_lint(""); + builder.set_span(span); + builder + } + (Level::Deny | Level::Forbid, None) => sess.diagnostic().struct_err_lint(""), }; // If this code originates in a foreign macro, aka something that this crate @@ -391,7 +395,7 @@ match expn_data.kind { ExpnKind::Inlined | ExpnKind::Root - | ExpnKind::Desugaring(DesugaringKind::ForLoop(_) | DesugaringKind::WhileLoop) => false, + | ExpnKind::Desugaring(DesugaringKind::ForLoop | DesugaringKind::WhileLoop) => false, ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external" ExpnKind::Macro(MacroKind::Bang, _) => { // Dummy span for the `def_site` means it's an external macro.
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 597622b..8a5fc5f 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -3,7 +3,7 @@ pub use self::StabilityLevel::*; -use crate::ty::{self, TyCtxt}; +use crate::ty::{self, DefIdTree, TyCtxt}; use rustc_ast::NodeId; use rustc_attr::{self as attr, ConstStability, Deprecation, Stability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -90,6 +90,7 @@ feature: Symbol, reason: Option<Symbol>, issue: Option<NonZeroU32>, + suggestion: Option<(Span, String, String, Applicability)>, is_soft: bool, span: Span, soft_handler: impl FnOnce(&'static Lint, Span, &str), @@ -116,8 +117,12 @@ if is_soft { soft_handler(SOFT_UNSTABLE, span, &msg) } else { - feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg) - .emit(); + let mut err = + feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg); + if let Some((inner_types, ref msg, sugg, applicability)) = suggestion { + err.span_suggestion(inner_types, msg, sugg, applicability); + } + err.emit(); } } } @@ -271,7 +276,13 @@ Allow, /// We cannot use the item because it is unstable and we did not provide the /// corresponding feature gate. - Deny { feature: Symbol, reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool }, + Deny { + feature: Symbol, + reason: Option<Symbol>, + issue: Option<NonZeroU32>, + suggestion: Option<(Span, String, String, Applicability)>, + is_soft: bool, + }, /// The item does not have the `#[stable]` or `#[unstable]` marker assigned. Unmarked, } @@ -292,6 +303,32 @@ } } +// See issue #83250. +fn suggestion_for_allocator_api( + tcx: TyCtxt<'_>, + def_id: DefId, + span: Span, + feature: Symbol, +) -> Option<(Span, String, String, Applicability)> { + if feature == sym::allocator_api { + if let Some(trait_) = tcx.parent(def_id) { + if tcx.is_diagnostic_item(sym::Vec, trait_) { + let sm = tcx.sess.parse_sess.source_map(); + let inner_types = sm.span_extend_to_prev_char(span, '<', true); + if let Ok(snippet) = sm.span_to_snippet(inner_types) { + return Some(( + inner_types, + "consider wrapping the inner types in tuple".to_string(), + format!("({})", snippet), + Applicability::MaybeIncorrect, + )); + } + } + } + } + None +} + impl<'tcx> TyCtxt<'tcx> { /// Evaluates the stability of an item. /// @@ -406,7 +443,8 @@ } } - EvalResult::Deny { feature, reason, issue, is_soft } + let suggestion = suggestion_for_allocator_api(self, def_id, span, feature); + EvalResult::Deny { feature, reason, issue, suggestion, is_soft } } Some(_) => { // Stable APIs are always ok to call and deprecated APIs are @@ -457,9 +495,16 @@ }; match self.eval_stability(def_id, id, span, method_span) { EvalResult::Allow => {} - EvalResult::Deny { feature, reason, issue, is_soft } => { - report_unstable(self.sess, feature, reason, issue, is_soft, span, soft_handler) - } + EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable( + self.sess, + feature, + reason, + issue, + suggestion, + is_soft, + span, + soft_handler, + ), EvalResult::Unmarked => unmarked(span, def_id), } }
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 9472a28..7a51bb4 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -538,12 +538,12 @@ /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors, /// so this method lets us detect them and `bug!` on unexpected errors. pub fn formatted_string(&self) -> bool { - match self { + matches!( + self, InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. }) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) => true, - _ => false, - } + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. }) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) + ) } /// Should this error be reported as a hard error, preventing compilation, or a soft error,
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 971556d..4210e07 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -2246,8 +2246,12 @@ /// The `*` operator (multiplication) Mul, /// The `/` operator (division) + /// + /// Division by zero is UB. Div, /// The `%` operator (modulus) + /// + /// Using zero as the modulus (second operand) is UB. Rem, /// The `^` operator (bitwise xor) BitXor, @@ -2256,8 +2260,12 @@ /// The `|` operator (bitwise or) BitOr, /// The `<<` operator (shift left) + /// + /// The offset is truncated to the size of the first operand before shifting. Shl, /// The `>>` operator (shift right) + /// + /// The offset is truncated to the size of the first operand before shifting. Shr, /// The `==` operator (equality) Eq, @@ -2665,7 +2673,7 @@ mut self, mut f: impl FnMut(UserTypeProjection) -> UserTypeProjection, ) -> Self { - self.contents = self.contents.drain(..).map(|(proj, span)| (f(proj), span)).collect(); + self.contents = self.contents.into_iter().map(|(proj, span)| (f(proj), span)).collect(); self }
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index db98cb7..8e1b887 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -71,6 +71,7 @@ /// or `typeck` appears in the name. /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name /// or `typeck` and `bar` both appear in the name. +#[inline] pub fn dump_mir<'tcx, F>( tcx: TyCtxt<'tcx>, pass_num: Option<&dyn Display>, @@ -957,7 +958,7 @@ write!(w, "static {}", if tcx.is_mutable_static(def_id) { "mut " } else { "" })? } (_, _) if is_function => write!(w, "fn ")?, - (DefKind::AnonConst, _) => {} // things like anon const, not an item + (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item _ => bug!("Unexpected def kind {:?}", kind), }
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index d5541d7..cb3f385 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs
@@ -225,6 +225,7 @@ pub struct ConstQualifs { pub has_mut_interior: bool, pub needs_drop: bool, + pub needs_non_const_drop: bool, pub custom_eq: bool, pub error_occured: Option<ErrorReported>, }
diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs index 42683da..1260c69 100644 --- a/compiler/rustc_middle/src/mir/spanview.rs +++ b/compiler/rustc_middle/src/mir/spanview.rs
@@ -632,11 +632,11 @@ for statement in statements { let source_range = source_range_no_file(tcx, &statement.source_info.span); text.push(format!( - "\n{}{}: {}: {}", + "\n{}{}: {}: {:?}", TOOLTIP_INDENT, source_range, statement_kind_name(&statement), - format!("{:?}", statement) + statement )); } if let Some(term) = terminator {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 4145cbd..ca93efb 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs
@@ -36,18 +36,9 @@ /// prefer wrappers like `tcx.visit_all_items_in_krate()`. query hir_crate(key: ()) -> &'tcx Crate<'tcx> { eval_always - no_hash desc { "get the crate HIR" } } - /// The indexed HIR. This can be conveniently accessed by `tcx.hir()`. - /// Avoid calling this query directly. - query index_hir(_: ()) -> &'tcx crate::hir::IndexedHir<'tcx> { - eval_always - no_hash - desc { "index HIR" } - } - /// The items in a module. /// /// This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`. @@ -62,7 +53,6 @@ /// This can be conveniently accessed by methods on `tcx.hir()`. /// Avoid calling this query directly. query hir_owner(key: LocalDefId) -> Option<crate::hir::Owner<'tcx>> { - eval_always desc { |tcx| "HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) } } @@ -71,7 +61,6 @@ /// This can be conveniently accessed by methods on `tcx.hir()`. /// Avoid calling this query directly. query hir_owner_parent(key: LocalDefId) -> hir::HirId { - eval_always desc { |tcx| "HIR parent of `{}`", tcx.def_path_str(key.to_def_id()) } } @@ -79,8 +68,7 @@ /// /// This can be conveniently accessed by methods on `tcx.hir()`. /// Avoid calling this query directly. - query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx crate::hir::OwnerNodes<'tcx>> { - eval_always + query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx hir::OwnerNodes<'tcx>> { desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } } @@ -88,8 +76,7 @@ /// /// This can be conveniently accessed by methods on `tcx.hir()`. /// Avoid calling this query directly. - query hir_attrs(key: LocalDefId) -> rustc_middle::hir::AttributeMap<'tcx> { - eval_always + query hir_attrs(key: LocalDefId) -> &'tcx hir::AttributeMap<'tcx> { desc { |tcx| "HIR owner attributes in `{}`", tcx.def_path_str(key.to_def_id()) } } @@ -120,6 +107,7 @@ /// parameter. e.g. `fn example<const N: usize=3>` called on `N` would return `3`. query const_param_default(param: DefId) -> &'tcx ty::Const<'tcx> { desc { |tcx| "compute const default for a given parameter `{}`", tcx.def_path_str(param) } + separate_provide_extern } query default_anon_const_substs(key: DefId) -> SubstsRef<'tcx> { @@ -141,6 +129,7 @@ path = tcx.def_path_str(key), } cache_on_disk_if { key.is_local() } + separate_provide_extern } query analysis(key: ()) -> Result<(), ErrorReported> { @@ -154,6 +143,7 @@ desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) } storage(ArenaCacheSelector<'tcx>) cache_on_disk_if { key.is_local() } + separate_provide_extern } /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the @@ -194,6 +184,7 @@ /// Bounds from the parent (e.g. with nested impl trait) are not included. query explicit_item_bounds(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + separate_provide_extern } /// Elaborated version of the predicates from `explicit_item_bounds`. @@ -222,6 +213,7 @@ query native_libraries(_: CrateNum) -> Lrc<Vec<NativeLib>> { desc { "looking up the native libraries of a linked crate" } + separate_provide_extern } query lint_levels(_: ()) -> LintLevelMap { @@ -239,11 +231,13 @@ // This query reads from untracked data in definitions. eval_always desc { |tcx| "expansion that defined `{}`", tcx.def_path_str(key) } + separate_provide_extern } query is_panic_runtime(_: CrateNum) -> bool { fatal_cycle desc { "checking if the crate is_panic_runtime" } + separate_provide_extern } /// Fetch the THIR for a given body. If typeck for that body failed, returns an empty `Thir`. @@ -273,6 +267,7 @@ query mir_const_qualif(key: DefId) -> mir::ConstQualifs { desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } + separate_provide_extern } query mir_const_qualif_const_arg( key: (LocalDefId, DefId) @@ -309,6 +304,7 @@ desc { |tcx| "building an abstract representation for {}", tcx.def_path_str(key), } + separate_provide_extern } /// Try to build an abstract representation of the given constant. query thir_abstract_const_of_const_arg( @@ -342,6 +338,7 @@ ) -> &'tcx mir::Body<'tcx> { desc { |tcx| "caching mir of `{}` for CTFE", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } + separate_provide_extern } query mir_for_ctfe_of_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::Body<'tcx> { @@ -379,6 +376,7 @@ query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { desc { |tcx| "optimizing MIR for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } + separate_provide_extern } /// Returns coverage summary info for a function, after executing the `InstrumentCoverage` @@ -417,6 +415,7 @@ query promoted_mir(key: DefId) -> &'tcx IndexVec<mir::Promoted, mir::Body<'tcx>> { desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } + separate_provide_extern } query promoted_mir_of_const_arg( key: (LocalDefId, DefId) @@ -475,12 +474,14 @@ /// Returns the predicates written explicitly by the user. query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } + separate_provide_extern } /// Returns the inferred outlives predicates (e.g., for `struct /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`). query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) } + separate_provide_extern } /// Maps from the `DefId` of a trait to the list of @@ -491,6 +492,7 @@ /// additional acyclicity requirements). query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing the super predicates of `{}`", tcx.def_path_str(key) } + separate_provide_extern } /// The `Option<Ident>` is the name of an associated type. If it is `None`, then this query @@ -516,12 +518,15 @@ query trait_def(key: DefId) -> ty::TraitDef { desc { |tcx| "computing trait definition for `{}`", tcx.def_path_str(key) } storage(ArenaCacheSelector<'tcx>) + separate_provide_extern } query adt_def(key: DefId) -> &'tcx ty::AdtDef { desc { |tcx| "computing ADT definition for `{}`", tcx.def_path_str(key) } + separate_provide_extern } query adt_destructor(key: DefId) -> Option<ty::Destructor> { desc { |tcx| "computing `Drop` impl for `{}`", tcx.def_path_str(key) } + separate_provide_extern } // The cycle error here should be reported as an error by `check_representable`. @@ -550,10 +555,12 @@ /// `is_const_fn` function. query is_const_fn_raw(key: DefId) -> bool { desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) } + separate_provide_extern } query asyncness(key: DefId) -> hir::IsAsync { desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } + separate_provide_extern } /// Returns `true` if calls to the function may be promoted. @@ -570,16 +577,19 @@ /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). query is_foreign_item(key: DefId) -> bool { desc { |tcx| "checking if `{}` is a foreign item", tcx.def_path_str(key) } + separate_provide_extern } /// Returns `Some(mutability)` if the node pointed to by `def_id` is a static item. query static_mutability(def_id: DefId) -> Option<hir::Mutability> { desc { |tcx| "looking up static mutability of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } /// Returns `Some(generator_kind)` if the node pointed to by `def_id` is a generator. query generator_kind(def_id: DefId) -> Option<hir::GeneratorKind> { desc { |tcx| "looking up generator kind of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } /// Gets a map with the variance of every item; use `item_variance` instead. @@ -591,6 +601,7 @@ /// Maps from the `DefId` of a type or region parameter to its (inferred) variance. query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } /// Maps from thee `DefId` of a type to its (inferred) outlives. @@ -602,12 +613,14 @@ /// Maps from an impl/trait `DefId` to a list of the `DefId`s of its items. query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } + separate_provide_extern } /// Maps from a trait item to the trait item "descriptor". query associated_item(key: DefId) -> ty::AssocItem { desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } storage(ArenaCacheSelector<'tcx>) + separate_provide_extern } /// Collects the associated items defined on a trait or impl. @@ -620,9 +633,11 @@ /// Return `None` if this is an inherent impl. query impl_trait_ref(impl_id: DefId) -> Option<ty::TraitRef<'tcx>> { desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) } + separate_provide_extern } query impl_polarity(impl_id: DefId) -> ty::ImplPolarity { desc { |tcx| "computing implementation polarity of `{}`", tcx.def_path_str(impl_id) } + separate_provide_extern } query issue33140_self_ty(key: DefId) -> Option<ty::Ty<'tcx>> { @@ -634,7 +649,7 @@ /// Methods in these implementations don't need to be exported. query inherent_impls(key: DefId) -> &'tcx [DefId] { desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } - eval_always + separate_provide_extern } /// The result of unsafety-checking this `LocalDefId`. @@ -671,11 +686,13 @@ desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } } - /// The signature of functions. + /// Computes the signature of the function. query fn_sig(key: DefId) -> ty::PolyFnSig<'tcx> { desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } + separate_provide_extern } + /// Performs lint checking for the module. query lint_mod(key: LocalDefId) -> () { desc { |tcx| "linting {}", describe_as_module(key, tcx) } } @@ -685,6 +702,7 @@ desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } } + /// Checks for uses of unstable APIs in the module. query check_mod_unstable_api_usage(key: LocalDefId) -> () { desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } } @@ -728,10 +746,10 @@ } /// Caches `CoerceUnsized` kinds for impls on custom types. - query coerce_unsized_info(key: DefId) - -> ty::adjustment::CoerceUnsizedInfo { - desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } - } + query coerce_unsized_info(key: DefId) -> ty::adjustment::CoerceUnsizedInfo { + desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } + separate_provide_extern + } query typeck_item_bodies(_: ()) -> () { desc { "type-checking all item bodies" } @@ -778,10 +796,7 @@ /// additional requirements that the closure's creator must verify. query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> { desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if(tcx, opt_result) { - tcx.is_closure(key.to_def_id()) - || opt_result.map_or(false, |r| !r.concrete_opaque_types.is_empty()) - } + cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) } } query mir_borrowck_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::BorrowCheckResult<'tcx> { desc { @@ -794,18 +809,23 @@ /// Not meant to be used directly outside of coherence. query crate_inherent_impls(k: ()) -> CrateInherentImpls { storage(ArenaCacheSelector<'tcx>) - eval_always desc { "all inherent impls defined in crate" } } /// Checks all types in the crate for overlap in their inherent impls. Reports errors. /// Not meant to be used directly outside of coherence. - query crate_inherent_impls_overlap_check(_: ()) - -> () { - eval_always + query crate_inherent_impls_overlap_check(_: ()) -> () { desc { "check for overlap between inherent impls defined in this crate" } } + /// Checks whether all impls in the crate pass the overlap check, returning + /// which impls fail it. If all impls are correct, the returned slice is empty. + query orphan_check_crate(_: ()) -> &'tcx [LocalDefId] { + desc { + "checking whether the immpl in the this crate follow the orphan rules", + } + } + /// Check whether the function has any recursion that could cause the inliner to trigger /// a cycle. Returns the call stack causing the cycle. The call stack does not contain the /// current function, just all intermediate functions. @@ -914,6 +934,7 @@ desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) } } + /// Generates a MIR body for the shim. query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> { storage(ArenaCacheSelector<'tcx>) desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } @@ -929,28 +950,29 @@ query opt_def_kind(def_id: DefId) -> Option<DefKind> { desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } + /// Gets the span for the definition. query def_span(def_id: DefId) -> Span { desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } - // FIXME(mw): DefSpans are not really inputs since they are derived from - // HIR. But at the moment HIR hashing still contains some hacks that allow - // to make type debuginfo to be source location independent. Declaring - // DefSpan an input makes sure that changes to these are always detected - // regardless of HIR hashing. - eval_always + separate_provide_extern } + /// Gets the span for the identifier of the definition. query def_ident_span(def_id: DefId) -> Option<Span> { desc { |tcx| "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) } + separate_provide_extern } query lookup_stability(def_id: DefId) -> Option<&'tcx attr::Stability> { desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } query lookup_const_stability(def_id: DefId) -> Option<&'tcx attr::ConstStability> { desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } query should_inherit_track_caller(def_id: DefId) -> bool { @@ -959,10 +981,12 @@ query lookup_deprecation_entry(def_id: DefId) -> Option<DeprecationEntry> { desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } + separate_provide_extern } query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] { desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } query codegen_fn_attrs(def_id: DefId) -> CodegenFnAttrs { @@ -973,27 +997,33 @@ query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] { desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } /// Gets the rendered value of the specified constant or associated constant. /// Used by rustdoc. query rendered_const(def_id: DefId) -> String { desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } query impl_parent(def_id: DefId) -> Option<DefId> { desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } /// Given an `associated_item`, find the trait it belongs to. /// Return `None` if the `DefId` is not an associated item. query trait_of_item(associated_item: DefId) -> Option<DefId> { desc { |tcx| "finding trait defining `{}`", tcx.def_path_str(associated_item) } + separate_provide_extern } query is_ctfe_mir_available(key: DefId) -> bool { desc { |tcx| "checking if item has ctfe mir available: `{}`", tcx.def_path_str(key) } + separate_provide_extern } query is_mir_available(key: DefId) -> bool { desc { |tcx| "checking if item has mir available: `{}`", tcx.def_path_str(key) } + separate_provide_extern } query own_existential_vtable_entries( @@ -1030,11 +1060,6 @@ } /// Return all `impl` blocks in the current crate. - /// - /// To allow caching this between crates, you must pass in [`LOCAL_CRATE`] as the crate number. - /// Passing in any other crate will cause an ICE. - /// - /// [`LOCAL_CRATE`]: rustc_hir::def_id::LOCAL_CRATE query all_local_trait_impls(_: ()) -> &'tcx BTreeMap<DefId, Vec<LocalDefId>> { desc { "local trait impls" } } @@ -1159,6 +1184,7 @@ query dylib_dependency_formats(_: CrateNum) -> &'tcx [(CrateNum, LinkagePreference)] { desc { "dylib dependency formats of crate" } + separate_provide_extern } query dependency_formats(_: ()) -> Lrc<crate::middle::dependency_format::Dependencies> { @@ -1168,41 +1194,50 @@ query is_compiler_builtins(_: CrateNum) -> bool { fatal_cycle desc { "checking if the crate is_compiler_builtins" } + separate_provide_extern } query has_global_allocator(_: CrateNum) -> bool { // This query depends on untracked global state in CStore eval_always fatal_cycle desc { "checking if the crate has_global_allocator" } + separate_provide_extern } query has_panic_handler(_: CrateNum) -> bool { fatal_cycle desc { "checking if the crate has_panic_handler" } + separate_provide_extern } query is_profiler_runtime(_: CrateNum) -> bool { fatal_cycle desc { "query a crate is `#![profiler_runtime]`" } + separate_provide_extern } query panic_strategy(_: CrateNum) -> PanicStrategy { fatal_cycle desc { "query a crate's configured panic strategy" } + separate_provide_extern } query panic_in_drop_strategy(_: CrateNum) -> PanicStrategy { fatal_cycle desc { "query a crate's configured panic-in-drop strategy" } + separate_provide_extern } query is_no_builtins(_: CrateNum) -> bool { fatal_cycle desc { "test whether a crate has `#![no_builtins]`" } + separate_provide_extern } query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { fatal_cycle desc { "query a crate's symbol mangling version" } + separate_provide_extern } query extern_crate(def_id: DefId) -> Option<&'tcx ExternCrate> { eval_always desc { "getting crate's ExternCrateData" } + separate_provide_extern } query specializes(_: (DefId, DefId)) -> bool { @@ -1219,10 +1254,12 @@ query impl_defaultness(def_id: DefId) -> hir::Defaultness { desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } + separate_provide_extern } query impl_constness(def_id: DefId) -> hir::Constness { desc { |tcx| "looking up whether `{}` is a const impl", tcx.def_path_str(def_id) } + separate_provide_extern } query check_item_well_formed(key: LocalDefId) -> () { @@ -1251,9 +1288,11 @@ -> DefIdMap<SymbolExportLevel> { storage(ArenaCacheSelector<'tcx>) desc { "looking up the exported symbols of a crate" } + separate_provide_extern } query is_reachable_non_generic(def_id: DefId) -> bool { desc { |tcx| "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } + separate_provide_extern } query is_unreachable_local_definition(def_id: LocalDefId) -> bool { desc { |tcx| @@ -1286,6 +1325,7 @@ "collecting available upstream monomorphizations for `{}`", tcx.def_path_str(def_id), } + separate_provide_extern } /// Returns the upstream crate that exports drop-glue for the given @@ -1309,6 +1349,7 @@ query foreign_modules(_: CrateNum) -> Lrc<FxHashMap<DefId, ForeignModule>> { desc { "looking up the foreign modules of a linked crate" } + separate_provide_extern } /// Identifies the entry-point (e.g., the `main` function) for a given @@ -1324,18 +1365,22 @@ query crate_hash(_: CrateNum) -> Svh { eval_always desc { "looking up the hash a crate" } + separate_provide_extern } query crate_host_hash(_: CrateNum) -> Option<Svh> { eval_always desc { "looking up the hash of a host version of a crate" } + separate_provide_extern } query extra_filename(_: CrateNum) -> String { eval_always desc { "looking up the extra filename for a crate" } + separate_provide_extern } query crate_extern_paths(_: CrateNum) -> Vec<PathBuf> { eval_always desc { "looking up the paths for extern crates" } + separate_provide_extern } /// Given a crate and a trait, look up all impls of that trait in the crate. @@ -1343,6 +1388,7 @@ query implementations_of_trait(_: (CrateNum, DefId)) -> &'tcx [(DefId, Option<ty::fast_reject::SimplifiedType>)] { desc { "looking up implementations of a trait in a crate" } + separate_provide_extern } /// Given a crate, look up all trait impls in that crate. @@ -1350,6 +1396,7 @@ query all_trait_implementations(_: CrateNum) -> &'tcx [(DefId, Option<ty::fast_reject::SimplifiedType>)] { desc { "looking up all (?) trait implementations" } + separate_provide_extern } query is_dllimport_foreign_item(def_id: DefId) -> bool { @@ -1406,6 +1453,7 @@ query visibility(def_id: DefId) -> ty::Visibility { desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } /// Computes the set of modules from which this type is visibly uninhabited. @@ -1420,13 +1468,18 @@ query dep_kind(_: CrateNum) -> CrateDepKind { eval_always desc { "fetching what a dependency looks like" } + separate_provide_extern } + + /// Gets the name of the crate. query crate_name(_: CrateNum) -> Symbol { eval_always desc { "fetching what a crate is named" } + separate_provide_extern } query item_children(def_id: DefId) -> &'tcx [Export] { desc { |tcx| "collecting child items of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern } query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option<CrateNum> { desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id.to_def_id()) } @@ -1440,6 +1493,7 @@ query defined_lib_features(_: CrateNum) -> &'tcx [(Symbol, Option<Symbol>)] { desc { "calculating the lib features defined in a crate" } + separate_provide_extern } /// Returns the lang items defined in another crate by loading it from metadata. query get_lang_items(_: ()) -> LanguageItems { @@ -1458,16 +1512,19 @@ /// Returns the lang items defined in another crate by loading it from metadata. query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] { desc { "calculating the lang items defined in a crate" } + separate_provide_extern } /// Returns the diagnostic items defined in a crate. query diagnostic_items(_: CrateNum) -> rustc_hir::diagnostic_items::DiagnosticItems { storage(ArenaCacheSelector<'tcx>) desc { "calculating the diagnostic items map in a crate" } + separate_provide_extern } query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { desc { "calculating the missing lang items in a crate" } + separate_provide_extern } query visible_parent_map(_: ()) -> DefIdMap<DefId> { storage(ArenaCacheSelector<'tcx>) @@ -1480,10 +1537,12 @@ query missing_extern_crate_item(_: CrateNum) -> bool { eval_always desc { "seeing if we're missing an `extern crate` item for this crate" } + separate_provide_extern } query used_crate_source(_: CrateNum) -> Lrc<CrateSource> { eval_always desc { "looking at the source for a crate" } + separate_provide_extern } query postorder_cnums(_: ()) -> &'tcx [CrateNum] { eval_always @@ -1494,6 +1553,7 @@ query is_private_dep(c: CrateNum) -> bool { eval_always desc { "check whether crate {} is a private dependency", c } + separate_provide_extern } query allocator_kind(_: ()) -> Option<AllocatorKind> { eval_always @@ -1502,7 +1562,6 @@ query upvars_mentioned(def_id: DefId) -> Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>> { desc { |tcx| "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) } - eval_always } query maybe_unused_trait_import(def_id: LocalDefId) -> bool { desc { |tcx| "maybe_unused_trait_import for `{}`", tcx.def_path_str(def_id.to_def_id()) } @@ -1539,6 +1598,7 @@ query exported_symbols(_: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] { desc { "exported_symbols" } + separate_provide_extern } query collect_and_partition_mono_items(_: ()) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { @@ -1564,6 +1624,7 @@ |tcx| "determining which generic parameters are unused by `{}`", tcx.def_path_str(key.def_id()) } + separate_provide_extern } query backend_optimization_level(_: ()) -> OptLevel { desc { "optimization level used by backend" }
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index b089ae2..49a64cb2 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -7,6 +7,7 @@ pub mod select; pub mod specialization_graph; mod structural_impls; +pub mod util; use crate::infer::canonical::Canonical; use crate::thir::abstract_const::NotConstEvaluatable; @@ -23,6 +24,7 @@ use std::borrow::Cow; use std::fmt; +use std::hash::{Hash, Hasher}; use std::ops::Deref; pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache}; @@ -108,7 +110,7 @@ } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] +#[derive(Clone, Debug, PartialEq, Eq, Lift)] pub struct ObligationCauseData<'tcx> { pub span: Span, @@ -123,6 +125,14 @@ pub code: ObligationCauseCode<'tcx>, } +impl Hash for ObligationCauseData<'_> { + fn hash<H: Hasher>(&self, state: &mut H) { + self.body_id.hash(state); + self.span.hash(state); + std::mem::discriminant(&self.code).hash(state); + } +} + impl<'tcx> ObligationCause<'tcx> { #[inline] pub fn new( @@ -267,14 +277,12 @@ /// Error derived when matching traits/impls; see ObligationCause for more details CompareImplMethodObligation { - item_name: Symbol, impl_item_def_id: DefId, trait_item_def_id: DefId, }, /// Error derived when matching traits/impls; see ObligationCause for more details CompareImplTypeObligation { - item_name: Symbol, impl_item_def_id: DefId, trait_item_def_id: DefId, }, @@ -440,16 +448,28 @@ #[derive(Clone, Debug, TypeFoldable, Lift)] pub enum SelectionError<'tcx> { + /// The trait is not implemented. Unimplemented, + /// After a closure impl has selected, its "outputs" were evaluated + /// (which for closures includes the "input" type params) and they + /// didn't resolve. See `confirm_poly_trait_refs` for more. OutputTypeParameterMismatch( ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>, ty::error::TypeError<'tcx>, ), + /// The trait pointed by `DefId` is not object safe. TraitNotObjectSafe(DefId), + /// A given constant couldn't be evaluated. NotConstEvaluatable(NotConstEvaluatable), + /// Exceeded the recursion depth during type projection. Overflow, + /// Signaling that an error has already been emitted, to avoid + /// multiple errors being shown. ErrorReporting, + /// Multiple applicable `impl`s where found. The `DefId`s correspond to + /// all the `impl`s' Items. + Ambiguous(Vec<DefId>), } /// When performing resolution, it is typically the case that there
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 6720493..5606605 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs
@@ -12,12 +12,14 @@ use rustc_query_system::cache::Cache; pub type SelectionCache<'tcx> = Cache< - ty::ConstnessAnd<ty::ParamEnvAnd<'tcx, ty::TraitRef<'tcx>>>, + (ty::ConstnessAnd<ty::ParamEnvAnd<'tcx, ty::TraitRef<'tcx>>>, ty::ImplPolarity), SelectionResult<'tcx, SelectionCandidate<'tcx>>, >; -pub type EvaluationCache<'tcx> = - Cache<ty::ParamEnvAnd<'tcx, ty::ConstnessAnd<ty::PolyTraitRef<'tcx>>>, EvaluationResult>; +pub type EvaluationCache<'tcx> = Cache< + (ty::ParamEnvAnd<'tcx, ty::ConstnessAnd<ty::PolyTraitRef<'tcx>>>, ty::ImplPolarity), + EvaluationResult, +>; /// The selection process begins by considering all impls, where /// clauses, and so forth that might resolve an obligation. Sometimes @@ -101,7 +103,7 @@ /// `false` if there are no *further* obligations. has_nested: bool, }, - ParamCandidate(ty::ConstnessAnd<ty::PolyTraitRef<'tcx>>), + ParamCandidate((ty::ConstnessAnd<ty::PolyTraitRef<'tcx>>, ty::ImplPolarity)), ImplCandidate(DefId), AutoImplCandidate(DefId),
diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs new file mode 100644 index 0000000..3490c68 --- /dev/null +++ b/compiler/rustc_middle/src/traits/util.rs
@@ -0,0 +1,49 @@ +use rustc_data_structures::stable_set::FxHashSet; + +use crate::ty::{PolyTraitRef, TyCtxt}; + +/// Given a PolyTraitRef, get the PolyTraitRefs of the trait's (transitive) supertraits. +/// +/// A simplfied version of the same function at `rustc_infer::traits::util::supertraits`. +pub fn supertraits<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: PolyTraitRef<'tcx>, +) -> impl Iterator<Item = PolyTraitRef<'tcx>> { + Elaborator { tcx, visited: FxHashSet::from_iter([trait_ref]), stack: vec![trait_ref] } +} + +struct Elaborator<'tcx> { + tcx: TyCtxt<'tcx>, + visited: FxHashSet<PolyTraitRef<'tcx>>, + stack: Vec<PolyTraitRef<'tcx>>, +} + +impl<'tcx> Elaborator<'tcx> { + fn elaborate(&mut self, trait_ref: PolyTraitRef<'tcx>) { + let supertrait_refs = self + .tcx + .super_predicates_of(trait_ref.def_id()) + .predicates + .into_iter() + .flat_map(|(pred, _)| { + pred.subst_supertrait(self.tcx, &trait_ref).to_opt_poly_trait_ref() + }) + .map(|t| t.value) + .filter(|supertrait_ref| self.visited.insert(*supertrait_ref)); + + self.stack.extend(supertrait_refs); + } +} + +impl<'tcx> Iterator for Elaborator<'tcx> { + type Item = PolyTraitRef<'tcx>; + + fn next(&mut self) -> Option<PolyTraitRef<'tcx>> { + if let Some(trait_ref) = self.stack.pop() { + self.elaborate(trait_ref); + Some(trait_ref) + } else { + None + } + } +}
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 434008e..3f2b987 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -417,17 +417,17 @@ macro_rules! impl_arena_allocatable_decoder { ([]$args:tt) => {}; ([decode $(, $attrs:ident)*] - [[$name:ident: $ty:ty], $tcx:lifetime]) => { - impl<$tcx, D: TyDecoder<$tcx>> RefDecodable<$tcx, D> for $ty { + [$name:ident: $ty:ty]) => { + impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty { #[inline] - fn decode(decoder: &mut D) -> Result<&$tcx Self, D::Error> { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { decode_arena_allocable(decoder) } } - impl<$tcx, D: TyDecoder<$tcx>> RefDecodable<$tcx, D> for [$ty] { + impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [$ty] { #[inline] - fn decode(decoder: &mut D) -> Result<&$tcx Self, D::Error> { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { decode_arena_allocable_slice(decoder) } } @@ -438,15 +438,15 @@ } macro_rules! impl_arena_allocatable_decoders { - ([$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => { + ([$($a:tt $name:ident: $ty:ty,)*]) => { $( - impl_arena_allocatable_decoder!($a [[$name: $ty], $tcx]); + impl_arena_allocatable_decoder!($a [$name: $ty]); )* } } -rustc_hir::arena_types!(impl_arena_allocatable_decoders, 'tcx); -arena_types!(impl_arena_allocatable_decoders, 'tcx); +rustc_hir::arena_types!(impl_arena_allocatable_decoders); +arena_types!(impl_arena_allocatable_decoders); #[macro_export] macro_rules! implement_ty_decoder {
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 869b2ab..27e22cc 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -1,7 +1,9 @@ use crate::mir::interpret::ConstValue; use crate::mir::interpret::{LitToConstInput, Scalar}; -use crate::ty::{self, Ty, TyCtxt}; -use crate::ty::{ParamEnv, ParamEnvAnd}; +use crate::ty::{ + self, InlineConstSubsts, InlineConstSubstsParts, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, + TyCtxt, TypeFoldable, +}; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -54,6 +56,24 @@ let ty = tcx.type_of(def.def_id_for_type_of()); + match Self::try_eval_lit_or_param(tcx, ty, expr) { + Some(v) => v, + None => tcx.mk_const(ty::Const { + val: ty::ConstKind::Unevaluated(ty::Unevaluated { + def: def.to_global(), + substs_: None, + promoted: None, + }), + ty, + }), + } + } + + fn try_eval_lit_or_param( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Option<&'tcx Self> { let lit_input = match expr.kind { hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind { @@ -69,7 +89,7 @@ // If an error occurred, ignore that it's a literal and leave reporting the error up to // mir. if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) { - return c; + return Some(c); } else { tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const"); } @@ -85,7 +105,7 @@ }; use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath}; - let val = match expr.kind { + match expr.kind { ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => { // Find the name and index of the const parameter by indexing the generics of // the parent item and construct a `ParamConst`. @@ -95,16 +115,53 @@ let generics = tcx.generics_of(item_def_id.to_def_id()); let index = generics.param_def_id_to_index[&def_id]; let name = tcx.hir().name(hir_id); - ty::ConstKind::Param(ty::ParamConst::new(index, name)) + Some(tcx.mk_const(ty::Const { + val: ty::ConstKind::Param(ty::ParamConst::new(index, name)), + ty, + })) } - _ => ty::ConstKind::Unevaluated(ty::Unevaluated { - def: def.to_global(), - substs_: None, - promoted: None, - }), + _ => None, + } + } + + pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self { + debug!("Const::from_inline_const(def_id={:?})", def_id); + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + let body_id = match tcx.hir().get(hir_id) { + hir::Node::AnonConst(ac) => ac.body, + _ => span_bug!( + tcx.def_span(def_id.to_def_id()), + "from_inline_const can only process anonymous constants" + ), }; - tcx.mk_const(ty::Const { val, ty }) + let expr = &tcx.hir().body(body_id).value; + + let ty = tcx.typeck(def_id).node_type(hir_id); + + let ret = match Self::try_eval_lit_or_param(tcx, ty, expr) { + Some(v) => v, + None => { + let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()); + let parent_substs = + tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id)); + let substs = + InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty }) + .substs; + tcx.mk_const(ty::Const { + val: ty::ConstKind::Unevaluated(ty::Unevaluated { + def: ty::WithOptConstParam::unknown(def_id).to_global(), + substs_: Some(substs), + promoted: None, + }), + ty, + }) + } + }; + debug_assert!(!ret.has_free_regions(tcx)); + ret } /// Interns the given value as a constant.
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 83d7c30..8240273 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1,7 +1,7 @@ //! Type context book-keeping. use crate::arena::Arena; -use crate::dep_graph::DepGraph; +use crate::dep_graph::{DepGraph, DepKind, DepKindStruct}; use crate::hir::place::Place as HirPlace; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; @@ -79,11 +79,6 @@ where Self: Sized; - /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation - /// session, if it still exists. This is used during incremental compilation to - /// turn a deserialized `DefPathHash` into its current `DefId`. - fn def_path_hash_to_def_id(&self, tcx: TyCtxt<'tcx>, def_path_hash: DefPathHash) -> DefId; - fn drop_serialized_data(&self, tcx: TyCtxt<'tcx>); fn serialize(&self, tcx: TyCtxt<'tcx>, encoder: &mut FileEncoder) -> FileEncodeResult; @@ -1016,6 +1011,7 @@ pub queries: &'tcx dyn query::QueryEngine<'tcx>, pub query_caches: query::QueryCaches<'tcx>, + query_kinds: &'tcx [DepKindStruct], // Internal caches for metadata decoding. No need to track deps on this. pub ty_rcache: Lock<FxHashMap<ty::CReaderCacheKey, Ty<'tcx>>>, @@ -1149,6 +1145,7 @@ dep_graph: DepGraph, on_disk_cache: Option<&'tcx dyn OnDiskCache<'tcx>>, queries: &'tcx dyn query::QueryEngine<'tcx>, + query_kinds: &'tcx [DepKindStruct], crate_name: &str, output_filenames: OutputFilenames, ) -> GlobalCtxt<'tcx> { @@ -1175,6 +1172,7 @@ on_disk_cache, queries, query_caches: query::QueryCaches::default(), + query_kinds, ty_rcache: Default::default(), pred_rcache: Default::default(), selection_cache: Default::default(), @@ -1188,6 +1186,10 @@ } } + crate fn query_kind(self, k: DepKind) -> &'tcx DepKindStruct { + &self.query_kinds[k as usize] + } + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used. #[track_caller] pub fn ty_error(self) -> Ty<'tcx> { @@ -1301,6 +1303,27 @@ } } + /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation + /// session, if it still exists. This is used during incremental compilation to + /// turn a deserialized `DefPathHash` into its current `DefId`. + pub fn def_path_hash_to_def_id(self, hash: DefPathHash) -> DefId { + debug!("def_path_hash_to_def_id({:?})", hash); + + let stable_crate_id = hash.stable_crate_id(); + + // 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.untracked_resolutions.definitions.local_def_path_hash_to_def_id(hash).to_def_id() + } else { + // If this is a DefPathHash from an upstream crate, let the CrateStore map + // it to a DefId. + let cstore = &self.untracked_resolutions.cstore; + let cnum = cstore.stable_crate_id_to_crate_num(stable_crate_id); + cstore.def_path_hash_to_def_id(cnum, hash) + } + } + pub fn def_path_debug_str(self, def_id: DefId) -> String { // We are explicitly not going through queries here in order to get // crate name and stable crate id since this code is called from debug!() @@ -1337,20 +1360,15 @@ #[inline(always)] pub fn create_stable_hashing_context(self) -> StableHashingContext<'tcx> { - let krate = self.gcx.untracked_crate; let resolutions = &self.gcx.untracked_resolutions; - - StableHashingContext::new(self.sess, krate, &resolutions.definitions, &*resolutions.cstore) + StableHashingContext::new(self.sess, &resolutions.definitions, &*resolutions.cstore) } #[inline(always)] pub fn create_no_span_stable_hashing_context(self) -> StableHashingContext<'tcx> { - let krate = self.gcx.untracked_crate; let resolutions = &self.gcx.untracked_resolutions; - StableHashingContext::ignore_spans( self.sess, - krate, &resolutions.definitions, &*resolutions.cstore, ) @@ -2823,7 +2841,8 @@ } pub fn provide(providers: &mut ty::query::Providers) { - providers.in_scope_traits_map = |tcx, id| tcx.hir_crate(()).trait_map.get(&id); + providers.in_scope_traits_map = + |tcx, id| tcx.hir_crate(()).owners[id].as_ref().map(|owner_info| &owner_info.trait_map); providers.resolutions = |tcx, ()| &tcx.untracked_resolutions; providers.module_exports = |tcx, id| tcx.resolutions(()).export_map.get(&id).map(|v| &v[..]); providers.crate_name = |tcx, id| {
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 092eae0..1b32c8a 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -221,9 +221,7 @@ ) -> bool { let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); - let param = if let Some(param) = param { - param - } else { + let Some(param) = param else { return false; };
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 08b4d3a..b14a698 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs
@@ -34,6 +34,7 @@ pub enum TypeError<'tcx> { Mismatch, ConstnessMismatch(ExpectedFound<ty::BoundConstness>), + PolarityMismatch(ExpectedFound<ty::ImplPolarity>), UnsafetyMismatch(ExpectedFound<hir::Unsafety>), AbiMismatch(ExpectedFound<abi::Abi>), Mutability, @@ -41,6 +42,7 @@ TupleSize(ExpectedFound<usize>), FixedArraySize(ExpectedFound<u64>), ArgCount, + FieldMisMatch(Symbol, Symbol), RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>), @@ -104,6 +106,9 @@ ConstnessMismatch(values) => { write!(f, "expected {} bound, found {} bound", values.expected, values.found) } + PolarityMismatch(values) => { + write!(f, "expected {} polarity, found {} polarity", values.expected, values.found) + } UnsafetyMismatch(values) => { write!(f, "expected {} fn, found {} fn", values.expected, values.found) } @@ -130,6 +135,7 @@ pluralize!(values.found) ), ArgCount => write!(f, "incorrect number of function parameters"), + FieldMisMatch(adt, field) => write!(f, "field type mismatch: {}.{}", adt, field), RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"), RegionsInsufficientlyPolymorphic(br, _) => write!( f, @@ -212,15 +218,15 @@ use self::TypeError::*; match self { CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | ConstnessMismatch(_) - | Mismatch | AbiMismatch(_) | FixedArraySize(_) | ArgumentSorts(..) | Sorts(_) - | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_) | TargetFeatureCast(_) => { - false - } + | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) + | ArgumentSorts(..) | Sorts(_) | IntMismatch(_) | FloatMismatch(_) + | VariadicMismatch(_) | TargetFeatureCast(_) => false, Mutability | ArgumentMutability(_) | TupleSize(_) | ArgCount + | FieldMisMatch(..) | RegionsDoesNotOutlive(..) | RegionsInsufficientlyPolymorphic(..) | RegionsOverlyPolymorphic(..) @@ -769,11 +775,16 @@ ) -> bool { let assoc = self.associated_item(proj_ty.item_def_id); if let ty::Opaque(def_id, _) = *proj_ty.self_ty().kind() { - let opaque_local_def_id = def_id.expect_local(); - let opaque_hir_id = self.hir().local_def_id_to_hir_id(opaque_local_def_id); - let opaque_hir_ty = match &self.hir().expect_item(opaque_hir_id).kind { - hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty, - _ => bug!("The HirId comes from a `ty::Opaque`"), + let opaque_local_def_id = def_id.as_local(); + let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id { + let hir = self.hir(); + let opaque_hir_id = hir.local_def_id_to_hir_id(opaque_local_def_id); + match &hir.expect_item(opaque_hir_id).kind { + hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty, + _ => bug!("The HirId comes from a `ty::Opaque`"), + } + } else { + return false; }; let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(self);
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 0f89581..f53f187 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -3,7 +3,6 @@ use crate::ty::subst::{Subst, SubstsRef}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; -use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -13,14 +12,8 @@ #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub enum GenericParamDefKind { Lifetime, - Type { - has_default: bool, - object_lifetime_default: ObjectLifetimeDefault, - synthetic: Option<hir::SyntheticTyParamKind>, - }, - Const { - has_default: bool, - }, + Type { has_default: bool, object_lifetime_default: ObjectLifetimeDefault, synthetic: bool }, + Const { has_default: bool }, } impl GenericParamDefKind { @@ -202,15 +195,7 @@ /// Returns `true` if `params` has `impl Trait`. pub fn has_impl_trait(&'tcx self) -> bool { self.params.iter().any(|param| { - matches!( - param.kind, - ty::GenericParamDefKind::Type { - synthetic: Some( - hir::SyntheticTyParamKind::ImplTrait | hir::SyntheticTyParamKind::FromAttr, - ), - .. - } - ) + matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. }) }) } }
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index d0c7379..b87e23a 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -755,17 +755,14 @@ } // Extract the number of elements from the layout of the array field: - let len = if let Ok(TyAndLayout { + let Ok(TyAndLayout { layout: Layout { fields: FieldsShape::Array { count, .. }, .. }, .. - }) = self.layout_of(f0_ty) - { - count - } else { + }) = self.layout_of(f0_ty) else { return Err(LayoutError::Unknown(ty)); }; - (*e_ty, *len, true) + (*e_ty, *count, true) } else { // First ADT field is not an array: (f0_ty, def.non_enum_variant().fields.len() as _, false) @@ -787,9 +784,7 @@ // Compute the ABI of the element type: let e_ly = self.layout_of(e_ty)?; - let e_abi = if let Abi::Scalar(scalar) = e_ly.abi { - scalar - } else { + let Abi::Scalar(e_abi) = e_ly.abi else { // This error isn't caught in typeck, e.g., if // the element type of the vector is generic. tcx.sess.fatal(&format!( @@ -3065,9 +3060,10 @@ // LLVM's definition of `noalias` is based solely on memory // dependencies rather than pointer equality // - // Due to miscompiles in LLVM < 12, we apply a separate NoAliasMutRef attribute - // for UniqueBorrowed arguments, so that the codegen backend can decide - // whether or not to actually emit the attribute. + // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute + // for UniqueBorrowed arguments, so that the codegen backend can decide whether + // or not to actually emit the attribute. It can also be controlled with the + // `-Zmutable-noalias` debugging option. let no_alias = match kind { PointerKind::Shared | PointerKind::UniqueBorrowed => false, PointerKind::UniqueOwned => true,
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 20d07bd..673733f 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -74,9 +74,10 @@ Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, BoundVariableKind, CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBoundRegion, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig, - GeneratorSubsts, GeneratorSubstsParts, ParamConst, ParamTy, PolyExistentialProjection, - PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, ProjectionTy, Region, RegionKind, - RegionVid, TraitRef, TyKind, TypeAndMut, UpvarSubsts, VarianceDiagInfo, VarianceDiagMutKind, + GeneratorSubsts, GeneratorSubstsParts, InlineConstSubsts, InlineConstSubstsParts, ParamConst, + ParamTy, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, PolyGenSig, + PolyTraitRef, ProjectionTy, Region, RegionKind, RegionVid, TraitRef, TyKind, TypeAndMut, + UpvarSubsts, VarianceDiagInfo, VarianceDiagMutKind, }; pub use self::trait_def::TraitDef; @@ -164,7 +165,18 @@ pub predicates: Vec<Predicate<'tcx>>, } -#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + TyEncodable, + TyDecodable, + HashStable, + Debug, + TypeFoldable +)] pub enum ImplPolarity { /// `impl Trait for Type` Positive, @@ -177,6 +189,27 @@ Reservation, } +impl ImplPolarity { + /// Flips polarity by turning `Positive` into `Negative` and `Negative` into `Positive`. + pub fn flip(&self) -> Option<ImplPolarity> { + match self { + ImplPolarity::Positive => Some(ImplPolarity::Negative), + ImplPolarity::Negative => Some(ImplPolarity::Positive), + ImplPolarity::Reservation => None, + } + } +} + +impl fmt::Display for ImplPolarity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Positive => f.write_str("positive"), + Self::Negative => f.write_str("negative"), + Self::Reservation => f.write_str("reservation"), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)] pub enum Visibility { /// Visible everywhere (including in other crates). @@ -300,6 +333,10 @@ Visibility::Invisible => false, } } + + pub fn is_public(self) -> bool { + matches!(self, Visibility::Public) + } } /// The crate variances map is computed during typeck and contains the @@ -459,6 +496,29 @@ pub fn kind(self) -> Binder<'tcx, PredicateKind<'tcx>> { self.inner.kind } + + /// Flips the polarity of a Predicate. + /// + /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. + pub fn flip_polarity(&self, tcx: TyCtxt<'tcx>) -> Option<Predicate<'tcx>> { + let kind = self + .inner + .kind + .map_bound(|kind| match kind { + PredicateKind::Trait(TraitPredicate { trait_ref, constness, polarity }) => { + Some(PredicateKind::Trait(TraitPredicate { + trait_ref, + constness, + polarity: polarity.flip()?, + })) + } + + _ => None, + }) + .transpose()?; + + Some(tcx.mk_predicate(kind)) + } } impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> { @@ -654,6 +714,8 @@ pub trait_ref: TraitRef<'tcx>, pub constness: BoundConstness, + + pub polarity: ImplPolarity, } pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; @@ -788,7 +850,11 @@ fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { self.value .map_bound(|trait_ref| { - PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: self.constness }) + PredicateKind::Trait(ty::TraitPredicate { + trait_ref, + constness: self.constness, + polarity: ty::ImplPolarity::Positive, + }) }) .to_predicate(tcx) } @@ -1866,7 +1932,8 @@ | DefKind::Static | DefKind::AssocConst | DefKind::Ctor(..) - | DefKind::AnonConst => self.mir_for_ctfe_opt_const_arg(def), + | DefKind::AnonConst + | DefKind::InlineConst => self.mir_for_ctfe_opt_const_arg(def), // If the caller wants `mir_for_ctfe` of a function they should not be using // `instance_mir`, so we'll assume const fn also wants the optimized version. _ => {
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 2610a76..175295b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -350,18 +350,26 @@ match self.tcx().extern_crate(def_id) { Some(&ExternCrate { src, dependency_of, span, .. }) => match (src, dependency_of) { (ExternCrateSource::Extern(def_id), LOCAL_CRATE) => { - debug!("try_print_visible_def_path: def_id={:?}", def_id); - return Ok(( - if !span.is_dummy() { - self.print_def_path(def_id, &[])? - } else { - self.path_crate(cnum)? - }, - true, - )); + // NOTE(eddyb) the only reason `span` might be dummy, + // that we're aware of, is that it's the `std`/`core` + // `extern crate` injected by default. + // FIXME(eddyb) find something better to key this on, + // or avoid ending up with `ExternCrateSource::Extern`, + // for the injected `std`/`core`. + if span.is_dummy() { + return Ok((self.path_crate(cnum)?, true)); + } + + // Disable `try_print_trimmed_def_path` behavior within + // the `print_def_path` call, to avoid infinite recursion + // in cases where the `extern crate foo` has non-trivial + // parents, e.g. it's nested in `impl foo::Trait for Bar` + // (see also issues #55779 and #87932). + self = with_no_visible_paths(|| self.print_def_path(def_id, &[]))?; + + return Ok((self, true)); } (ExternCrateSource::Path, LOCAL_CRATE) => { - debug!("try_print_visible_def_path: def_id={:?}", def_id); return Ok((self.path_crate(cnum)?, true)); } _ => {} @@ -635,37 +643,8 @@ } return Ok(self); } - // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, - // by looking up the projections associated with the def_id. - let bounds = self.tcx().explicit_item_bounds(def_id); - let mut first = true; - let mut is_sized = false; - p!("impl"); - for (predicate, _) in bounds { - let predicate = predicate.subst(self.tcx(), substs); - let bound_predicate = predicate.kind(); - if let ty::PredicateKind::Trait(pred) = bound_predicate.skip_binder() { - let trait_ref = bound_predicate.rebind(pred.trait_ref); - // Don't print +Sized, but rather +?Sized if absent. - if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() { - is_sized = true; - continue; - } - - p!( - write("{}", if first { " " } else { "+" }), - print(trait_ref.print_only_trait_path()) - ); - first = false; - } - } - if !is_sized { - p!(write("{}?Sized", if first { " " } else { "+" })); - } else if first { - p!(" Sized"); - } - Ok(self) + self.pretty_print_opaque_impl_type(def_id, substs) }); } ty::Str => p!("str"), @@ -736,6 +715,7 @@ p!(print_def_path(did, substs)); if !substs.as_closure().is_valid() { p!(" closure_substs=(unavailable)"); + p!(write(" substs={:?}", substs)); } else { p!(" closure_kind_ty=", print(substs.as_closure().kind_ty())); p!( @@ -773,6 +753,225 @@ Ok(self) } + fn pretty_print_opaque_impl_type( + mut self, + def_id: DefId, + substs: &'tcx ty::List<ty::GenericArg<'tcx>>, + ) -> Result<Self::Type, Self::Error> { + define_scoped_cx!(self); + + // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, + // by looking up the projections associated with the def_id. + let bounds = self.tcx().explicit_item_bounds(def_id); + + let mut traits = BTreeMap::new(); + let mut fn_traits = BTreeMap::new(); + let mut is_sized = false; + + for (predicate, _) in bounds { + let predicate = predicate.subst(self.tcx(), substs); + let bound_predicate = predicate.kind(); + + match bound_predicate.skip_binder() { + ty::PredicateKind::Trait(pred) => { + let trait_ref = bound_predicate.rebind(pred.trait_ref); + + // Don't print + Sized, but rather + ?Sized if absent. + if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() { + is_sized = true; + continue; + } + + self.insert_trait_and_projection(trait_ref, None, &mut traits, &mut fn_traits); + } + ty::PredicateKind::Projection(pred) => { + let proj_ref = bound_predicate.rebind(pred); + let trait_ref = proj_ref.required_poly_trait_ref(self.tcx()); + + // Projection type entry -- the def-id for naming, and the ty. + let proj_ty = (proj_ref.projection_def_id(), proj_ref.ty()); + + self.insert_trait_and_projection( + trait_ref, + Some(proj_ty), + &mut traits, + &mut fn_traits, + ); + } + _ => {} + } + } + + let mut first = true; + // Insert parenthesis around (Fn(A, B) -> C) if the opaque ty has more than one other trait + let paren_needed = fn_traits.len() > 1 || traits.len() > 0 || !is_sized; + + p!("impl"); + + for (fn_once_trait_ref, entry) in fn_traits { + // Get the (single) generic ty (the args) of this FnOnce trait ref. + let generics = self.generic_args_to_print( + self.tcx().generics_of(fn_once_trait_ref.def_id()), + fn_once_trait_ref.skip_binder().substs, + ); + + match (entry.return_ty, generics[0].expect_ty()) { + // We can only print `impl Fn() -> ()` if we have a tuple of args and we recorded + // a return type. + (Some(return_ty), arg_tys) if matches!(arg_tys.kind(), ty::Tuple(_)) => { + let name = if entry.fn_trait_ref.is_some() { + "Fn" + } else if entry.fn_mut_trait_ref.is_some() { + "FnMut" + } else { + "FnOnce" + }; + + p!( + write("{}", if first { " " } else { " + " }), + write("{}{}(", if paren_needed { "(" } else { "" }, name) + ); + + for (idx, ty) in arg_tys.tuple_fields().enumerate() { + if idx > 0 { + p!(", "); + } + p!(print(ty)); + } + + p!(")"); + if !return_ty.skip_binder().is_unit() { + p!("-> ", print(return_ty)); + } + p!(write("{}", if paren_needed { ")" } else { "" })); + + first = false; + } + // If we got here, we can't print as a `impl Fn(A, B) -> C`. Just record the + // trait_refs we collected in the OpaqueFnEntry as normal trait refs. + _ => { + if entry.has_fn_once { + traits.entry(fn_once_trait_ref).or_default().extend( + // Group the return ty with its def id, if we had one. + entry + .return_ty + .map(|ty| (self.tcx().lang_items().fn_once_output().unwrap(), ty)), + ); + } + if let Some(trait_ref) = entry.fn_mut_trait_ref { + traits.entry(trait_ref).or_default(); + } + if let Some(trait_ref) = entry.fn_trait_ref { + traits.entry(trait_ref).or_default(); + } + } + } + } + + // Print the rest of the trait types (that aren't Fn* family of traits) + for (trait_ref, assoc_items) in traits { + p!( + write("{}", if first { " " } else { " + " }), + print(trait_ref.skip_binder().print_only_trait_name()) + ); + + let generics = self.generic_args_to_print( + self.tcx().generics_of(trait_ref.def_id()), + trait_ref.skip_binder().substs, + ); + + if !generics.is_empty() || !assoc_items.is_empty() { + p!("<"); + let mut first = true; + + for ty in generics { + if !first { + p!(", "); + } + p!(print(trait_ref.rebind(*ty))); + first = false; + } + + for (assoc_item_def_id, ty) in assoc_items { + if !first { + p!(", "); + } + p!(write("{} = ", self.tcx().associated_item(assoc_item_def_id).ident)); + + // Skip printing `<[generator@] as Generator<_>>::Return` from async blocks + match ty.skip_binder().kind() { + ty::Projection(ty::ProjectionTy { item_def_id, .. }) + if Some(*item_def_id) == self.tcx().lang_items().generator_return() => + { + p!("[async output]") + } + _ => { + p!(print(ty)) + } + } + + first = false; + } + + p!(">"); + } + + first = false; + } + + if !is_sized { + p!(write("{}?Sized", if first { " " } else { " + " })); + } else if first { + p!(" Sized"); + } + + Ok(self) + } + + /// Insert the trait ref and optionally a projection type associated with it into either the + /// traits map or fn_traits map, depending on if the trait is in the Fn* family of traits. + fn insert_trait_and_projection( + &mut self, + trait_ref: ty::PolyTraitRef<'tcx>, + proj_ty: Option<(DefId, ty::Binder<'tcx, Ty<'tcx>>)>, + traits: &mut BTreeMap<ty::PolyTraitRef<'tcx>, BTreeMap<DefId, ty::Binder<'tcx, Ty<'tcx>>>>, + fn_traits: &mut BTreeMap<ty::PolyTraitRef<'tcx>, OpaqueFnEntry<'tcx>>, + ) { + let trait_def_id = trait_ref.def_id(); + + // If our trait_ref is FnOnce or any of its children, project it onto the parent FnOnce + // super-trait ref and record it there. + if let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait() { + // If we have a FnOnce, then insert it into + if trait_def_id == fn_once_trait { + let entry = fn_traits.entry(trait_ref).or_default(); + // Optionally insert the return_ty as well. + if let Some((_, ty)) = proj_ty { + entry.return_ty = Some(ty); + } + entry.has_fn_once = true; + return; + } else if Some(trait_def_id) == self.tcx().lang_items().fn_mut_trait() { + let super_trait_ref = crate::traits::util::supertraits(self.tcx(), trait_ref) + .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait) + .unwrap(); + + fn_traits.entry(super_trait_ref).or_default().fn_mut_trait_ref = Some(trait_ref); + return; + } else if Some(trait_def_id) == self.tcx().lang_items().fn_trait() { + let super_trait_ref = crate::traits::util::supertraits(self.tcx(), trait_ref) + .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait) + .unwrap(); + + fn_traits.entry(super_trait_ref).or_default().fn_trait_ref = Some(trait_ref); + return; + } + } + + // Otherwise, just group our traits and projection types. + traits.entry(trait_ref).or_default().extend(proj_ty); + } + fn pretty_print_bound_var( &mut self, debruijn: ty::DebruijnIndex, @@ -2395,7 +2594,7 @@ // Iterate external crate defs but be mindful about visibility while let Some(def) = queue.pop() { for child in tcx.item_children(def).iter() { - if child.vis != ty::Visibility::Public { + if !child.vis.is_public() { continue; } @@ -2500,3 +2699,12 @@ pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { trimmed_def_paths, ..*providers }; } + +#[derive(Default)] +pub struct OpaqueFnEntry<'tcx> { + // The trait ref is already stored as a key, so just track if we have it as a real predicate + has_fn_once: bool, + fn_mut_trait_ref: Option<ty::PolyTraitRef<'tcx>>, + fn_trait_ref: Option<ty::PolyTraitRef<'tcx>>, + return_ty: Option<ty::Binder<'tcx, Ty<'tcx>>>, +}
diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index b1bc073..34f8062 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs
@@ -102,6 +102,10 @@ } } +/// Helper for `TyCtxtEnsure` to avoid a closure. +#[inline(always)] +fn noop<T>(_: &T) {} + macro_rules! query_helper_param_ty { (DefId) => { impl IntoQueryParam<DefId> }; ($K:ty) => { $K }; @@ -119,6 +123,39 @@ }; } +macro_rules! separate_provide_extern_decl { + ([][$name:ident]) => { + () + }; + ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { + for<'tcx> fn( + TyCtxt<'tcx>, + query_keys::$name<'tcx>, + ) -> query_values::$name<'tcx> + }; + ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { + separate_provide_extern_decl!([$($modifiers)*][$($args)*]) + }; +} + +macro_rules! separate_provide_extern_default { + ([][$name:ident]) => { + () + }; + ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { + |_, key| bug!( + "`tcx.{}({:?})` unsupported by its crate; \ + perhaps the `{}` query was never assigned a provider function", + stringify!($name), + key, + stringify!($name), + ) + }; + ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { + separate_provide_extern_default!([$($modifiers)*][$($args)*]) + }; +} + macro_rules! define_callbacks { (<$tcx:tt> $($(#[$attr:meta])* @@ -165,7 +202,7 @@ #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) { let key = key.into_query_param(); - let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, |_| {}); + let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, noop); let lookup = match cached { Ok(()) => return, @@ -192,9 +229,7 @@ pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> query_stored::$name<$tcx> { let key = key.into_query_param(); - let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, |value| { - value.clone() - }); + let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, Clone::clone); let lookup = match cached { Ok(value) => return value, @@ -212,6 +247,10 @@ ) -> query_values::$name<'tcx>,)* } + pub struct ExternProviders { + $(pub $name: separate_provide_extern_decl!([$($modifiers)*][$name]),)* + } + impl Default for Providers { fn default() -> Self { Providers { @@ -226,11 +265,24 @@ } } + impl Default for ExternProviders { + fn default() -> Self { + ExternProviders { + $($name: separate_provide_extern_default!([$($modifiers)*][$name]),)* + } + } + } + impl Copy for Providers {} impl Clone for Providers { fn clone(&self) -> Self { *self } } + impl Copy for ExternProviders {} + impl Clone for ExternProviders { + fn clone(&self) -> Self { *self } + } + pub trait QueryEngine<'tcx>: rustc_data_structures::sync::Sync { fn as_any(&'tcx self) -> &'tcx dyn std::any::Any;
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 2c78653..c7d8bec 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -187,8 +187,12 @@ }) .enumerate() .map(|(i, r)| match r { - Err(TypeError::Sorts(exp_found)) => Err(TypeError::ArgumentSorts(exp_found, i)), - Err(TypeError::Mutability) => Err(TypeError::ArgumentMutability(i)), + Err(TypeError::Sorts(exp_found) | TypeError::ArgumentSorts(exp_found, _)) => { + Err(TypeError::ArgumentSorts(exp_found, i)) + } + Err(TypeError::Mutability | TypeError::ArgumentMutability(_)) => { + Err(TypeError::ArgumentMutability(i)) + } r => r, }); Ok(ty::FnSig { @@ -797,6 +801,20 @@ } } +impl<'tcx> Relate<'tcx> for ty::ImplPolarity { + fn relate<R: TypeRelation<'tcx>>( + relation: &mut R, + a: ty::ImplPolarity, + b: ty::ImplPolarity, + ) -> RelateResult<'tcx, ty::ImplPolarity> { + if a != b { + Err(TypeError::PolarityMismatch(expected_found(relation, a, b))) + } else { + Ok(a) + } + } +} + impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> { fn relate<R: TypeRelation<'tcx>>( relation: &mut R, @@ -806,6 +824,7 @@ Ok(ty::TraitPredicate { trait_ref: relation.relate(a.trait_ref, b.trait_ref)?, constness: relation.relate(a.constness, b.constness)?, + polarity: relation.relate(a.polarity, b.polarity)?, }) } }
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 8f343ba9..0f8e808 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -157,7 +157,7 @@ if let ty::BoundConstness::ConstIfConst = self.constness { write!(f, "~const ")?; } - write!(f, "TraitPredicate({:?})", self.trait_ref) + write!(f, "TraitPredicate({:?}, polarity:{:?})", self.trait_ref, self.polarity) } } @@ -365,8 +365,11 @@ impl<'a, 'tcx> Lift<'tcx> for ty::TraitPredicate<'a> { type Lifted = ty::TraitPredicate<'tcx>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<ty::TraitPredicate<'tcx>> { - tcx.lift(self.trait_ref) - .map(|trait_ref| ty::TraitPredicate { trait_ref, constness: self.constness }) + tcx.lift(self.trait_ref).map(|trait_ref| ty::TraitPredicate { + trait_ref, + constness: self.constness, + polarity: self.polarity, + }) } } @@ -591,6 +594,7 @@ Some(match self { Mismatch => Mismatch, ConstnessMismatch(x) => ConstnessMismatch(x), + PolarityMismatch(x) => PolarityMismatch(x), UnsafetyMismatch(x) => UnsafetyMismatch(x), AbiMismatch(x) => AbiMismatch(x), Mutability => Mutability, @@ -598,6 +602,7 @@ TupleSize(x) => TupleSize(x), FixedArraySize(x) => FixedArraySize(x), ArgCount => ArgCount, + FieldMisMatch(x, y) => FieldMisMatch(x, y), RegionsDoesNotOutlive(a, b) => { return tcx.lift((a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b)); }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index d3094b3..c2b32cd 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -704,6 +704,66 @@ } } +/// An inline const is modeled like +/// +/// const InlineConst<'l0...'li, T0...Tj, R>: R; +/// +/// where: +/// +/// - 'l0...'li and T0...Tj are the generic parameters +/// inherited from the item that defined the inline const, +/// - R represents the type of the constant. +/// +/// When the inline const is instantiated, `R` is substituted as the actual inferred +/// type of the constant. The reason that `R` is represented as an extra type parameter +/// is the same reason that [`ClosureSubsts`] have `CS` and `U` as type parameters: +/// inline const can reference lifetimes that are internal to the creating function. +#[derive(Copy, Clone, Debug, TypeFoldable)] +pub struct InlineConstSubsts<'tcx> { + /// Generic parameters from the enclosing item, + /// concatenated with the inferred type of the constant. + pub substs: SubstsRef<'tcx>, +} + +/// Struct returned by `split()`. +pub struct InlineConstSubstsParts<'tcx, T> { + pub parent_substs: &'tcx [GenericArg<'tcx>], + pub ty: T, +} + +impl<'tcx> InlineConstSubsts<'tcx> { + /// Construct `InlineConstSubsts` from `InlineConstSubstsParts`. + pub fn new( + tcx: TyCtxt<'tcx>, + parts: InlineConstSubstsParts<'tcx, Ty<'tcx>>, + ) -> InlineConstSubsts<'tcx> { + InlineConstSubsts { + substs: tcx.mk_substs( + parts.parent_substs.iter().copied().chain(std::iter::once(parts.ty.into())), + ), + } + } + + /// Divides the inline const substs into their respective components. + /// The ordering assumed here must match that used by `InlineConstSubsts::new` above. + fn split(self) -> InlineConstSubstsParts<'tcx, GenericArg<'tcx>> { + match self.substs[..] { + [ref parent_substs @ .., ty] => InlineConstSubstsParts { parent_substs, ty }, + _ => bug!("inline const substs missing synthetics"), + } + } + + /// Returns the substitutions of the inline const's parent. + pub fn parent_substs(self) -> &'tcx [GenericArg<'tcx>] { + self.split().parent_substs + } + + /// Returns the type of this inline const. + pub fn ty(self) -> Ty<'tcx> { + self.split().ty.expect_ty() + } +} + #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub enum ExistentialPredicate<'tcx> { @@ -830,7 +890,7 @@ /// /// Trait references also appear in object types like `Foo<U>`, but in /// that case the `Self` parameter is absent from the substitutions. -#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub struct TraitRef<'tcx> { pub def_id: DefId, @@ -882,6 +942,7 @@ self.map_bound(|trait_ref| ty::TraitPredicate { trait_ref, constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, }) } } @@ -1744,10 +1805,13 @@ pub fn simd_size_and_type(&self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { match self.kind() { Adt(def, substs) => { + assert!(def.repr.simd(), "`simd_size_and_type` called on non-SIMD type"); let variant = def.non_enum_variant(); let f0_ty = variant.fields[0].ty(tcx, substs); match f0_ty.kind() { + // If the first field is an array, we assume it is the only field and its + // elements are the SIMD components. Array(f0_elem_ty, f0_len) => { // FIXME(repr_simd): https://github.com/rust-lang/rust/pull/78863#discussion_r522784112 // The way we evaluate the `N` in `[T; N]` here only works since we use @@ -1755,6 +1819,8 @@ // if we use it in generic code. See the `simd-array-trait` ui test. (f0_len.eval_usize(tcx, ParamEnv::empty()) as u64, f0_elem_ty) } + // Otherwise, the fields of this Adt are the SIMD components (and we assume they + // all have the same type). _ => (variant.fields.len() as u64, f0_ty), } } @@ -2006,7 +2072,9 @@ ) -> Option<Discr<'tcx>> { match self.kind() { TyKind::Adt(adt, _) if adt.variants.is_empty() => { - bug!("discriminant_for_variant called on zero variant enum"); + // This can actually happen during CTFE, see + // https://github.com/rust-lang/rust/issues/89765. + None } TyKind::Adt(adt, _) if adt.is_enum() => { Some(adt.discriminant_for_variant(tcx, variant_index)) @@ -2025,10 +2093,10 @@ ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), ty::Param(_) | ty::Projection(_) | ty::Opaque(..) | ty::Infer(ty::TyVar(_)) => { - let assoc_items = - tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); - let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; - tcx.mk_projection(discriminant_def_id, tcx.mk_substs([self.into()].iter())) + let assoc_items = tcx.associated_item_def_ids( + tcx.require_lang_item(hir::LangItem::DiscriminantKind, None), + ); + tcx.mk_projection(assoc_items[0], tcx.intern_substs(&[self.into()])) } ty::Bool @@ -2195,10 +2263,11 @@ /// a miscompilation or unsoundness. /// /// When in doubt, use `VarianceDiagInfo::default()` -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] pub enum VarianceDiagInfo<'tcx> { /// No additional information - this is the default. /// We will not add any additional information to error messages. + #[default] None, /// We switched our variance because a type occurs inside /// the generic argument of a mutable reference or pointer @@ -2233,9 +2302,3 @@ } } } - -impl<'tcx> Default for VarianceDiagInfo<'tcx> { - fn default() -> Self { - Self::None - } -}
diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 2438d1a..73a8e18 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs
@@ -3,7 +3,7 @@ use crate::mir; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::sty::{ClosureSubsts, GeneratorSubsts}; +use crate::ty::sty::{ClosureSubsts, GeneratorSubsts, InlineConstSubsts}; use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt}; use rustc_hir::def_id::DefId; @@ -204,6 +204,14 @@ GeneratorSubsts { substs: self } } + /// Interpret these substitutions as the substitutions of an inline const. + /// Inline const substitutions have a particular structure controlled by the + /// compiler that encodes information like the inferred type; + /// see `ty::InlineConstSubsts` struct for more comments. + pub fn as_inline_const(&'tcx self) -> InlineConstSubsts<'tcx> { + InlineConstSubsts { substs: self } + } + /// Creates an `InternalSubsts` that maps each generic parameter to itself. pub fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { Self::for_item(tcx, def_id, |param, _| tcx.mk_param_from_def(param))
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 2c88481..6e7acb2 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs
@@ -325,9 +325,9 @@ let ty = self.type_of(adt_did); let (did, constness) = self.find_map_relevant_impl(drop_trait, ty, |impl_did| { - if let Some(item) = self.associated_items(impl_did).in_definition_order().next() { + if let Some(item_id) = self.associated_item_def_ids(impl_did).first() { if validate(self, impl_did).is_ok() { - return Some((item.def_id, self.impl_constness(impl_did))); + return Some((*item_id, self.impl_constness(impl_did))); } } None @@ -423,6 +423,15 @@ matches!(self.def_kind(def_id), DefKind::Closure | DefKind::Generator) } + /// Returns `true` if `def_id` refers to a definition that does not have its own + /// type-checking context, i.e. closure, generator or inline const. + pub fn is_typeck_child(self, def_id: DefId) -> bool { + matches!( + self.def_kind(def_id), + DefKind::Closure | DefKind::Generator | DefKind::InlineConst + ) + } + /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). pub fn is_trait(self, def_id: DefId) -> bool { self.def_kind(def_id) == DefKind::Trait @@ -440,16 +449,19 @@ matches!(self.def_kind(def_id), DefKind::Ctor(..)) } - /// Given the def-ID of a fn or closure, returns the def-ID of - /// the innermost fn item that the closure is contained within. - /// This is a significant `DefId` because, when we do - /// type-checking, we type-check this fn item and all of its - /// (transitive) closures together. Therefore, when we fetch the + /// Given the `DefId`, returns the `DefId` of the innermost item that + /// has its own type-checking context or "inference enviornment". + /// + /// For example, a closure has its own `DefId`, but it is type-checked + /// with the containing item. Similarly, an inline const block has its + /// own `DefId` but it is type-checked together with the containing item. + /// + /// Therefore, when we fetch the /// `typeck` the closure, for example, we really wind up /// fetching the `typeck` the enclosing fn item. - pub fn closure_base_def_id(self, def_id: DefId) -> DefId { + pub fn typeck_root_def_id(self, def_id: DefId) -> DefId { let mut def_id = def_id; - while self.is_closure(def_id) { + while self.is_typeck_child(def_id) { def_id = self.parent(def_id).unwrap_or_else(|| { bug!("closure {:?} has no parent", def_id); });
diff --git a/compiler/rustc_middle/src/util/common.rs b/compiler/rustc_middle/src/util/common.rs index da857b0..0897704 100644 --- a/compiler/rustc_middle/src/util/common.rs +++ b/compiler/rustc_middle/src/util/common.rs
@@ -34,7 +34,7 @@ let rv = f(); let duration = start.elapsed(); let mut accu = accu.lock(); - *accu = *accu + duration; + *accu += duration; rv }
diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 05995dd..c6a34ec 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -221,15 +221,13 @@ let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); let closure_span = tcx.hir().span(closure_hir_id); - let (capture_index, capture) = if let Some(capture_details) = + let Some((capture_index, capture)) = find_capture_matching_projections( typeck_results, var_hir_id, closure_def_id, &from_builder.projection, - ) { - capture_details - } else { + ) else { if !enable_precise_capture(tcx, closure_span) { bug!( "No associated capture found for {:?}[{:#?}] even though \
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 4df073c..e3a05e0 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -1606,13 +1606,12 @@ // encounter a candidate where the test is not relevant; at // that point, we stop sorting. while let Some(candidate) = candidates.first_mut() { - if let Some(idx) = self.sort_candidate(&match_place.clone(), &test, candidate) { - let (candidate, rest) = candidates.split_first_mut().unwrap(); - target_candidates[idx].push(candidate); - candidates = rest; - } else { + let Some(idx) = self.sort_candidate(&match_place.clone(), &test, candidate) else { break; - } + }; + let (candidate, rest) = candidates.split_first_mut().unwrap(); + target_candidates[idx].push(candidate); + candidates = rest; } // at least the first candidate ought to be tested assert!(total_candidate_count > candidates.len()); @@ -1762,8 +1761,8 @@ ) -> BlockAnd<()> { let expr_span = expr.span; let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr, expr_span)); - let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false); let wildcard = Pat::wildcard_from_ty(pat.ty); + let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false); let mut otherwise_candidate = Candidate::new(expr_place_builder.clone(), &wildcard, false); let fake_borrow_temps = self.lower_match_tree( block,
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 4108ad5..cb94e75 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -966,59 +966,58 @@ DropKind::Value, ); - if let Some(arg) = arg_opt { - let pat = match tcx.hir().get(arg.pat.hir_id) { - Node::Pat(pat) | Node::Binding(pat) => pat, - node => bug!("pattern became {:?}", node), - }; - let pattern = pat_from_hir(tcx, self.param_env, self.typeck_results, pat); - let original_source_scope = self.source_scope; - let span = pattern.span; - self.set_correct_source_scope_for_arg(arg.hir_id, original_source_scope, span); - match *pattern.kind { - // Don't introduce extra copies for simple bindings - PatKind::Binding { - mutability, - var, - mode: BindingMode::ByValue, - subpattern: None, - .. - } => { - self.local_decls[local].mutability = mutability; - self.local_decls[local].source_info.scope = self.source_scope; - self.local_decls[local].local_info = if let Some(kind) = self_binding { - Some(Box::new(LocalInfo::User(ClearCrossCrate::Set( - BindingForm::ImplicitSelf(*kind), - )))) - } else { - let binding_mode = ty::BindingMode::BindByValue(mutability); - Some(Box::new(LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( - VarBindingForm { - binding_mode, - opt_ty_info, - opt_match_place: Some((Some(place), span)), - pat_span: span, - }, - ))))) - }; - self.var_indices.insert(var, LocalsForNode::One(local)); - } - _ => { - scope = self.declare_bindings( - scope, - expr.span, - &pattern, - matches::ArmHasGuard(false), - Some((Some(&place), span)), - ); - let place_builder = PlaceBuilder::from(local); - unpack!( - block = self.place_into_pattern(block, pattern, place_builder, false) - ); - } + let Some(arg) = arg_opt else { + continue; + }; + let pat = match tcx.hir().get(arg.pat.hir_id) { + Node::Pat(pat) | Node::Binding(pat) => pat, + node => bug!("pattern became {:?}", node), + }; + let pattern = pat_from_hir(tcx, self.param_env, self.typeck_results, pat); + let original_source_scope = self.source_scope; + let span = pattern.span; + self.set_correct_source_scope_for_arg(arg.hir_id, original_source_scope, span); + match *pattern.kind { + // Don't introduce extra copies for simple bindings + PatKind::Binding { + mutability, + var, + mode: BindingMode::ByValue, + subpattern: None, + .. + } => { + self.local_decls[local].mutability = mutability; + self.local_decls[local].source_info.scope = self.source_scope; + self.local_decls[local].local_info = if let Some(kind) = self_binding { + Some(Box::new(LocalInfo::User(ClearCrossCrate::Set( + BindingForm::ImplicitSelf(*kind), + )))) + } else { + let binding_mode = ty::BindingMode::BindByValue(mutability); + Some(Box::new(LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + VarBindingForm { + binding_mode, + opt_ty_info, + opt_match_place: Some((Some(place), span)), + pat_span: span, + }, + ))))) + }; + self.var_indices.insert(var, LocalsForNode::One(local)); } - self.source_scope = original_source_scope; + _ => { + scope = self.declare_bindings( + scope, + expr.span, + &pattern, + matches::ArmHasGuard(false), + Some((Some(&place), span)), + ); + let place_builder = PlaceBuilder::from(local); + unpack!(block = self.place_into_pattern(block, pattern, place_builder, false)); + } } + self.source_scope = original_source_scope; } // Enter the argument pattern bindings source scope, if it exists.
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index b74208e..8dadbf5 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -362,11 +362,7 @@ blocks: &IndexVec<DropIdx, Option<BasicBlock>>, ) { for (drop_idx, drop_data) in self.drops.iter_enumerated().rev() { - let block = if let Some(block) = blocks[drop_idx] { - block - } else { - continue; - }; + let Some(block) = blocks[drop_idx] else { continue }; match drop_data.0.kind { DropKind::Value => { let terminator = TerminatorKind::Drop {
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 0e82b18..7940bd1 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -256,23 +256,22 @@ } PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => { if self.inside_adt { - if let ty::Ref(_, ty, _) = ty.kind() { - match borrow_kind { - BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => { - if !ty.is_freeze(self.tcx.at(pat.span), self.param_env) { - self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField); - } - } - BorrowKind::Mut { .. } => { - self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField); - } - } - } else { + let ty::Ref(_, ty, _) = ty.kind() else { span_bug!( pat.span, "BindingMode::ByRef in pattern, but found non-reference type {}", ty ); + }; + match borrow_kind { + BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => { + if !ty.is_freeze(self.tcx.at(pat.span), self.param_env) { + self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField); + } + } + BorrowKind::Mut { .. } => { + self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField); + } } } visit::walk_pat(self, pat);
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 02023c4..b0f1e08 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs
@@ -6,6 +6,7 @@ #![feature(crate_visibility_modifier)] #![feature(bool_to_option)] #![feature(iter_zip)] +#![feature(let_else)] #![feature(once_cell)] #![feature(min_specialization)] #![recursion_limit = "256"]
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index ef8bd20..e4c2d2d 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs
@@ -2,7 +2,6 @@ NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, }; use rustc_hir::intravisit::FnKind; -use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::mir::{BasicBlock, Body, Operand, TerminatorKind}; use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt}; @@ -14,8 +13,8 @@ let def_id = body.source.def_id().expect_local(); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir().get(hir_id)) { - if let FnKind::Closure = fn_like_node.kind() { + if let Some(fn_kind) = tcx.hir().get(hir_id).fn_kind() { + if let FnKind::Closure = fn_kind { // closures can't recur, so they don't matter. return; }
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 17296a9..b4005cc 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -578,7 +578,7 @@ hir::ExprKind::ConstBlock(ref anon_const) => { let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); - let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id); + let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id); ExprKind::ConstBlock { value } }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index e28fd2c..d74c53f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -74,19 +74,16 @@ let (msg, sp) = match loc.source { hir::LocalSource::Normal => ("local binding", Some(loc.span)), - hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None), hir::LocalSource::AsyncFn => ("async fn binding", None), hir::LocalSource::AwaitDesugar => ("`await` future binding", None), hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None), }; self.check_irrefutable(&loc.pat, msg, sp); - self.check_patterns(&loc.pat, Irrefutable); } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { intravisit::walk_param(self, param); self.check_irrefutable(¶m.pat, "function argument", None); - self.check_patterns(¶m.pat, Irrefutable); } } @@ -161,12 +158,12 @@ fn check_match( &mut self, scrut: &hir::Expr<'_>, - arms: &'tcx [hir::Arm<'tcx>], + hir_arms: &'tcx [hir::Arm<'tcx>], source: hir::MatchSource, ) { let mut cx = self.new_cx(scrut.hir_id); - for arm in arms { + for arm in hir_arms { // Check the arm for some things unrelated to exhaustiveness. self.check_patterns(&arm.pat, Refutable); if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard { @@ -178,7 +175,7 @@ let mut have_errors = false; - let arms: Vec<_> = arms + let arms: Vec<_> = hir_arms .iter() .map(|hir::Arm { pat, guard, .. }| MatchArm { pat: self.lower_pattern(&mut cx, pat, &mut have_errors), @@ -196,6 +193,9 @@ let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty); match source { + // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }` + // when the iterator is an uninhabited type. unreachable_code will trigger instead. + hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {} hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { report_arm_reachability(&cx, &report) } @@ -208,7 +208,13 @@ let is_empty_match = arms.is_empty(); let witnesses = report.non_exhaustiveness_witnesses; if !witnesses.is_empty() { - non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match); + if source == hir::MatchSource::ForLoopDesugar && hir_arms.len() == 2 { + // the for loop pattern is not irrefutable + let pat = hir_arms[1].pat.for_loop_some().unwrap(); + self.check_irrefutable(pat, "`for` loop binding", None); + } else { + non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match); + } } } @@ -225,6 +231,7 @@ let witnesses = report.non_exhaustiveness_witnesses; if witnesses.is_empty() { // The pattern is irrefutable. + self.check_patterns(pat, Irrefutable); return; }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 847b89f..dd16e3c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -130,6 +130,9 @@ traits::NonStructuralMatchTy::Opaque => { "opaque types cannot be used in patterns".to_string() } + traits::NonStructuralMatchTy::Closure => { + "closures cannot be used in patterns".to_string() + } traits::NonStructuralMatchTy::Generator => { "generators cannot be used in patterns".to_string() }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index cb74ae4..ce80214 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -544,7 +544,7 @@ let (lit, neg) = match expr.kind { hir::ExprKind::ConstBlock(ref anon_const) => { let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); - let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id); + let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id); if matches!(value.val, ConstKind::Param(_)) { let span = self.tcx.hir().span(anon_const.hir_id); self.errors.push(PatternError::ConstParamInPattern(span));
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index d959d2f..286473c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -289,6 +289,7 @@ use rustc_data_structures::captures::Captures; use rustc_arena::TypedArena; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; use rustc_hir::HirId; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -443,9 +444,7 @@ /// expands it. fn push(&mut self, row: PatStack<'p, 'tcx>) { if !row.is_empty() && row.head().is_or_pat() { - for row in row.expand_or_pat() { - self.patterns.push(row); - } + self.patterns.extend(row.expand_or_pat()); } else { self.patterns.push(row); } @@ -797,7 +796,7 @@ return ret; } - assert!(rows.iter().all(|r| r.len() == v.len())); + debug_assert!(rows.iter().all(|r| r.len() == v.len())); let ty = v.head().ty(); let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); @@ -810,8 +809,9 @@ // We try each or-pattern branch in turn. let mut matrix = matrix.clone(); for v in v.expand_or_pat() { - let usefulness = - is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); + let usefulness = ensure_sufficient_stack(|| { + is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false) + }); ret.extend(usefulness); // If pattern has a guard don't add it to the matrix. if !is_under_guard { @@ -842,8 +842,9 @@ // We cache the result of `Fields::wildcards` because it is used a lot. let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor); let v = v.pop_head_constructor(cx, &ctor); - let usefulness = - is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false); + let usefulness = ensure_sufficient_stack(|| { + is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false) + }); let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor); // When all the conditions are met we have a match with a `non_exhaustive` enum
diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 7607ccc..11856f6 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
@@ -604,7 +604,7 @@ debug!("destructor_call_block({:?}, {:?})", self, succ); let tcx = self.tcx(); let drop_trait = tcx.require_lang_item(LangItem::Drop, None); - let drop_fn = tcx.associated_items(drop_trait).in_definition_order().next().unwrap(); + let drop_fn = tcx.associated_item_def_ids(drop_trait)[0]; let ty = self.place_ty(self.place); let substs = tcx.mk_substs_trait(ty, &[]); @@ -624,12 +624,7 @@ )], terminator: Some(Terminator { kind: TerminatorKind::Call { - func: Operand::function_handle( - tcx, - drop_fn.def_id, - substs, - self.source_info.span, - ), + func: Operand::function_handle(tcx, drop_fn, substs, self.source_info.span), args: vec![Operand::Move(Place::from(ref_place))], destination: Some((unit_temp, succ)), cleanup: unwind.into_option(),
diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index c000e49..ba6b566 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs
@@ -64,6 +64,13 @@ } } + /// Allows inspection of unreachable basic blocks even with `debug_assertions` enabled. + #[cfg(test)] + pub(crate) fn allow_unreachable(&mut self) { + #[cfg(debug_assertions)] + self.reachable_blocks.insert_all() + } + /// Returns the underlying `Results`. pub fn results(&self) -> &Results<'tcx, A> { &self.results.borrow()
diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index a598912..6efa8da 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs
@@ -268,6 +268,8 @@ let mut cursor = Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); + cursor.allow_unreachable(); + let every_target = || { body.basic_blocks() .iter_enumerated()
diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 158ba1b..d38b567 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
@@ -3,25 +3,14 @@ use crate::{AnalysisDomain, GenKill, GenKillAnalysis}; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; -use rustc_middle::ty::{ParamEnv, TyCtxt}; -use rustc_span::DUMMY_SP; - -pub type MaybeMutBorrowedLocals<'mir, 'tcx> = MaybeBorrowedLocals<MutBorrow<'mir, 'tcx>>; /// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points /// to a given local. /// -/// The `K` parameter determines what kind of borrows are tracked. By default, -/// `MaybeBorrowedLocals` looks for *any* borrow of a local. If you are only interested in borrows -/// that might allow mutation, use the `MaybeMutBorrowedLocals` type alias instead. -/// /// At present, this is used as a very limited form of alias analysis. For example, /// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for -/// immovable generators. `MaybeMutBorrowedLocals` is used during const checking to prove that a -/// local has not been mutated via indirect assignment (e.g., `*p = 42`), the side-effects of a -/// function call or inline assembly. -pub struct MaybeBorrowedLocals<K = AnyBorrow> { - kind: K, +/// immovable generators. +pub struct MaybeBorrowedLocals { ignore_borrow_on_drop: bool, } @@ -29,29 +18,11 @@ /// A dataflow analysis that records whether a pointer or reference exists that may alias the /// given local. pub fn all_borrows() -> Self { - MaybeBorrowedLocals { kind: AnyBorrow, ignore_borrow_on_drop: false } + MaybeBorrowedLocals { ignore_borrow_on_drop: false } } } -impl MaybeMutBorrowedLocals<'mir, 'tcx> { - /// A dataflow analysis that records whether a pointer or reference exists that may *mutably* - /// alias the given local. - /// - /// This includes `&mut` and pointers derived from an `&mut`, as well as shared borrows of - /// types with interior mutability. - pub fn mut_borrows_only( - tcx: TyCtxt<'tcx>, - body: &'mir mir::Body<'tcx>, - param_env: ParamEnv<'tcx>, - ) -> Self { - MaybeBorrowedLocals { - kind: MutBorrow { body, tcx, param_env }, - ignore_borrow_on_drop: false, - } - } -} - -impl<K> MaybeBorrowedLocals<K> { +impl MaybeBorrowedLocals { /// During dataflow analysis, ignore the borrow that may occur when a place is dropped. /// /// Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut self` as a @@ -69,21 +40,14 @@ MaybeBorrowedLocals { ignore_borrow_on_drop: true, ..self } } - fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T, K> { - TransferFunction { - kind: &self.kind, - trans, - ignore_borrow_on_drop: self.ignore_borrow_on_drop, - } + fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> { + TransferFunction { trans, ignore_borrow_on_drop: self.ignore_borrow_on_drop } } } -impl<K> AnalysisDomain<'tcx> for MaybeBorrowedLocals<K> -where - K: BorrowAnalysisKind<'tcx>, -{ +impl AnalysisDomain<'tcx> for MaybeBorrowedLocals { type Domain = BitSet<Local>; - const NAME: &'static str = K::ANALYSIS_NAME; + const NAME: &'static str = "maybe_borrowed_locals"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { // bottom = unborrowed @@ -95,10 +59,7 @@ } } -impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K> -where - K: BorrowAnalysisKind<'tcx>, -{ +impl GenKillAnalysis<'tcx> for MaybeBorrowedLocals { type Idx = Local; fn statement_effect( @@ -131,16 +92,14 @@ } /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`. -struct TransferFunction<'a, T, K> { +struct TransferFunction<'a, T> { trans: &'a mut T, - kind: &'a K, ignore_borrow_on_drop: bool, } -impl<T, K> Visitor<'tcx> for TransferFunction<'a, T, K> +impl<T> Visitor<'tcx> for TransferFunction<'a, T> where T: GenKill<Local>, - K: BorrowAnalysisKind<'tcx>, { fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) { self.super_statement(stmt, location); @@ -156,14 +115,14 @@ self.super_rvalue(rvalue, location); match rvalue { - mir::Rvalue::AddressOf(mt, borrowed_place) => { - if !borrowed_place.is_indirect() && self.kind.in_address_of(*mt, *borrowed_place) { + mir::Rvalue::AddressOf(_mt, borrowed_place) => { + if !borrowed_place.is_indirect() { self.trans.gen(borrowed_place.local); } } - mir::Rvalue::Ref(_, kind, borrowed_place) => { - if !borrowed_place.is_indirect() && self.kind.in_ref(*kind, *borrowed_place) { + mir::Rvalue::Ref(_, _kind, borrowed_place) => { + if !borrowed_place.is_indirect() { self.trans.gen(borrowed_place.local); } } @@ -211,64 +170,3 @@ } } } - -pub struct AnyBorrow; - -pub struct MutBorrow<'mir, 'tcx> { - tcx: TyCtxt<'tcx>, - body: &'mir Body<'tcx>, - param_env: ParamEnv<'tcx>, -} - -impl MutBorrow<'mir, 'tcx> { - /// `&` and `&raw` only allow mutation if the borrowed place is `!Freeze`. - /// - /// This assumes that it is UB to take the address of a struct field whose type is - /// `Freeze`, then use pointer arithmetic to derive a pointer to a *different* field of - /// that same struct whose type is `!Freeze`. If we decide that this is not UB, we will - /// have to check the type of the borrowed **local** instead of the borrowed **place** - /// below. See [rust-lang/unsafe-code-guidelines#134]. - /// - /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134 - fn shared_borrow_allows_mutation(&self, place: Place<'tcx>) -> bool { - !place.ty(self.body, self.tcx).ty.is_freeze(self.tcx.at(DUMMY_SP), self.param_env) - } -} - -pub trait BorrowAnalysisKind<'tcx> { - const ANALYSIS_NAME: &'static str; - - fn in_address_of(&self, mt: Mutability, place: Place<'tcx>) -> bool; - fn in_ref(&self, kind: mir::BorrowKind, place: Place<'tcx>) -> bool; -} - -impl BorrowAnalysisKind<'tcx> for AnyBorrow { - const ANALYSIS_NAME: &'static str = "maybe_borrowed_locals"; - - fn in_ref(&self, _: mir::BorrowKind, _: Place<'_>) -> bool { - true - } - fn in_address_of(&self, _: Mutability, _: Place<'_>) -> bool { - true - } -} - -impl BorrowAnalysisKind<'tcx> for MutBorrow<'mir, 'tcx> { - const ANALYSIS_NAME: &'static str = "maybe_mut_borrowed_locals"; - - fn in_ref(&self, kind: mir::BorrowKind, place: Place<'tcx>) -> bool { - match kind { - mir::BorrowKind::Mut { .. } => true, - mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => { - self.shared_borrow_allows_mutation(place) - } - } - } - - fn in_address_of(&self, mt: Mutability, place: Place<'tcx>) -> bool { - match mt { - Mutability::Mut => true, - Mutability::Not => self.shared_borrow_allows_mutation(place), - } - } -}
diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 474f4f2..2585701 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs
@@ -4,17 +4,18 @@ use rustc_index::bit_set::BitSet; use rustc_index::vec::Idx; +use rustc_middle::mir::visit::{MirVisitable, Visitor}; use rustc_middle::mir::{self, Body, Location}; use rustc_middle::ty::{self, TyCtxt}; -use crate::drop_flag_effects; use crate::drop_flag_effects_for_function_entry; use crate::drop_flag_effects_for_location; use crate::elaborate_drops::DropFlagState; use crate::framework::SwitchIntEdgeEffects; -use crate::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex}; +use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex}; use crate::on_lookup_result_bits; use crate::MoveDataParamEnv; +use crate::{drop_flag_effects, on_all_children_bits}; use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis}; mod borrowed_locals; @@ -22,7 +23,7 @@ mod liveness; mod storage_liveness; -pub use self::borrowed_locals::{MaybeBorrowedLocals, MaybeMutBorrowedLocals}; +pub use self::borrowed_locals::MaybeBorrowedLocals; pub use self::init_locals::MaybeInitializedLocals; pub use self::liveness::MaybeLiveLocals; pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive}; @@ -307,22 +308,45 @@ fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, - _statement: &mir::Statement<'tcx>, + statement: &mir::Statement<'tcx>, location: Location, ) { drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { Self::update_bits(trans, path, s) + }); + + if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration { + return; + } + + // Mark all places as "maybe init" if they are mutably borrowed. See #90752. + for_each_mut_borrow(statement, location, |place| { + let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref()) else { return }; + on_all_children_bits(self.tcx, self.body, self.move_data(), mpi, |child| { + trans.gen(child); + }) }) } fn terminator_effect( &self, trans: &mut impl GenKill<Self::Idx>, - _terminator: &mir::Terminator<'tcx>, + terminator: &mir::Terminator<'tcx>, location: Location, ) { drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { Self::update_bits(trans, path, s) + }); + + if !self.tcx.sess.opts.debugging_opts.precise_enum_drop_elaboration { + return; + } + + for_each_mut_borrow(terminator, location, |place| { + let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref()) else { return }; + on_all_children_bits(self.tcx, self.body, self.move_data(), mpi, |child| { + trans.gen(child); + }) }) } @@ -427,7 +451,10 @@ ) { drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { Self::update_bits(trans, path, s) - }) + }); + + // Unlike in `MaybeInitializedPlaces` above, we don't need to change the state when a + // mutable borrow occurs. Places cannot become uninitialized through a mutable reference. } fn terminator_effect( @@ -438,7 +465,7 @@ ) { drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| { Self::update_bits(trans, path, s) - }) + }); } fn call_return_effect( @@ -704,3 +731,37 @@ _ => None, } } + +struct OnMutBorrow<F>(F); + +impl<F> Visitor<'_> for OnMutBorrow<F> +where + F: FnMut(&mir::Place<'_>), +{ + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'_>, location: Location) { + // FIXME: Does `&raw const foo` allow mutation? See #90413. + match rvalue { + mir::Rvalue::Ref(_, mir::BorrowKind::Mut { .. }, place) + | mir::Rvalue::AddressOf(_, place) => (self.0)(place), + + _ => {} + } + + self.super_rvalue(rvalue, location) + } +} + +/// Calls `f` for each mutable borrow or raw reference in the program. +/// +/// This DOES NOT call `f` for a shared borrow of a type with interior mutability. That's okay for +/// initializedness, because we cannot move from an `UnsafeCell` (outside of `core::cell`), but +/// other analyses will likely need to check for `!Freeze`. +fn for_each_mut_borrow<'tcx>( + mir: &impl MirVisitable<'tcx>, + location: Location, + f: impl FnMut(&mir::Place<'_>), +) { + let mut vis = OnMutBorrow(f); + + mir.apply(location, &mut vis); +}
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 402391b..77a72ce 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -2,10 +2,10 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] -#![cfg_attr(bootstrap, feature(const_panic))] #![feature(exact_size_is_empty)] #![feature(in_band_lifetimes)] #![feature(iter_zip)] +#![feature(let_else)] #![feature(min_specialization)] #![feature(once_cell)] #![feature(stmt_expr_attributes)]
diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index c0bf4b6..28e5d76 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
@@ -11,8 +11,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use crate::impls::{ - DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeLiveLocals, MaybeMutBorrowedLocals, - MaybeUninitializedPlaces, + DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces, }; use crate::move_paths::{HasMoveData, MoveData}; use crate::move_paths::{LookupResult, MovePathIndex}; @@ -62,14 +61,6 @@ sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_def_inits); } - if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_indirectly_mutable).is_some() { - let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env) - .into_engine(tcx, body) - .iterate_to_fixpoint(); - - sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_mut_borrowed); - } - if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_liveness).is_some() { let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint(); @@ -281,28 +272,6 @@ } } -impl<'tcx> RustcPeekAt<'tcx> for MaybeMutBorrowedLocals<'_, 'tcx> { - fn peek_at( - &self, - tcx: TyCtxt<'tcx>, - place: mir::Place<'tcx>, - flow_state: &BitSet<Local>, - call: PeekCall, - ) { - info!(?place, "peek_at"); - let local = if let Some(l) = place.as_local() { - l - } else { - tcx.sess.span_err(call.span, "rustc_peek: argument was not a local"); - return; - }; - - if !flow_state.contains(local) { - tcx.sess.span_err(call.span, "rustc_peek: bit not set"); - } - } -} - impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals { fn peek_at( &self, @@ -312,9 +281,7 @@ call: PeekCall, ) { info!(?place, "peek_at"); - let local = if let Some(l) = place.as_local() { - l - } else { + let Some(local) = place.as_local() else { tcx.sess.span_err(call.span, "rustc_peek: argument was not a local"); return; };
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 17790ec..63c637a 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -68,11 +68,10 @@ return; } - use rustc_middle::hir::map::blocks::FnLikeNode; let def_id = body.source.def_id().expect_local(); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + let is_fn_like = tcx.hir().get(hir_id).fn_kind().is_some(); let is_assoc_const = tcx.def_kind(def_id.to_def_id()) == DefKind::AssocConst; // Only run const prop on functions, methods, closures and associated constants
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 1c946bd..6807d02 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -14,13 +14,11 @@ use crate::MirPass; -use rustc_data_structures::fingerprint::Fingerprint; 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; -use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::*; use rustc_middle::mir::dump_enabled; @@ -29,7 +27,6 @@ TerminatorKind, }; use rustc_middle::ty::TyCtxt; -use rustc_query_system::ich::StableHashingContext; use rustc_span::def_id::DefId; use rustc_span::source_map::SourceMap; use rustc_span::{CharPos, ExpnKind, Pos, SourceFile, Span, Symbol}; @@ -66,7 +63,7 @@ } let hir_id = tcx.hir().local_def_id_to_hir_id(mir_source.def_id().expect_local()); - let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + let is_fn_like = tcx.hir().get(hir_id).fn_kind().is_some(); // Only instrument functions, methods, and closures (not constants since they are evaluated // at compile time by Miri). @@ -76,7 +73,7 @@ // be tricky if const expressions have no corresponding statements in the enclosing MIR. // Closures are carved out by their initial `Assign` statement.) if !is_fn_like { - trace!("InstrumentCoverage skipped for {:?} (not an FnLikeNode)", mir_source.def_id()); + trace!("InstrumentCoverage skipped for {:?} (not an fn-like)", mir_source.def_id()); return; } @@ -488,7 +485,7 @@ // Non-code expressions are injected into the coverage map, without generating executable code. fn inject_intermediate_expression(mir_body: &mut mir::Body<'tcx>, expression: CoverageKind) { - debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false }); + debug_assert!(matches!(expression, CoverageKind::Expression { .. })); debug!(" injecting non-code expression {:?}", expression); let inject_in_bb = mir::START_BLOCK; let data = &mut mir_body[inject_in_bb]; @@ -574,15 +571,13 @@ } 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(); - hash(&mut hcx, &hir_body.value).to_smaller_hash() -} - -fn hash( - hcx: &mut StableHashingContext<'tcx>, - node: &impl HashStable<StableHashingContext<'tcx>>, -) -> Fingerprint { let mut stable_hasher = StableHasher::new(); - node.hash_stable(hcx, &mut stable_hasher); + let owner = hir_body.id().hir_id.owner; + let bodies = &tcx.hir_owner_nodes(owner).as_ref().unwrap().bodies; + hcx.with_hir_bodies(false, owner, bodies, |hcx| { + hir_body.value.hash_stable(hcx, &mut stable_hasher) + }); stable_hasher.finish() }
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index cc525a4..d13fa07 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -94,10 +94,9 @@ stmt_index: usize, ) -> Self { let is_closure = match statement.kind { - StatementKind::Assign(box (_, Rvalue::Aggregate(box ref kind, _))) => match kind { - AggregateKind::Closure(_, _) | AggregateKind::Generator(_, _, _) => true, - _ => false, - }, + StatementKind::Assign(box (_, Rvalue::Aggregate(box ref kind, _))) => { + matches!(kind, AggregateKind::Closure(_, _) | AggregateKind::Generator(_, _, _)) + } _ => false, };
diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index 2a24e1e..6b99514 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs
@@ -1,13 +1,12 @@ //! This pass just dumps MIR at a specified point. use std::borrow::Cow; -use std::fmt; use std::fs::File; use std::io; use crate::MirPass; +use rustc_middle::mir::write_mir_pretty; use rustc_middle::mir::Body; -use rustc_middle::mir::{dump_enabled, dump_mir, write_mir_pretty}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{OutputFilenames, OutputType}; @@ -21,29 +20,6 @@ fn run_pass(&self, _tcx: TyCtxt<'tcx>, _body: &mut Body<'tcx>) {} } -pub struct Disambiguator { - is_after: bool, -} - -impl fmt::Display for Disambiguator { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - let title = if self.is_after { "after" } else { "before" }; - write!(formatter, "{}", title) - } -} - -pub fn on_mir_pass<'tcx>( - tcx: TyCtxt<'tcx>, - pass_num: &dyn fmt::Display, - pass_name: &str, - body: &Body<'tcx>, - is_after: bool, -) { - if dump_enabled(tcx, pass_name, body.source.def_id()) { - dump_mir(tcx, Some(pass_num), pass_name, &Disambiguator { is_after }, body, |_, _| Ok(())); - } -} - pub fn emit_mir(tcx: TyCtxt<'_>, outputs: &OutputFilenames) -> io::Result<()> { let path = outputs.path(OutputType::Mir); let mut f = io::BufWriter::new(File::create(&path)?);
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index ee4e91e..84a1e3f 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -673,9 +673,7 @@ assert!(args.next().is_none()); let tuple = Place::from(tuple); - let tuple_tys = if let ty::Tuple(s) = tuple.ty(caller_body, tcx).ty.kind() { - s - } else { + let ty::Tuple(tuple_tys) = tuple.ty(caller_body, tcx).ty.kind() else { bug!("Closure arguments are not passed as a tuple"); };
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9b11c8f..f9ef314 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -1,9 +1,9 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(crate_visibility_modifier)] -#![cfg_attr(bootstrap, feature(const_panic))] #![feature(in_band_lifetimes)] #![feature(iter_zip)] +#![feature(let_else)] #![feature(map_try_insert)] #![feature(min_specialization)] #![feature(option_get_or_insert_default)] @@ -27,7 +27,7 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::Visitor as _; -use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted}; +use rustc_middle::mir::{dump_mir, traversal, Body, ConstQualifs, MirPhase, Promoted}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::{Span, Symbol}; @@ -65,6 +65,7 @@ mod remove_unneeded_drops; mod remove_zsts; mod required_consts; +mod reveal_all; mod separate_const_switch; mod shim; mod simplify; @@ -187,12 +188,14 @@ let mut index = 0; let mut run_pass = |pass: &dyn MirPass<'tcx>| { let run_hooks = |body: &_, index, is_after| { - dump_mir::on_mir_pass( + let disambiguator = if is_after { "after" } else { "before" }; + dump_mir( tcx, - &format_args!("{:03}-{:03}", phase_index, index), + Some(&format_args!("{:03}-{:03}", phase_index, index)), &pass.name(), + &disambiguator, body, - is_after, + |_, _| Ok(()), ); }; run_hooks(body, index, false); @@ -428,8 +431,7 @@ } let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - use rustc_middle::hir::map::blocks::FnLikeNode; - let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + let is_fn_like = tcx.hir().get(hir_id).fn_kind().is_some(); if is_fn_like { let did = def.did.to_def_id(); let def = ty::WithOptConstParam::unknown(did); @@ -488,6 +490,7 @@ // to them. We run some optimizations before that, because they may be harder to do on the state // machine than on MIR with async primitives. let optimizations_with_generators: &[&dyn MirPass<'tcx>] = &[ + &reveal_all::RevealAll, // has to be done before inlining, since inlined code is in RevealAll mode. &lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first &normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering &unreachable_prop::UnreachablePropagation,
diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index a2cce9f..822a372 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs
@@ -17,9 +17,7 @@ pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let language_items = tcx.lang_items(); - let slice_len_fn_item_def_id = if let Some(slice_len_fn_item) = language_items.slice_len_fn() { - slice_len_fn_item - } else { + let Some(slice_len_fn_item_def_id) = language_items.slice_len_fn() else { // there is no language item to compare to :) return; };
diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs index 76f0e83..a04a0b5 100644 --- a/compiler/rustc_mir_transform/src/normalize_array_len.rs +++ b/compiler/rustc_mir_transform/src/normalize_array_len.rs
@@ -208,7 +208,7 @@ operand, cast_ty, ) => { - let local = if let Some(local) = place.as_local() { local } else { return }; + let Some(local) = place.as_local() else { return }; match operand { Operand::Copy(place) | Operand::Move(place) => { let operand_local = @@ -255,9 +255,7 @@ } } Rvalue::Len(place) => { - let local = if let Some(local) = place.local_or_deref_local() { - local - } else { + let Some(local) = place.local_or_deref_local() else { return; }; if let Some(cast_statement_idx) = state.get(&local).copied() {
diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index 5c9d04a..c71bc51 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
@@ -13,7 +13,7 @@ trace!("Running RemoveUnneededDrops on {:?}", body.source); let did = body.source.def_id(); - let param_env = tcx.param_env(did); + 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();
diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs new file mode 100644 index 0000000..6c423a2 --- /dev/null +++ b/compiler/rustc_mir_transform/src/reveal_all.rs
@@ -0,0 +1,58 @@ +//! Normalizes MIR in RevealAll mode. + +use crate::MirPass; +use rustc_middle::mir::visit::*; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +pub struct RevealAll; + +impl<'tcx> MirPass<'tcx> for RevealAll { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + // This pass must run before inlining, since we insert callee bodies in RevealAll mode. + // Do not apply this transformation to generators. + if (tcx.sess.mir_opt_level() >= 3 || super::inline::is_enabled(tcx)) + && body.generator.is_none() + { + let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); + RevealAllVisitor { tcx, param_env }.visit_body(body); + } + } +} + +struct RevealAllVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, +} + +impl<'tcx> MutVisitor<'tcx> for RevealAllVisitor<'tcx> { + #[inline] + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + #[inline] + fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) { + *ty = self.tcx.normalize_erasing_regions(self.param_env, ty); + } + + #[inline] + fn process_projection_elem( + &mut self, + elem: PlaceElem<'tcx>, + _: Location, + ) -> Option<PlaceElem<'tcx>> { + match elem { + PlaceElem::Field(field, ty) => { + let new_ty = self.tcx.normalize_erasing_regions(self.param_env, ty); + if ty != new_ty { Some(PlaceElem::Field(field, new_ty)) } else { None } + } + // None of those contain a Ty. + PlaceElem::Index(..) + | PlaceElem::Deref + | PlaceElem::ConstantIndex { .. } + | PlaceElem::Subslice { .. } + | PlaceElem::Downcast(..) => None, + } + } +}
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index f2ea5fe..f59aaa6 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -310,7 +310,6 @@ match self_ty.kind() { _ if is_copy => builder.copy_shim(), - ty::Array(ty, len) => builder.array_shim(dest, src, ty, len), ty::Closure(_, substs) => { builder.tuple_like_shim(dest, src, substs.as_closure().upvar_tys()) } @@ -459,154 +458,6 @@ ); } - fn loop_header( - &mut self, - beg: Place<'tcx>, - end: Place<'tcx>, - loop_body: BasicBlock, - loop_end: BasicBlock, - is_cleanup: bool, - ) { - let tcx = self.tcx; - - let cond = self.make_place(Mutability::Mut, tcx.types.bool); - let compute_cond = self.make_statement(StatementKind::Assign(Box::new(( - cond, - Rvalue::BinaryOp(BinOp::Ne, Box::new((Operand::Copy(end), Operand::Copy(beg)))), - )))); - - // `if end != beg { goto loop_body; } else { goto loop_end; }` - self.block( - vec![compute_cond], - TerminatorKind::if_(tcx, Operand::Move(cond), loop_body, loop_end), - is_cleanup, - ); - } - - fn make_usize(&self, value: u64) -> Box<Constant<'tcx>> { - Box::new(Constant { - span: self.span, - user_ty: None, - literal: ty::Const::from_usize(self.tcx, value).into(), - }) - } - - fn array_shim( - &mut self, - dest: Place<'tcx>, - src: Place<'tcx>, - ty: Ty<'tcx>, - len: &'tcx ty::Const<'tcx>, - ) { - let tcx = self.tcx; - let span = self.span; - - let beg = self.local_decls.push(LocalDecl::new(tcx.types.usize, span)); - let end = self.make_place(Mutability::Not, tcx.types.usize); - - // BB #0 - // `let mut beg = 0;` - // `let end = len;` - // `goto #1;` - let inits = vec![ - self.make_statement(StatementKind::Assign(Box::new(( - Place::from(beg), - Rvalue::Use(Operand::Constant(self.make_usize(0))), - )))), - self.make_statement(StatementKind::Assign(Box::new(( - end, - Rvalue::Use(Operand::Constant(Box::new(Constant { - span: self.span, - user_ty: None, - literal: len.into(), - }))), - )))), - ]; - self.block(inits, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); - - // BB #1: loop { - // BB #2; - // BB #3; - // } - // BB #4; - self.loop_header(Place::from(beg), end, BasicBlock::new(2), BasicBlock::new(4), false); - - // BB #2 - // `dest[i] = Clone::clone(src[beg])`; - // Goto #3 if ok, #5 if unwinding happens. - let dest_field = self.tcx.mk_place_index(dest, beg); - let src_field = self.tcx.mk_place_index(src, beg); - self.make_clone_call(dest_field, src_field, ty, BasicBlock::new(3), BasicBlock::new(5)); - - // BB #3 - // `beg = beg + 1;` - // `goto #1`; - let statements = vec![self.make_statement(StatementKind::Assign(Box::new(( - Place::from(beg), - Rvalue::BinaryOp( - BinOp::Add, - Box::new((Operand::Copy(Place::from(beg)), Operand::Constant(self.make_usize(1)))), - ), - ))))]; - self.block(statements, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); - - // BB #4 - // `return dest;` - self.block(vec![], TerminatorKind::Return, false); - - // BB #5 (cleanup) - // `let end = beg;` - // `let mut beg = 0;` - // goto #6; - let end = beg; - let beg = self.local_decls.push(LocalDecl::new(tcx.types.usize, span)); - let init = self.make_statement(StatementKind::Assign(Box::new(( - Place::from(beg), - Rvalue::Use(Operand::Constant(self.make_usize(0))), - )))); - self.block(vec![init], TerminatorKind::Goto { target: BasicBlock::new(6) }, true); - - // BB #6 (cleanup): loop { - // BB #7; - // BB #8; - // } - // BB #9; - self.loop_header( - Place::from(beg), - Place::from(end), - BasicBlock::new(7), - BasicBlock::new(9), - true, - ); - - // BB #7 (cleanup) - // `drop(dest[beg])`; - self.block( - vec![], - TerminatorKind::Drop { - place: self.tcx.mk_place_index(dest, beg), - target: BasicBlock::new(8), - unwind: None, - }, - true, - ); - - // BB #8 (cleanup) - // `beg = beg + 1;` - // `goto #6;` - let statement = self.make_statement(StatementKind::Assign(Box::new(( - Place::from(beg), - Rvalue::BinaryOp( - BinOp::Add, - Box::new((Operand::Copy(Place::from(beg)), Operand::Constant(self.make_usize(1)))), - ), - )))); - self.block(vec![statement], TerminatorKind::Goto { target: BasicBlock::new(6) }, true); - - // BB #9 (resume) - self.block(vec![], TerminatorKind::Resume, true); - } - fn tuple_like_shim<I>(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I) where I: Iterator<Item = Ty<'tcx>>,
diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs index 5cef64d..2aa5061 100644 --- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
@@ -83,12 +83,9 @@ let bb = BasicBlock::from_usize(bb); trace!("processing block {:?}", bb); - let discriminant_ty = - if let Some(ty) = get_switched_on_type(&body.basic_blocks()[bb], tcx, body) { - ty - } else { - continue; - }; + let Some(discriminant_ty) = get_switched_on_type(&body.basic_blocks()[bb], tcx, body) else { + continue; + }; let layout = tcx.layout_of(tcx.param_env(body.source.def_id()).and(discriminant_ty));
diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index baf3810..64cd6f5 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs
@@ -27,9 +27,8 @@ // This is a temporary solution that handles possibly diverging asm statements. // Accompanying testcases: mir-opt/unreachable_asm.rs and mir-opt/unreachable_asm_2.rs let asm_stmt_in_block = || { - bb_data.statements.iter().any(|stmt: &Statement<'_>| match stmt.kind { - StatementKind::LlvmInlineAsm(..) => true, - _ => false, + bb_data.statements.iter().any(|stmt: &Statement<'_>| { + matches!(stmt.kind, StatementKind::LlvmInlineAsm(..)) }) };
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 5147408..59988e6 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -806,13 +806,22 @@ } } } + mir::TerminatorKind::Assert { ref msg, .. } => { + let lang_item = match msg { + mir::AssertKind::BoundsCheck { .. } => LangItem::PanicBoundsCheck, + _ => LangItem::Panic, + }; + let instance = Instance::mono(tcx, tcx.require_lang_item(lang_item, Some(source))); + if should_codegen_locally(tcx, &instance) { + self.output.push(create_fn_mono_item(tcx, instance, source)); + } + } mir::TerminatorKind::Goto { .. } | mir::TerminatorKind::SwitchInt { .. } | mir::TerminatorKind::Resume | mir::TerminatorKind::Abort | mir::TerminatorKind::Return - | mir::TerminatorKind::Unreachable - | mir::TerminatorKind::Assert { .. } => {} + | mir::TerminatorKind::Unreachable => {} mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Yield { .. } | mir::TerminatorKind::FalseEdge { .. }
diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 08b1d7b..f408215 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs
@@ -2,6 +2,7 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(control_flow_enum)] +#![feature(let_else)] #![feature(in_band_lifetimes)] #![recursion_limit = "256"]
diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs index be68208..b419061 100644 --- a/compiler/rustc_monomorphize/src/partitioning/default.rs +++ b/compiler/rustc_monomorphize/src/partitioning/default.rs
@@ -458,9 +458,7 @@ let is_generic = instance.substs.non_erasable_generics().next().is_some(); // Upstream `DefId` instances get different handling than local ones. - let def_id = if let Some(def_id) = def_id.as_local() { - def_id - } else { + let Some(def_id) = def_id.as_local() else { return if export_generics && is_generic { // If it is an upstream monomorphization and we export generics, we must make // it available to downstream crates.
diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index 7a7a56a..658c902 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs
@@ -361,6 +361,17 @@ ) }); + if tcx.prof.enabled() { + // Record CGU size estimates for self-profiling. + for cgu in codegen_units { + tcx.prof.artifact_size( + "codegen_unit_size_estimate", + &cgu.name().as_str()[..], + cgu.size_estimate() as u64, + ); + } + } + let mono_items: DefIdSet = items .iter() .filter_map(|mono_item| match *mono_item {
diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index e6e4438..5950806 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs
@@ -167,6 +167,7 @@ | DefKind::Use | DefKind::ForeignMod | DefKind::AnonConst + | DefKind::InlineConst | DefKind::OpaqueTy | DefKind::Field | DefKind::LifetimeParam @@ -195,7 +196,7 @@ generics: &'tcx ty::Generics, unused_parameters: &FiniteBitSet<u32>, ) { - let base_def_id = tcx.closure_base_def_id(def_id); + let base_def_id = tcx.typeck_root_def_id(def_id); if !tcx.get_attrs(base_def_id).iter().any(|a| a.has_name(sym::rustc_polymorphize_error)) { return; } @@ -303,7 +304,7 @@ ControlFlow::CONTINUE } ty::ConstKind::Unevaluated(uv) - if self.tcx.def_kind(uv.def.did) == DefKind::AnonConst => + if matches!(self.tcx.def_kind(uv.def.did), DefKind::AnonConst | DefKind::InlineConst) => { self.visit_child_body(uv.def.did, uv.substs(self.tcx)); ControlFlow::CONTINUE
diff --git a/compiler/rustc_monomorphize/src/util.rs b/compiler/rustc_monomorphize/src/util.rs index 799b4e1..4392c02 100644 --- a/compiler/rustc_monomorphize/src/util.rs +++ b/compiler/rustc_monomorphize/src/util.rs
@@ -67,7 +67,7 @@ src_file.prefer_local(), line_nos ) { - eprintln!("Error writting to file {}", e.to_string()) + eprintln!("Error writing to file {}", e) } } }
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 8e90f73..1a62096 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -1,11 +1,13 @@ +use crate::lexer::unicode_chars::UNICODE_ARRAY; use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::token::{self, CommentKind, Token, TokenKind}; use rustc_ast::tokenstream::{Spacing, TokenStream}; +use rustc_ast::util::unicode::contains_text_flow_control_chars; use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError, PResult}; use rustc_lexer::unescape::{self, Mode}; use rustc_lexer::{Base, DocStyle, RawStrError}; use rustc_session::lint::builtin::{ - TEXT_DIRECTION_CODEPOINT_IN_COMMENT, RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, + RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, }; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::ParseSess; @@ -137,12 +139,8 @@ // Opening delimiter of the length 2 is not included into the comment text. let content_start = start + BytePos(2); let content = self.str_from(content_start); - let span = self.mk_sp(start, self.pos); - const UNICODE_TEXT_FLOW_CHARS: &[char] = &[ - '\u{202A}', '\u{202B}', '\u{202D}', '\u{202E}', '\u{2066}', '\u{2067}', '\u{2068}', - '\u{202C}', '\u{2069}', - ]; - if content.contains(UNICODE_TEXT_FLOW_CHARS) { + if contains_text_flow_control_chars(content) { + let span = self.mk_sp(start, self.pos); self.sess.buffer_lint_with_diagnostic( &TEXT_DIRECTION_CODEPOINT_IN_COMMENT, span, @@ -225,6 +223,22 @@ } token::Ident(sym, is_raw_ident) } + rustc_lexer::TokenKind::InvalidIdent + // Do not recover an identifier with emoji if the codepoint is a confusable + // with a recoverable substitution token, like `âž–`. + if UNICODE_ARRAY + .iter() + .find(|&&(c, _, _)| { + let sym = self.str_from(start); + sym.chars().count() == 1 && c == sym.chars().next().unwrap() + }) + .is_none() => + { + let sym = nfc_normalize(self.str_from(start)); + let span = self.mk_sp(start, self.pos); + self.sess.bad_unicode_identifiers.borrow_mut().entry(sym).or_default().push(span); + token::Ident(sym, false) + } rustc_lexer::TokenKind::Literal { kind, suffix_start } => { let suffix_start = start + BytePos(suffix_start as u32); let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind); @@ -296,7 +310,7 @@ rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret), rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent), - rustc_lexer::TokenKind::Unknown => { + rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => { let c = self.str_from(start).chars().next().unwrap(); let mut err = self.struct_fatal_span_char(start, self.pos, "unknown start of token", c);
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 569f186..7f68112 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -82,6 +82,33 @@ Applicability::MachineApplicable, ); } + } else { + let printable: Vec<char> = lit + .chars() + .filter(|&x| { + unicode_width::UnicodeWidthChar::width(x).unwrap_or(0) != 0 + && !x.is_whitespace() + }) + .collect(); + + if let [ch] = printable.as_slice() { + has_help = true; + + handler.span_note( + span, + &format!( + "there are non-printing characters, the full sequence is `{}`", + lit.escape_default(), + ), + ); + + handler.span_suggestion( + span, + "consider removing the non-printing characters", + ch.to_string(), + Applicability::MaybeIncorrect, + ); + } } if !has_help {
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index 3eebc08..ccd11f0 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -7,7 +7,7 @@ use rustc_span::{symbol::kw, BytePos, Pos, Span}; #[rustfmt::skip] // for line breaks -const UNICODE_ARRAY: &[(char, &str, char)] = &[ +pub(crate) const UNICODE_ARRAY: &[(char, &str, char)] = &[ ('
', "Line Separator", ' '), ('
', "Paragraph Separator", ' '), (' ', "Ogham Space mark", ' '),
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 8095f38..ce39d07 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -6,9 +6,11 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; -use rustc_ast::{AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec}; -use rustc_ast::{BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item}; -use rustc_ast::{ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty, TyKind}; +use rustc_ast::{ + AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, + BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Mutability, Param, Pat, + PatKind, Path, PathSegment, QSelf, Ty, TyKind, +}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err}; @@ -662,7 +664,7 @@ let snapshot = self.clone(); self.bump(); let lo = self.token.span; - match self.parse_angle_args() { + match self.parse_angle_args(None) { Ok(args) => { let span = lo.to(self.prev_token.span); // Detect trailing `>` like in `x.collect::Vec<_>>()`. @@ -719,7 +721,7 @@ let x = self.parse_seq_to_before_end( &token::Gt, SeqSep::trailing_allowed(token::Comma), - |p| p.parse_generic_arg(), + |p| p.parse_generic_arg(None), ); match x { Ok((_, _, false)) => { @@ -1103,7 +1105,7 @@ self.expect(&token::ModSep)?; let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP, tokens: None }; - self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?; + self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?; path.span = ty_span.to(self.prev_token.span); let ty_str = self.span_to_snippet(ty_span).unwrap_or_else(|_| pprust::ty_to_string(&ty)); @@ -1121,7 +1123,7 @@ Ok(P(T::recovered(Some(QSelf { ty, path_span, position: 0 }), path))) } - pub(super) fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool { + pub fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool { if self.eat(&token::Semi) { let mut err = self.struct_span_err(self.prev_token.span, "expected item, found `;`"); err.span_suggestion_short( @@ -1342,10 +1344,10 @@ self.struct_span_err( MultiSpan::from_spans(vec![begin_par_sp, self.prev_token.span]), - "unexpected parenthesis surrounding `for` loop head", + "unexpected parentheses surrounding `for` loop head", ) .multipart_suggestion( - "remove parenthesis in `for` loop", + "remove parentheses in `for` loop", vec![(begin_par_sp, String::new()), (self.prev_token.span, String::new())], // With e.g. `for (x) in y)` this would replace `(x) in y)` // with `x) in y)` which is syntactically invalid. @@ -1909,6 +1911,71 @@ Ok(expr) } + fn recover_const_param_decl( + &mut self, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Option<GenericArg>> { + let snapshot = self.clone(); + let param = match self.parse_const_param(vec![]) { + Ok(param) => param, + Err(mut err) => { + err.cancel(); + *self = snapshot; + return Err(err); + } + }; + let mut err = + self.struct_span_err(param.span(), "unexpected `const` parameter declaration"); + err.span_label(param.span(), "expected a `const` expression, not a parameter declaration"); + if let (Some(generics), Ok(snippet)) = + (ty_generics, self.sess.source_map().span_to_snippet(param.span())) + { + let (span, sugg) = match &generics.params[..] { + [] => (generics.span, format!("<{}>", snippet)), + [.., generic] => (generic.span().shrink_to_hi(), format!(", {}", snippet)), + }; + err.multipart_suggestion( + "`const` parameters must be declared for the `impl`", + vec![(span, sugg), (param.span(), param.ident.to_string())], + Applicability::MachineApplicable, + ); + } + let value = self.mk_expr_err(param.span()); + err.emit(); + return Ok(Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))); + } + + pub fn recover_const_param_declaration( + &mut self, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Option<GenericArg>> { + // We have to check for a few different cases. + if let Ok(arg) = self.recover_const_param_decl(ty_generics) { + return Ok(arg); + } + + // We haven't consumed `const` yet. + let start = self.token.span; + self.bump(); // `const` + + // Detect and recover from the old, pre-RFC2000 syntax for const generics. + let mut err = self + .struct_span_err(start, "expected lifetime, type, or constant, found keyword `const`"); + if self.check_const_arg() { + err.span_suggestion_verbose( + start.until(self.token.span), + "the `const` keyword is only needed in the definition of the type", + String::new(), + Applicability::MaybeIncorrect, + ); + err.emit(); + Ok(Some(GenericArg::Const(self.parse_const_arg()?))) + } else { + let after_kw_const = self.token.span; + self.recover_const_arg(after_kw_const, err).map(Some) + } + } + /// Try to recover from possible generic const argument without `{` and `}`. /// /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3d29d30..f7ee874 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1032,6 +1032,8 @@ [IdentLike(_), Punct('+' | '-')] | // 1e+2 | 1e-2 [IdentLike(_), Punct('+' | '-'), IdentLike(_)] | + // 1.2e+ | 1.2e- + [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-')] | // 1.2e+3 | 1.2e-3 [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => { // See the FIXME about `TokenCursor` above. @@ -1148,7 +1150,7 @@ } let fn_span_lo = self.token.span; - let mut segment = self.parse_path_segment(PathStyle::Expr)?; + let mut segment = self.parse_path_segment(PathStyle::Expr, None)?; self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]); self.check_turbofish_missing_angle_brackets(&mut segment); @@ -1241,7 +1243,7 @@ } else if self.eat_keyword(kw::Unsafe) { self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs) } else if self.check_inline_const(0) { - self.parse_const_block(lo.to(self.token.span)) + self.parse_const_block(lo.to(self.token.span), false) } else if self.is_do_catch_block() { self.recover_do_catch(attrs) } else if self.is_try_block() {
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index f175c5b..a9ab2cd 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -48,7 +48,10 @@ }) } - fn parse_const_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> { + crate fn parse_const_param( + &mut self, + preceding_attrs: Vec<Attribute>, + ) -> PResult<'a, GenericParam> { let const_span = self.token.span; self.expect_keyword(kw::Const)?;
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 624390a..24a8df4 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs
@@ -216,11 +216,11 @@ return Err(e); } - (Ident::invalid(), ItemKind::Use(tree)) + (Ident::empty(), ItemKind::Use(tree)) } else if self.check_fn_front_matter(def_final) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, req_name, lo)?; - (ident, ItemKind::Fn(Box::new(FnKind(def(), sig, generics, body)))) + (ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body }))) } else if self.eat_keyword(kw::Extern) { if self.eat_keyword(kw::Crate) { // EXTERN CRATE @@ -279,15 +279,15 @@ } else if self.eat_keyword(kw::Macro) { // MACROS 2.0 ITEM self.parse_item_decl_macro(lo)? - } else if self.is_macro_rules_item() { + } else if let IsMacroRulesItem::Yes { has_bang } = self.is_macro_rules_item() { // MACRO_RULES ITEM - self.parse_item_macro_rules(vis)? + self.parse_item_macro_rules(vis, has_bang)? } else if vis.kind.is_pub() && self.isnt_macro_invocation() { self.recover_missing_kw_before_item()?; return Ok(None); } else if macros_allowed && self.check_path() { // MACRO INVOCATION ITEM - (Ident::invalid(), ItemKind::MacCall(self.parse_item_macro(vis)?)) + (Ident::empty(), ItemKind::MacCall(self.parse_item_macro(vis)?)) } else { return Ok(None); }; @@ -300,7 +300,7 @@ || self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }` || self.check_auto_or_unsafe_trait_item() // no: `auto::b`, yes: `auto trait X { .. }` || self.is_async_fn() // no(2015): `async::b`, yes: `async fn` - || self.is_macro_rules_item() // no: `macro_rules::b`, yes: `macro_rules! mac` + || matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac` } /// Are we sure this could not possibly be a macro invocation? @@ -514,7 +514,7 @@ tokens: None, }) } else { - self.parse_ty()? + self.parse_ty_with_generics_recovery(&generics)? }; // If `for` is missing we try to recover. @@ -560,7 +560,7 @@ }; let trait_ref = TraitRef { path, ref_id: ty_first.id }; - ItemKind::Impl(Box::new(ImplKind { + ItemKind::Impl(Box::new(Impl { unsafety, polarity, defaultness, @@ -573,7 +573,7 @@ } None => { // impl Type - ItemKind::Impl(Box::new(ImplKind { + ItemKind::Impl(Box::new(Impl { unsafety, polarity, defaultness, @@ -586,7 +586,7 @@ } }; - Ok((Ident::invalid(), item_kind)) + Ok((Ident::empty(), item_kind)) } fn parse_item_list<T>( @@ -682,7 +682,7 @@ self.expect_keyword(kw::Trait)?; let ident = self.parse_ident()?; - let mut tps = self.parse_generics()?; + let mut generics = self.parse_generics()?; // Parse optional colon and supertrait bounds. let had_colon = self.eat(&token::Colon); @@ -702,7 +702,7 @@ } let bounds = self.parse_generic_bounds(None)?; - tps.where_clause = self.parse_where_clause()?; + generics.where_clause = self.parse_where_clause()?; self.expect_semi()?; let whole_span = lo.to(self.prev_token.span); @@ -717,12 +717,15 @@ self.sess.gated_spans.gate(sym::trait_alias, whole_span); - Ok((ident, ItemKind::TraitAlias(tps, bounds))) + Ok((ident, ItemKind::TraitAlias(generics, bounds))) } else { // It's a normal trait. - tps.where_clause = self.parse_where_clause()?; + generics.where_clause = self.parse_where_clause()?; let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; - Ok((ident, ItemKind::Trait(Box::new(TraitKind(is_auto, unsafety, tps, bounds, items))))) + Ok(( + ident, + ItemKind::Trait(Box::new(Trait { is_auto, unsafety, generics, bounds, items })), + )) } } @@ -769,7 +772,7 @@ /// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ; /// ``` /// The `"type"` has already been eaten. - fn parse_type_alias(&mut self, def: Defaultness) -> PResult<'a, ItemInfo> { + fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> { let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; @@ -778,10 +781,10 @@ if self.eat(&token::Colon) { self.parse_generic_bounds(None)? } else { Vec::new() }; generics.where_clause = self.parse_where_clause()?; - let default = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None }; + let ty = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None }; self.expect_semi()?; - Ok((ident, ItemKind::TyAlias(Box::new(TyAliasKind(def, generics, bounds, default))))) + Ok((ident, ItemKind::TyAlias(Box::new(TyAlias { defaultness, generics, bounds, ty })))) } /// Parses a `UseTree`. @@ -933,7 +936,7 @@ let abi = self.parse_abi(); // ABI? let items = self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?; let module = ast::ForeignMod { unsafety, abi, items }; - Ok((Ident::invalid(), ItemKind::ForeignMod(module))) + Ok((Ident::empty(), ItemKind::ForeignMod(module))) } /// Parses a foreign item (one in an `extern { ... }` block). @@ -1039,9 +1042,7 @@ }; match impl_info.1 { - ItemKind::Impl(box ImplKind { - of_trait: Some(ref trai), ref mut constness, .. - }) => { + ItemKind::Impl(box Impl { of_trait: Some(ref trai), ref mut constness, .. }) => { *constness = Const::Yes(const_span); let before_trait = trai.path.span.shrink_to_lo(); @@ -1534,18 +1535,43 @@ Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, macro_rules: false }))) } - /// Is this unambiguously the start of a `macro_rules! foo` item definition? - fn is_macro_rules_item(&mut self) -> bool { - self.check_keyword(kw::MacroRules) - && self.look_ahead(1, |t| *t == token::Not) - && self.look_ahead(2, |t| t.is_ident()) + /// Is this a possibly malformed start of a `macro_rules! foo` item definition? + + fn is_macro_rules_item(&mut self) -> IsMacroRulesItem { + if self.check_keyword(kw::MacroRules) { + let macro_rules_span = self.token.span; + + if self.look_ahead(1, |t| *t == token::Not) && self.look_ahead(2, |t| t.is_ident()) { + return IsMacroRulesItem::Yes { has_bang: true }; + } else if self.look_ahead(1, |t| (t.is_ident())) { + // macro_rules foo + self.struct_span_err(macro_rules_span, "expected `!` after `macro_rules`") + .span_suggestion( + macro_rules_span, + "add a `!`", + "macro_rules!".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + + return IsMacroRulesItem::Yes { has_bang: false }; + } + } + + IsMacroRulesItem::No } /// Parses a `macro_rules! foo { ... }` declarative macro. - fn parse_item_macro_rules(&mut self, vis: &Visibility) -> PResult<'a, ItemInfo> { + fn parse_item_macro_rules( + &mut self, + vis: &Visibility, + has_bang: bool, + ) -> PResult<'a, ItemInfo> { self.expect_keyword(kw::MacroRules)?; // `macro_rules` - self.expect(&token::Not)?; // `!` + if has_bang { + self.expect(&token::Not)?; // `!` + } let ident = self.parse_ident()?; if self.eat(&token::Not) { @@ -2121,3 +2147,8 @@ } } } + +enum IsMacroRulesItem { + Yes { has_bang: bool }, + No, +}
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 5c701fe..9212aaa 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -1095,8 +1095,12 @@ } /// Parses inline const expressions. - fn parse_const_block(&mut self, span: Span) -> PResult<'a, P<Expr>> { - self.sess.gated_spans.gate(sym::inline_const, span); + fn parse_const_block(&mut self, span: Span, pat: bool) -> PResult<'a, P<Expr>> { + if pat { + self.sess.gated_spans.gate(sym::inline_const_pat, span); + } else { + self.sess.gated_spans.gate(sym::inline_const, span); + } self.eat_keyword(kw::Const); let blk = self.parse_block()?; let anon_const = AnonConst { @@ -1258,7 +1262,7 @@ /// Parses `pub`, `pub(crate)` and `pub(in path)` plus shortcuts `crate` for `pub(crate)`, /// `pub(self)` for `pub(in self)` and `pub(super)` for `pub(in super)`. /// If the following element can't be a tuple (i.e., it's a function definition), then - /// it's not a tuple struct field), and the contents within the parentheses isn't valid, + /// it's not a tuple struct field), and the contents within the parentheses aren't valid, /// so emit a proper diagnostic. // Public for rustfmt usage. pub fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> {
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index b03b545..bb3947b 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -437,7 +437,7 @@ PatKind::Box(pat) } else if self.check_inline_const(0) { // Parse `const pat` - let const_expr = self.parse_const_block(lo.to(self.token.span))?; + let const_expr = self.parse_const_block(lo.to(self.token.span), true)?; if let Some(re) = self.parse_range_end() { self.parse_pat_range_begin_with(const_expr, re)? @@ -817,7 +817,7 @@ // Ensure the user doesn't receive unhelpful unexpected token errors self.bump(); if self.is_pat_range_end_start(0) { - let _ = self.parse_pat_range_end(); + let _ = self.parse_pat_range_end().map_err(|mut e| e.cancel()); } self.error_inclusive_range_with_extra_equals(span_with_eq); @@ -884,7 +884,7 @@ fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> { if self.check_inline_const(0) { - self.parse_const_block(self.token.span) + self.parse_const_block(self.token.span, true) } else if self.check_path() { let lo = self.token.span; let (qself, path) = if self.eat_lt() {
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index c7d080a..7f8fadb 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs
@@ -3,10 +3,11 @@ use crate::maybe_whole; use rustc_ast::ptr::P; use rustc_ast::token::{self, Token}; -use rustc_ast::{self as ast, AngleBracketedArg, AngleBracketedArgs, ParenthesizedArgs}; -use rustc_ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode}; -use rustc_ast::{GenericArg, GenericArgs}; -use rustc_ast::{Path, PathSegment, QSelf}; +use rustc_ast::{ + self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocTyConstraint, + AssocTyConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, + Path, PathSegment, QSelf, +}; use rustc_errors::{pluralize, Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym, Ident}; @@ -78,7 +79,7 @@ } let qself = QSelf { ty, path_span, position: path.segments.len() }; - self.parse_path_segments(&mut path.segments, style)?; + self.parse_path_segments(&mut path.segments, style, None)?; Ok(( qself, @@ -119,6 +120,10 @@ true } + pub(super) fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> { + self.parse_path_inner(style, None) + } + /// Parses simple paths. /// /// `path = [::] segment+` @@ -129,7 +134,11 @@ /// `a::b::C::<D>` (with disambiguator) /// `Fn(Args)` (without disambiguator) /// `Fn::(Args)` (with disambiguator) - pub(super) fn parse_path(&mut self, style: PathStyle) -> PResult<'a, Path> { + pub(super) fn parse_path_inner( + &mut self, + style: PathStyle, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Path> { maybe_whole!(self, NtPath, |path| { if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some()) { @@ -152,7 +161,7 @@ if self.eat(&token::ModSep) { segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); } - self.parse_path_segments(&mut segments, style)?; + self.parse_path_segments(&mut segments, style, ty_generics)?; Ok(Path { segments, span: lo.to(self.prev_token.span), tokens: None }) } @@ -161,9 +170,10 @@ &mut self, segments: &mut Vec<PathSegment>, style: PathStyle, + ty_generics: Option<&Generics>, ) -> PResult<'a, ()> { loop { - let segment = self.parse_path_segment(style)?; + let segment = self.parse_path_segment(style, ty_generics)?; if style == PathStyle::Expr { // In order to check for trailing angle brackets, we must have finished // recursing (`parse_path_segment` can indirectly call this function), @@ -191,7 +201,11 @@ } } - pub(super) fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> { + pub(super) fn parse_path_segment( + &mut self, + style: PathStyle, + ty_generics: Option<&Generics>, + ) -> PResult<'a, PathSegment> { let ident = self.parse_path_segment_ident()?; let is_args_start = |token: &Token| { matches!( @@ -229,8 +243,11 @@ let lo = self.token.span; let args = if self.eat_lt() { // `<'a, T, A = U>` - let args = - self.parse_angle_args_with_leading_angle_bracket_recovery(style, lo)?; + let args = self.parse_angle_args_with_leading_angle_bracket_recovery( + style, + lo, + ty_generics, + )?; self.expect_gt()?; let span = lo.to(self.prev_token.span); AngleBracketedArgs { args, span }.into() @@ -238,9 +255,9 @@ // `(T, U) -> R` let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?; let inputs_span = lo.to(self.prev_token.span); - let span = ident.span.to(self.prev_token.span); let output = self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?; + let span = ident.span.to(self.prev_token.span); ParenthesizedArgs { span, inputs, inputs_span, output }.into() }; @@ -275,6 +292,7 @@ &mut self, style: PathStyle, lo: Span, + ty_generics: Option<&Generics>, ) -> PResult<'a, Vec<AngleBracketedArg>> { // We need to detect whether there are extra leading left angle brackets and produce an // appropriate error and suggestion. This cannot be implemented by looking ahead at @@ -350,7 +368,7 @@ let snapshot = if is_first_invocation { Some(self.clone()) } else { None }; debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)"); - match self.parse_angle_args() { + match self.parse_angle_args(ty_generics) { Ok(args) => Ok(args), Err(mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => { // Swap `self` with our backup of the parser state before attempting to parse @@ -403,7 +421,7 @@ .emit(); // Try again without unmatched angle bracket characters. - self.parse_angle_args() + self.parse_angle_args(ty_generics) } } Err(e) => Err(e), @@ -412,9 +430,12 @@ /// Parses (possibly empty) list of generic arguments / associated item constraints, /// possibly including trailing comma. - pub(super) fn parse_angle_args(&mut self) -> PResult<'a, Vec<AngleBracketedArg>> { + pub(super) fn parse_angle_args( + &mut self, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Vec<AngleBracketedArg>> { let mut args = Vec::new(); - while let Some(arg) = self.parse_angle_arg()? { + while let Some(arg) = self.parse_angle_arg(ty_generics)? { args.push(arg); if !self.eat(&token::Comma) { if !self.token.kind.should_end_const_arg() { @@ -431,9 +452,12 @@ } /// Parses a single argument in the angle arguments `<...>` of a path segment. - fn parse_angle_arg(&mut self) -> PResult<'a, Option<AngleBracketedArg>> { + fn parse_angle_arg( + &mut self, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Option<AngleBracketedArg>> { let lo = self.token.span; - let arg = self.parse_generic_arg()?; + let arg = self.parse_generic_arg(ty_generics)?; match arg { Some(arg) => { if self.check(&token::Colon) | self.check(&token::Eq) { @@ -476,7 +500,7 @@ /// That is, parse `<term>` in `Item = <term>`. /// Right now, this only admits types in `<term>`. fn parse_assoc_equality_term(&mut self, ident: Ident, eq: Span) -> PResult<'a, P<ast::Ty>> { - let arg = self.parse_generic_arg()?; + let arg = self.parse_generic_arg(None)?; let span = ident.span.to(self.prev_token.span); match arg { Some(GenericArg::Type(ty)) => return Ok(ty), @@ -563,7 +587,10 @@ /// Parse a generic argument in a path segment. /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`. - pub(super) fn parse_generic_arg(&mut self) -> PResult<'a, Option<GenericArg>> { + pub(super) fn parse_generic_arg( + &mut self, + ty_generics: Option<&Generics>, + ) -> PResult<'a, Option<GenericArg>> { let start = self.token.span; let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { // Parse lifetime argument. @@ -580,25 +607,8 @@ return self.recover_const_arg(start, err).map(Some); } } - } else if self.eat_keyword_noexpect(kw::Const) { - // Detect and recover from the old, pre-RFC2000 syntax for const generics. - let mut err = self.struct_span_err( - start, - "expected lifetime, type, or constant, found keyword `const`", - ); - if self.check_const_arg() { - err.span_suggestion_verbose( - start.until(self.token.span), - "the `const` keyword is only needed in the definition of the type", - String::new(), - Applicability::MaybeIncorrect, - ); - err.emit(); - GenericArg::Const(self.parse_const_arg()?) - } else { - let after_kw_const = self.token.span; - return self.recover_const_arg(after_kw_const, err).map(Some); - } + } else if self.token.is_keyword(kw::Const) { + return self.recover_const_param_declaration(ty_generics); } else { return Ok(None); };
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 9ec6eff..01e751e 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -16,7 +16,7 @@ }; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt}; use rustc_ast::{StmtKind, DUMMY_NODE_ID}; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym}; @@ -300,6 +300,12 @@ None => LocalKind::Decl, Some(init) => { if self.eat_keyword(kw::Else) { + if self.token.is_keyword(kw::If) { + // `let...else if`. Emit the same error that `parse_block()` would, + // but explicitly point out that this pattern is not allowed. + let msg = "conditional `else if` is not supported for `let...else`"; + return Err(self.error_block_no_opening_brace_msg(msg)); + } let els = self.parse_block()?; self.check_let_else_init_bool_expr(&init); self.check_let_else_init_trailing_brace(&init); @@ -328,7 +334,7 @@ ), ) .multipart_suggestion( - "wrap the expression in parenthesis", + "wrap the expression in parentheses", suggs, Applicability::MachineApplicable, ) @@ -349,7 +355,7 @@ "right curly brace `}` before `else` in a `let...else` statement not allowed", ) .multipart_suggestion( - "try wrapping the expression in parenthesis", + "try wrapping the expression in parentheses", suggs, Applicability::MachineApplicable, ) @@ -392,10 +398,9 @@ Ok(block) } - fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> { + fn error_block_no_opening_brace_msg(&mut self, msg: &str) -> DiagnosticBuilder<'a> { let sp = self.token.span; - let tok = super::token_descr(&self.token); - let mut e = self.struct_span_err(sp, &format!("expected `{{`, found {}", tok)); + let mut e = self.struct_span_err(sp, msg); let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon; // Check to see if the user has written something like @@ -435,7 +440,13 @@ _ => {} } e.span_label(sp, "expected `{`"); - Err(e) + e + } + + fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> { + let tok = super::token_descr(&self.token); + let msg = format!("expected `{{`, found {}", tok); + Err(self.error_block_no_opening_brace_msg(&msg)) } /// Parses a block. Inner attributes are allowed.
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 9840037..9bfde0e 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -4,9 +4,10 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Token, TokenKind}; -use rustc_ast::{self as ast, BareFnTy, FnRetTy, GenericParam, Lifetime, MutTy, Ty, TyKind}; -use rustc_ast::{GenericBound, GenericBounds, MacCall, Mutability}; -use rustc_ast::{PolyTraitRef, TraitBoundModifier, TraitObjectSyntax}; +use rustc_ast::{ + self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, + MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, +}; use rustc_errors::{pluralize, struct_span_err, Applicability, PResult}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym}; @@ -98,6 +99,20 @@ AllowCVariadic::No, RecoverQPath::Yes, RecoverReturnSign::Yes, + None, + ) + } + + pub(super) fn parse_ty_with_generics_recovery( + &mut self, + ty_params: &Generics, + ) -> PResult<'a, P<Ty>> { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + Some(ty_params), ) } @@ -110,6 +125,7 @@ AllowCVariadic::Yes, RecoverQPath::Yes, RecoverReturnSign::Yes, + None, ) } @@ -125,6 +141,7 @@ AllowCVariadic::No, RecoverQPath::Yes, RecoverReturnSign::Yes, + None, ) } @@ -135,6 +152,7 @@ AllowCVariadic::Yes, RecoverQPath::Yes, RecoverReturnSign::OnlyFatArrow, + None, ) } @@ -152,6 +170,7 @@ AllowCVariadic::No, recover_qpath, recover_return_sign, + None, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -171,6 +190,7 @@ AllowCVariadic::No, recover_qpath, recover_return_sign, + None, )?; FnRetTy::Ty(ty) } else { @@ -184,6 +204,7 @@ allow_c_variadic: AllowCVariadic, recover_qpath: RecoverQPath, recover_return_sign: RecoverReturnSign, + ty_generics: Option<&Generics>, ) -> PResult<'a, P<Ty>> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -233,7 +254,7 @@ let (qself, path) = self.parse_qpath(PathStyle::Type)?; TyKind::Path(Some(qself), path) } else if self.check_path() { - self.parse_path_start_ty(lo, allow_plus)? + self.parse_path_start_ty(lo, allow_plus, ty_generics)? } else if self.can_begin_bound() { self.parse_bare_trait_object(lo, allow_plus)? } else if self.eat(&token::DotDotDot) { @@ -430,7 +451,7 @@ } // Parses the `typeof(EXPR)`. - // To avoid ambiguity, the type is surrounded by parenthesis. + // To avoid ambiguity, the type is surrounded by parentheses. fn parse_typeof_ty(&mut self) -> PResult<'a, TyKind> { self.expect(&token::OpenDelim(token::Paren))?; let expr = self.parse_anon_const_expr()?; @@ -512,9 +533,14 @@ /// 1. a type macro, `mac!(...)`, /// 2. a bare trait object, `B0 + ... + Bn`, /// 3. or a path, `path::to::MyType`. - fn parse_path_start_ty(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> { + fn parse_path_start_ty( + &mut self, + lo: Span, + allow_plus: AllowPlus, + ty_generics: Option<&Generics>, + ) -> PResult<'a, TyKind> { // Simple path - let path = self.parse_path(PathStyle::Type)?; + let path = self.parse_path_inner(PathStyle::Type, ty_generics)?; if self.eat(&token::Not) { // Macro invocation in type position Ok(TyKind::MacCall(MacCall {
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 2aa20d0..4781813 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -5,7 +5,7 @@ use rustc_ast::tokenstream::{DelimSpan, TokenTree}; use rustc_ast::{self as ast, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind}; use rustc_errors::{Applicability, FatalError, PResult}; -use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP}; +use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; use rustc_session::parse::ParseSess; use rustc_span::{sym, Symbol}; @@ -15,14 +15,13 @@ return; } - let attr_info = - attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a); + let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); // Check input tokens for built-in and key-value attributes. match attr_info { // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. - Some((name, _, template, _)) if name != sym::rustc_dummy => { - check_builtin_attribute(sess, attr, name, template) + Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => { + check_builtin_attribute(sess, attr, *name, *template) } _ if let MacArgs::Eq(..) = attr.get_normal_item().args => { // All key-value attributes are restricted to meta-item syntax. @@ -168,7 +167,7 @@ attr: &Attribute, name: Symbol, ) -> ! { - let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").2; + let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").template; emit_malformed_attribute(sess, attr, name, template); // This is fatal, otherwise it will likely cause a cascade of other errors // (and an error here is expected to be very rare).
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e5fbddd..f761eaa 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs
@@ -9,9 +9,9 @@ use rustc_middle::ty::TyCtxt; use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, NestedMetaItem}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability}; -use rustc_feature::{AttributeType, BUILTIN_ATTRIBUTE_MAP}; +use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; @@ -23,6 +23,7 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{MultiSpan, Span, DUMMY_SP}; +use std::collections::hash_map::Entry; pub(crate) fn target_from_impl_item<'tcx>( tcx: TyCtxt<'tcx>, @@ -69,7 +70,7 @@ let mut doc_aliases = FxHashMap::default(); let mut is_valid = true; let mut specified_inline = None; - let mut seen = FxHashSet::default(); + let mut seen = FxHashMap::default(); let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { let attr_is_valid = match attr.name_or_empty() { @@ -112,6 +113,7 @@ self.check_default_method_body_is_const(attr, span, target) } sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target), + sym::must_use => self.check_must_use(hir_id, &attr, span, target), sym::rustc_const_unstable | sym::rustc_const_stable | sym::unstable @@ -147,8 +149,10 @@ _ => {} } + let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); + if hir_id != CRATE_HIR_ID { - if let Some((_, AttributeType::CrateLevel, ..)) = + if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) { self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { @@ -164,21 +168,37 @@ } } - // Duplicate attributes - match attr.name_or_empty() { - name @ sym::macro_use => { - let args = attr.meta_item_list().unwrap_or_else(Vec::new); - let args: Vec<_> = args.iter().map(|arg| arg.name_or_empty()).collect(); - if !seen.insert((name, args)) { - self.tcx.struct_span_lint_hir( - UNUSED_ATTRIBUTES, - hir_id, + if let Some(BuiltinAttribute { duplicates, .. }) = builtin { + check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen); + } + + // Warn on useless empty attributes. + if matches!( + attr.name_or_empty(), + sym::macro_use + | sym::allow + | sym::warn + | sym::deny + | sym::forbid + | sym::feature + | sym::repr + | sym::target_feature + ) && attr.meta_item_list().map_or(false, |list| list.is_empty()) + { + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build("unused attribute") + .span_suggestion( attr.span, - |lint| lint.build("unused attribute").emit(), - ); - } - } - _ => {} + "remove this attribute", + String::new(), + Applicability::MachineApplicable, + ) + .note(&format!( + "attribute `{}` with an empty list has no effect", + attr.name_or_empty() + )) + .emit(); + }); } } @@ -765,7 +785,7 @@ "not a `use` item", ); } - err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#docno_inlinedocinline for more information") + err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information") .emit(); }, ); @@ -962,7 +982,7 @@ } sym::primitive => { - if !self.tcx.features().doc_primitive { + if !self.tcx.features().rustdoc_internals { self.tcx.struct_span_lint_hir( INVALID_DOC_ATTRIBUTES, hir_id, @@ -1046,6 +1066,37 @@ is_valid } + /// Warns against some misuses of `#[must_use]` + fn check_must_use( + &self, + hir_id: HirId, + attr: &Attribute, + span: &Span, + _target: Target, + ) -> bool { + let node = self.tcx.hir().get(hir_id); + if let Some(fn_node) = node.fn_kind() { + if let rustc_hir::IsAsync::Async = fn_node.asyncness() { + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build( + "`must_use` attribute on `async` functions \ + applies to the anonymous `Future` returned by the \ + function, not the value within", + ) + .span_label( + *span, + "this attribute does nothing, the `Future`s \ + returned by async functions are already `must_use`", + ) + .emit(); + }); + } + } + + // For now, its always valid + true + } + /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid. fn check_must_not_suspend(&self, attr: &Attribute, span: &Span, target: Target) -> bool { match target { @@ -1958,3 +2009,77 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_attrs, ..*providers }; } + +fn check_duplicates( + tcx: TyCtxt<'_>, + attr: &Attribute, + hir_id: HirId, + duplicates: AttributeDuplicates, + seen: &mut FxHashMap<Symbol, Span>, +) { + use AttributeDuplicates::*; + if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() { + return; + } + match duplicates { + DuplicatesOk => {} + WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => { + match seen.entry(attr.name_or_empty()) { + Entry::Occupied(mut entry) => { + let (this, other) = if matches!(duplicates, FutureWarnPreceding) { + let to_remove = entry.insert(attr.span); + (to_remove, attr.span) + } else { + (attr.span, *entry.get()) + }; + tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, this, |lint| { + let mut db = lint.build("unused attribute"); + db.span_note(other, "attribute also specified here").span_suggestion( + this, + "remove this attribute", + String::new(), + Applicability::MachineApplicable, + ); + if matches!(duplicates, FutureWarnFollowing | FutureWarnPreceding) { + db.warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ); + } + db.emit(); + }); + } + Entry::Vacant(entry) => { + entry.insert(attr.span); + } + } + } + ErrorFollowing | ErrorPreceding => match seen.entry(attr.name_or_empty()) { + Entry::Occupied(mut entry) => { + let (this, other) = if matches!(duplicates, ErrorPreceding) { + let to_remove = entry.insert(attr.span); + (to_remove, attr.span) + } else { + (attr.span, *entry.get()) + }; + tcx.sess + .struct_span_err( + this, + &format!("multiple `{}` attributes", attr.name_or_empty()), + ) + .span_note(other, "attribute also specified here") + .span_suggestion( + this, + "remove this attribute", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + } + Entry::Vacant(entry) => { + entry.insert(attr.span); + } + }, + } +}
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index a0ceb56..9ccf76b 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs
@@ -173,6 +173,12 @@ None => return true, }; + // If the function belongs to a trait, then it must enable the const_trait_impl + // feature to use that trait function (with a const default body). + if tcx.trait_of_item(def_id).is_some() { + return true; + } + // If this crate is not using stability attributes, or this function is not claiming to be a // stable `const fn`, that is all that is required. if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) { @@ -210,10 +216,10 @@ required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect(); match missing_gates.as_slice() { - &[] => struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit(), + [] => struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit(), - &[missing_primary, ref missing_secondary @ ..] => { - let mut err = feature_err(&tcx.sess.parse_sess, missing_primary, span, &msg); + [missing_primary, ref missing_secondary @ ..] => { + let mut err = feature_err(&tcx.sess.parse_sess, *missing_primary, span, &msg); // If multiple feature gates would be required to enable this expression, include // them as help messages. Don't emit a separate error for each missing feature gate.
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 4adec3c..af1c724 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs
@@ -7,7 +7,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(iter_zip)] #![feature(map_try_insert)] #![feature(min_specialization)]
diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_passes/src/region.rs index 5fc8e23..6a8feb0 100644 --- a/compiler/rustc_passes/src/region.rs +++ b/compiler/rustc_passes/src/region.rs
@@ -334,9 +334,10 @@ // properly, we can't miss any types. match expr.kind { - // Manually recurse over closures, because they are the only + // Manually recurse over closures and inline consts, because they are the only // case of nested bodies that share the parent environment. - hir::ExprKind::Closure(.., body, _, _) => { + hir::ExprKind::Closure(.., body, _, _) + | hir::ExprKind::ConstBlock(hir::AnonConst { body, .. }) => { let body = visitor.tcx.hir().body(body); visitor.visit_body(body); } @@ -817,9 +818,9 @@ } fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { - let closure_base_def_id = tcx.closure_base_def_id(def_id); - if closure_base_def_id != def_id { - return tcx.region_scope_tree(closure_base_def_id); + let typeck_root_def_id = tcx.typeck_root_def_id(def_id); + if typeck_root_def_id != def_id { + return tcx.region_scope_tree(typeck_root_def_id); } let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index ae3a9c7..1166814 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs
@@ -23,7 +23,7 @@ use rustc_middle::thir::abstract_const::Node as ACNode; use rustc_middle::ty::fold::TypeVisitor; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable}; use rustc_session::lint; use rustc_span::hygiene::Transparency; @@ -124,9 +124,11 @@ fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<V::BreakTy> { match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _ }) => { - self.visit_trait(trait_ref) - } + ty::PredicateKind::Trait(ty::TraitPredicate { + trait_ref, + constness: _, + polarity: _, + }) => self.visit_trait(trait_ref), ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, ty }) => { ty.visit_with(self)?; self.visit_projection_ty(projection_ty) @@ -153,11 +155,8 @@ tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, ) -> ControlFlow<V::BreakTy> { - const_evaluatable::walk_abstract_const(tcx, ct, |node| match node.root() { - ACNode::Leaf(leaf) => { - let leaf = leaf.subst(tcx, ct.substs); - self.visit_const(leaf) - } + const_evaluatable::walk_abstract_const(tcx, ct, |node| match node.root(tcx) { + ACNode::Leaf(leaf) => self.visit_const(leaf), ACNode::Cast(_, _, ty) => self.visit_ty(ty), ACNode::Binop(..) | ACNode::UnaryOp(..) | ACNode::FunctionCall(_, _) => { ControlFlow::CONTINUE @@ -544,7 +543,7 @@ module: LocalDefId, ) { let level = Some(AccessLevel::Reachable); - if let ty::Visibility::Public = vis { + if vis.is_public() { self.update(def_id, level); } match def_kind { @@ -581,7 +580,7 @@ DefKind::Struct | DefKind::Union => { // While structs and unions have type privacy, their fields do not. - if let ty::Visibility::Public = vis { + if vis.is_public() { let item = self.tcx.hir().expect_item(self.tcx.hir().local_def_id_to_hir_id(def_id)); if let hir::ItemKind::Struct(ref struct_def, _) @@ -619,6 +618,7 @@ | DefKind::Use | DefKind::ForeignMod | DefKind::AnonConst + | DefKind::InlineConst | DefKind::Field | DefKind::GlobalAsm | DefKind::Impl @@ -933,7 +933,7 @@ let def_id = self.tcx.hir().local_def_id(id); if let Some(exports) = self.tcx.module_exports(def_id) { for export in exports.iter() { - if export.vis == ty::Visibility::Public { + if export.vis.is_public() { if let Some(def_id) = export.res.opt_def_id() { if let Some(def_id) = def_id.as_local() { self.update(def_id, Some(AccessLevel::Exported)); @@ -1918,8 +1918,7 @@ /// 1. It's contained within a public type /// 2. It comes from a private crate fn leaks_private_dep(&self, item_id: DefId) -> bool { - let ret = self.required_visibility == ty::Visibility::Public - && self.tcx.is_private_dep(item_id.krate); + let ret = self.required_visibility.is_public() && self.tcx.is_private_dep(item_id.krate); tracing::debug!("leaks_private_dep(item_id={:?})={}", item_id, ret); ret
diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index 89df3d4..f984bb1 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml
@@ -7,9 +7,8 @@ doctest = false [dependencies] -measureme = "9.0.0" +measureme = "10.0.0" rustc-rayon-core = "0.3.1" -tracing = "0.1" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index e50a8c1..440b6f1 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs
@@ -13,23 +13,19 @@ extern crate rustc_macros; #[macro_use] extern crate rustc_middle; -#[macro_use] -extern crate tracing; -use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_errors::DiagnosticBuilder; -use rustc_middle::dep_graph; +use rustc_middle::arena::Arena; +use rustc_middle::dep_graph::{self, DepKindStruct, SerializedDepNodeIndex}; use rustc_middle::ty::query::{query_keys, query_storage, query_stored, query_values}; -use rustc_middle::ty::query::{Providers, QueryEngine}; +use rustc_middle::ty::query::{ExternProviders, Providers, QueryEngine}; use rustc_middle::ty::{self, TyCtxt}; -use rustc_query_system::ich::StableHashingContext; +use rustc_span::def_id::LocalDefId; use rustc_span::Span; #[macro_use] mod plumbing; pub use plumbing::QueryCtxt; -use plumbing::QueryStruct; use rustc_query_system::query::*; mod stats; @@ -41,9 +37,8 @@ mod values; use self::values::Value; -use rustc_query_system::query::QueryAccessors; pub use rustc_query_system::query::QueryConfig; -pub(crate) use rustc_query_system::query::QueryDescription; +pub(crate) use rustc_query_system::query::{QueryDescription, QueryVtable}; mod on_disk_cache; pub use on_disk_cache::OnDiskCache; @@ -53,6 +48,14 @@ mod util; +fn describe_as_module(def_id: LocalDefId, tcx: TyCtxt<'_>) -> String { + if def_id.is_top_level_module() { + "top-level module".to_string() + } else { + format!("module `{}`", tcx.def_path_str(def_id.to_def_id())) + } +} + rustc_query_append! { [define_queries!][<'tcx>] } impl<'tcx> Queries<'tcx> {
diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 48eb488..552906a 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs
@@ -219,7 +219,7 @@ // Do this *before* we clone 'latest_foreign_def_path_hashes', since // loading existing queries may cause us to create new DepNodes, which // may in turn end up invoking `store_foreign_def_id_hash` - tcx.dep_graph.exec_cache_promotions(QueryCtxt::from_tcx(tcx)); + tcx.dep_graph.exec_cache_promotions(tcx); *self.serialized_data.write() = None; } @@ -358,23 +358,6 @@ Ok(()) }) } - - fn def_path_hash_to_def_id(&self, tcx: TyCtxt<'tcx>, hash: DefPathHash) -> DefId { - debug!("def_path_hash_to_def_id({:?})", hash); - - let stable_crate_id = hash.stable_crate_id(); - - // If this is a DefPathHash from the local crate, we can look up the - // DefId in the tcx's `Definitions`. - if stable_crate_id == tcx.sess.local_stable_crate_id() { - tcx.definitions_untracked().local_def_path_hash_to_def_id(hash).to_def_id() - } else { - // If this is a DefPathHash from an upstream crate, let the CrateStore map - // it to a DefId. - let cnum = tcx.cstore_untracked().stable_crate_id_to_crate_num(stable_crate_id); - tcx.cstore_untracked().def_path_hash_to_def_id(cnum, hash) - } - } } impl<'sess> OnDiskCache<'sess> { @@ -764,7 +747,7 @@ // If we get to this point, then all of the query inputs were green, // which means that the definition with this hash is guaranteed to // still exist in the current compilation session. - Ok(d.tcx().on_disk_cache.as_ref().unwrap().def_path_hash_to_def_id(d.tcx(), def_path_hash)) + Ok(d.tcx().def_path_hash_to_def_id(def_path_hash)) } } @@ -1035,7 +1018,7 @@ ) -> FileEncodeResult where CTX: QueryContext + 'tcx, - Q: super::QueryDescription<CTX> + super::QueryAccessors<CTX>, + Q: super::QueryDescription<CTX>, Q::Value: Encodable<CacheEncoder<'a, 'tcx, FileEncoder>>, { let _timer = tcx @@ -1050,7 +1033,7 @@ if res.is_err() { return; } - if Q::cache_on_disk(tcx, &key, Some(value)) { + if Q::cache_on_disk(*tcx.dep_context(), &key) { let dep_node = SerializedDepNodeIndex::new(dep_node.index()); // Record position of the cache entry.
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 4d1e39d..81a36e0 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -2,20 +2,17 @@ //! generate the actual methods on tcx which find and execute the provider, //! manage the caches, and so forth. -use crate::{on_disk_cache, queries, Queries}; -use rustc_middle::dep_graph::{DepKind, DepNode, DepNodeIndex, SerializedDepNodeIndex}; +use crate::{on_disk_cache, Queries}; +use rustc_middle::dep_graph::{DepKind, DepNodeIndex, SerializedDepNodeIndex}; use rustc_middle::ty::tls::{self, ImplicitCtxt}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_query_system::dep_graph::HasDepContext; -use rustc_query_system::query::{ - QueryContext, QueryDescription, QueryJobId, QueryMap, QuerySideEffects, -}; +use rustc_query_system::query::{QueryContext, QueryJobId, QueryMap, QuerySideEffects}; use rustc_data_structures::sync::Lock; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{Diagnostic, Handler}; use rustc_serialize::opaque; -use rustc_span::def_id::LocalDefId; use std::any::Any; @@ -53,36 +50,6 @@ self.queries.try_collect_active_jobs(**self) } - fn try_load_from_on_disk_cache(&self, dep_node: &DepNode) { - let cb = &super::QUERY_CALLBACKS[dep_node.kind as usize]; - (cb.try_load_from_on_disk_cache)(*self, dep_node) - } - - fn try_force_from_dep_node(&self, dep_node: &DepNode) -> bool { - debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); - - // We must avoid ever having to call `force_from_dep_node()` for a - // `DepNode::codegen_unit`: - // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we - // would always end up having to evaluate the first caller of the - // `codegen_unit` query that *is* reconstructible. This might very well be - // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just - // to re-trigger calling the `codegen_unit` query with the right key. At - // that point we would already have re-done all the work we are trying to - // avoid doing in the first place. - // The solution is simple: Just explicitly call the `codegen_unit` query for - // each CGU, right after partitioning. This way `try_mark_green` will always - // hit the cache instead of having to go through `force_from_dep_node`. - // This assertion makes sure, we actually keep applying the solution above. - debug_assert!( - dep_node.kind != DepKind::codegen_unit, - "calling force_from_dep_node() on DepKind::codegen_unit" - ); - - let cb = &super::QUERY_CALLBACKS[dep_node.kind as usize]; - (cb.force_from_dep_node)(*self, dep_node) - } - // Interactions with on_disk_cache fn load_side_effects(&self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects { self.queries @@ -193,60 +160,6 @@ } } -/// This struct stores metadata about each Query. -/// -/// Information is retrieved by indexing the `QUERIES` array using the integer value -/// of the `DepKind`. Overall, this allows to implement `QueryContext` using this manual -/// jump table instead of large matches. -pub struct QueryStruct { - /// The red/green evaluation system will try to mark a specific DepNode in the - /// dependency graph as green by recursively trying to mark the dependencies of - /// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` - /// where we don't know if it is red or green and we therefore actually have - /// to recompute its value in order to find out. Since the only piece of - /// information that we have at that point is the `DepNode` we are trying to - /// re-evaluate, we need some way to re-run a query from just that. This is what - /// `force_from_dep_node()` implements. - /// - /// In the general case, a `DepNode` consists of a `DepKind` and an opaque - /// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint - /// is usually constructed by computing a stable hash of the query-key that the - /// `DepNode` corresponds to. Consequently, it is not in general possible to go - /// back from hash to query-key (since hash functions are not reversible). For - /// this reason `force_from_dep_node()` is expected to fail from time to time - /// because we just cannot find out, from the `DepNode` alone, what the - /// corresponding query-key is and therefore cannot re-run the query. - /// - /// The system deals with this case letting `try_mark_green` fail which forces - /// the root query to be re-evaluated. - /// - /// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. - /// Fortunately, we can use some contextual information that will allow us to - /// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we - /// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a - /// valid `DefPathHash`. Since we also always build a huge table that maps every - /// `DefPathHash` in the current codebase to the corresponding `DefId`, we have - /// everything we need to re-run the query. - /// - /// Take the `mir_promoted` query as an example. Like many other queries, it - /// just has a single parameter: the `DefId` of the item it will compute the - /// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` - /// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` - /// is actually a `DefPathHash`, and can therefore just look up the corresponding - /// `DefId` in `tcx.def_path_hash_to_def_id`. - /// - /// When you implement a new query, it will likely have a corresponding new - /// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As - /// a rule of thumb, if your query takes a `DefId` or `LocalDefId` as sole parameter, - /// then `force_from_dep_node()` should not fail for it. Otherwise, you can just - /// add it to the "We don't have enough information to reconstruct..." group in - /// the match below. - pub(crate) force_from_dep_node: fn(tcx: QueryCtxt<'_>, dep_node: &DepNode) -> bool, - - /// Invoke a query to put the on-disk cached value in memory. - pub(crate) try_load_from_on_disk_cache: fn(QueryCtxt<'_>, &DepNode), -} - macro_rules! handle_cycle_error { ([][$tcx: expr, $error:expr]) => {{ $error.emit(); @@ -291,14 +204,30 @@ } macro_rules! hash_result { - ([][$hcx:expr, $result:expr]) => {{ - dep_graph::hash_result($hcx, &$result) + ([]) => {{ + Some(dep_graph::hash_result) }}; - ([(no_hash) $($rest:tt)*][$hcx:expr, $result:expr]) => {{ + ([(no_hash) $($rest:tt)*]) => {{ None }}; + ([$other:tt $($modifiers:tt)*]) => { + hash_result!([$($modifiers)*]) + }; +} + +macro_rules! get_provider { + ([][$tcx:expr, $name:ident, $key:expr]) => {{ + $tcx.queries.local_providers.$name + }}; + ([(separate_provide_extern) $($rest:tt)*][$tcx:expr, $name:ident, $key:expr]) => {{ + if $key.query_crate_is_local() { + $tcx.queries.local_providers.$name + } else { + $tcx.queries.extern_providers.$name + } + }}; ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { - hash_result!([$($modifiers)*][$($args)*]) + get_provider!([$($modifiers)*][$($args)*]) }; } @@ -374,10 +303,8 @@ const NAME: &'static str = stringify!($name); } - impl<$tcx> QueryAccessors<QueryCtxt<$tcx>> for queries::$name<$tcx> { - const ANON: bool = is_anon!([$($modifiers)*]); - const EVAL_ALWAYS: bool = is_eval_always!([$($modifiers)*]); - const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$name; + impl<$tcx> QueryDescription<QueryCtxt<$tcx>> for queries::$name<$tcx> { + rustc_query_description! { $name<$tcx> } type Cache = query_storage::$name<$tcx>; @@ -396,32 +323,25 @@ } #[inline] - fn compute_fn(tcx: QueryCtxt<'tcx>, key: &Self::Key) -> - fn(TyCtxt<'tcx>, Self::Key) -> Self::Value + fn make_vtable(tcx: QueryCtxt<'tcx>, key: &Self::Key) -> + QueryVtable<QueryCtxt<$tcx>, Self::Key, Self::Value> { - if key.query_crate_is_local() { - tcx.queries.local_providers.$name - } else { - tcx.queries.extern_providers.$name + let compute = get_provider!([$($modifiers)*][tcx, $name, key]); + let cache_on_disk = Self::cache_on_disk(tcx.tcx, key); + QueryVtable { + anon: is_anon!([$($modifiers)*]), + eval_always: is_eval_always!([$($modifiers)*]), + dep_kind: dep_graph::DepKind::$name, + hash_result: hash_result!([$($modifiers)*]), + handle_cycle_error: |tcx, mut error| handle_cycle_error!([$($modifiers)*][tcx, error]), + compute, + cache_on_disk, + try_load_from_disk: Self::TRY_LOAD_FROM_DISK, } } - - fn hash_result( - _hcx: &mut StableHashingContext<'_>, - _result: &Self::Value - ) -> Option<Fingerprint> { - hash_result!([$($modifiers)*][_hcx, _result]) - } - - fn handle_cycle_error( - tcx: QueryCtxt<'tcx>, - mut error: DiagnosticBuilder<'_>, - ) -> Self::Value { - handle_cycle_error!([$($modifiers)*][tcx, error]) - } })* - #[allow(non_upper_case_globals)] + #[allow(nonstandard_style)] pub mod query_callbacks { use super::*; use rustc_middle::dep_graph::DepNode; @@ -431,68 +351,100 @@ use rustc_query_system::dep_graph::FingerprintStyle; // We use this for most things when incr. comp. is turned off. - pub const Null: QueryStruct = QueryStruct { - force_from_dep_node: |_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node), - try_load_from_on_disk_cache: |_, _| {}, - }; + pub fn Null() -> 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 const TraitSelect: QueryStruct = QueryStruct { - force_from_dep_node: |_, _| false, - try_load_from_on_disk_cache: |_, _| {}, - }; + pub fn TraitSelect() -> DepKindStruct { + DepKindStruct { + is_anon: true, + is_eval_always: false, + fingerprint_style: FingerprintStyle::Unit, + force_from_dep_node: None, + try_load_from_on_disk_cache: None, + } + } - pub const CompileCodegenUnit: QueryStruct = QueryStruct { - force_from_dep_node: |_, _| false, - try_load_from_on_disk_cache: |_, _| {}, - }; + pub fn CompileCodegenUnit() -> DepKindStruct { + DepKindStruct { + is_anon: false, + is_eval_always: false, + fingerprint_style: FingerprintStyle::Opaque, + force_from_dep_node: None, + try_load_from_on_disk_cache: None, + } + } - pub const CompileMonoItem: QueryStruct = QueryStruct { - force_from_dep_node: |_, _| false, - try_load_from_on_disk_cache: |_, _| {}, - }; + pub fn CompileMonoItem() -> DepKindStruct { + DepKindStruct { + is_anon: false, + is_eval_always: false, + fingerprint_style: FingerprintStyle::Opaque, + force_from_dep_node: None, + try_load_from_on_disk_cache: None, + } + } - $(pub const $name: QueryStruct = { - const is_anon: bool = is_anon!([$($modifiers)*]); + $(pub fn $name()-> DepKindStruct { + let is_anon = is_anon!([$($modifiers)*]); + let is_eval_always = is_eval_always!([$($modifiers)*]); + + let fingerprint_style = + <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>>::fingerprint_style(); + + if is_anon || !fingerprint_style.reconstructible() { + return DepKindStruct { + is_anon, + is_eval_always, + fingerprint_style, + force_from_dep_node: None, + try_load_from_on_disk_cache: None, + } + } #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>> - ::fingerprint_style() + fn recover<'tcx>(tcx: TyCtxt<'tcx>, dep_node: DepNode) -> Option<query_keys::$name<'tcx>> { + <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, &dep_node) } - fn recover<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<query_keys::$name<'tcx>> { - <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node) - } - - fn force_from_dep_node(tcx: QueryCtxt<'_>, dep_node: &DepNode) -> bool { - force_query::<queries::$name<'_>, _>(tcx, dep_node) - } - - fn try_load_from_on_disk_cache(tcx: QueryCtxt<'_>, dep_node: &DepNode) { - if is_anon { - return + fn force_from_dep_node(tcx: TyCtxt<'_>, dep_node: DepNode) -> bool { + if let Some(key) = recover(tcx, dep_node) { + let tcx = QueryCtxt::from_tcx(tcx); + force_query::<queries::$name<'_>, _>(tcx, key, dep_node); + true + } else { + false } + } - if !fingerprint_style().reconstructible() { - return - } + fn try_load_from_on_disk_cache(tcx: TyCtxt<'_>, dep_node: DepNode) { + debug_assert!(tcx.dep_graph.is_green(&dep_node)); - debug_assert!(tcx.dep_graph.is_green(dep_node)); - - let key = recover(*tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); - if queries::$name::cache_on_disk(tcx, &key, None) { + let key = recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); + if queries::$name::cache_on_disk(tcx, &key) { let _ = tcx.$name(key); } } - QueryStruct { - force_from_dep_node, - try_load_from_on_disk_cache, + DepKindStruct { + is_anon, + is_eval_always, + fingerprint_style, + force_from_dep_node: Some(force_from_dep_node), + try_load_from_on_disk_cache: Some(try_load_from_on_disk_cache), } - };)* + })* } - static QUERY_CALLBACKS: &[QueryStruct] = &make_dep_kind_array!(query_callbacks); + pub fn query_callbacks<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindStruct] { + arena.alloc_from_iter(make_dep_kind_array!(query_callbacks)) + } } } @@ -504,7 +456,7 @@ input: ($(([$($modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => { pub struct Queries<$tcx> { local_providers: Box<Providers>, - extern_providers: Box<Providers>, + extern_providers: Box<ExternProviders>, pub on_disk_cache: Option<OnDiskCache<$tcx>>, @@ -517,7 +469,7 @@ impl<$tcx> Queries<$tcx> { pub fn new( local_providers: Providers, - extern_providers: Providers, + extern_providers: ExternProviders, on_disk_cache: Option<OnDiskCache<$tcx>>, ) -> Self { Queries { @@ -575,13 +527,3 @@ } }; } - -fn describe_as_module(def_id: LocalDefId, tcx: TyCtxt<'_>) -> String { - if def_id.is_top_level_module() { - "top-level module".to_string() - } else { - format!("module `{}`", tcx.def_path_str(def_id.to_def_id())) - } -} - -rustc_query_description! {}
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 8602219..c274c2c 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs
@@ -60,8 +60,11 @@ /// Creates a new, parameterless DepNode. This method will assert /// that the DepNode corresponding to the given DepKind actually /// does not require any parameters. - pub fn new_no_params(kind: K) -> DepNode<K> { - debug_assert!(!kind.has_params()); + pub fn new_no_params<Ctxt>(tcx: Ctxt, kind: K) -> DepNode<K> + where + Ctxt: super::DepContext<DepKind = K>, + { + debug_assert_eq!(tcx.fingerprint_style(kind), FingerprintStyle::Unit); DepNode { kind, hash: Fingerprint::ZERO.into() } } @@ -75,7 +78,7 @@ #[cfg(debug_assertions)] { - if !kind.fingerprint_style().reconstructible() + if !tcx.fingerprint_style(kind).reconstructible() && (tcx.sess().opts.debugging_opts.incremental_info || tcx.sess().opts.debugging_opts.query_dep_graph) { @@ -121,11 +124,12 @@ where T: for<'a> HashStable<StableHashingContext<'a>> + fmt::Debug, { - #[inline] + #[inline(always)] default fn fingerprint_style() -> FingerprintStyle { FingerprintStyle::Opaque } + #[inline(always)] default fn to_fingerprint(&self, tcx: Ctxt) -> Fingerprint { let mut hcx = tcx.create_stable_hashing_context(); let mut hasher = StableHasher::new(); @@ -135,10 +139,12 @@ hasher.finish() } + #[inline(always)] default fn to_debug_str(&self, _: Ctxt) -> String { format!("{:?}", *self) } + #[inline(always)] default fn recover(_: Ctxt, _: &DepNode<Ctxt::DepKind>) -> Option<Self> { None }
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 114d12f..a8be1ca 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -33,12 +33,6 @@ /// each task has a `DepNodeIndex` that uniquely identifies it. This unique /// ID is used for self-profiling. virtual_dep_node_index: Lrc<AtomicU32>, - - /// The cached event id for profiling node interning. This saves us - /// from having to look up the event id every time we intern a node - /// which may incur too much overhead. - /// This will be None if self-profiling is disabled. - node_intern_event_id: Option<EventId>, } rustc_index::newtype_index! { @@ -96,14 +90,13 @@ dep_node_debug: Lock<FxHashMap<DepNode<K>, String>>, } -pub fn hash_result<R>(hcx: &mut StableHashingContext<'_>, result: &R) -> Option<Fingerprint> +pub fn hash_result<R>(hcx: &mut StableHashingContext<'_>, result: &R) -> Fingerprint where R: for<'a> HashStable<StableHashingContext<'a>>, { let mut stable_hasher = StableHasher::new(); result.hash_stable(hcx, &mut stable_hasher); - - Some(stable_hasher.finish()) + stable_hasher.finish() } impl<K: DepKind> DepGraph<K> { @@ -117,8 +110,13 @@ ) -> DepGraph<K> { let prev_graph_node_count = prev_graph.node_count(); - let current = - CurrentDepGraph::new(prev_graph_node_count, encoder, record_graph, record_stats); + let current = CurrentDepGraph::new( + profiler, + prev_graph_node_count, + encoder, + record_graph, + record_stats, + ); // Instantiate a dependy-less node only once for anonymous queries. let _green_node_index = current.intern_new_node( @@ -129,10 +127,6 @@ ); debug_assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE); - let node_intern_event_id = profiler - .get_or_alloc_cached_string("incr_comp_intern_dep_graph_node") - .map(EventId::from_label); - DepGraph { data: Some(Lrc::new(DepGraphData { previous_work_products: prev_work_products, @@ -143,16 +137,11 @@ colors: DepNodeColorMap::new(prev_graph_node_count), })), virtual_dep_node_index: Lrc::new(AtomicU32::new(0)), - node_intern_event_id, } } pub fn new_disabled() -> DepGraph<K> { - DepGraph { - data: None, - virtual_dep_node_index: Lrc::new(AtomicU32::new(0)), - node_intern_event_id: None, - } + DepGraph { data: None, virtual_dep_node_index: Lrc::new(AtomicU32::new(0)) } } /// Returns `true` if we are actually building the full dep-graph, and `false` otherwise. @@ -215,7 +204,7 @@ cx: Ctxt, arg: A, task: fn(Ctxt, A) -> R, - hash_result: fn(&mut StableHashingContext<'_>, &R) -> Option<Fingerprint>, + hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>, ) -> (R, DepNodeIndex) { if self.is_fully_enabled() { self.with_task_impl(key, cx, arg, task, hash_result) @@ -234,7 +223,7 @@ cx: Ctxt, arg: A, task: fn(Ctxt, A) -> R, - hash_result: fn(&mut StableHashingContext<'_>, &R) -> Option<Fingerprint>, + hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>, ) -> (R, DepNodeIndex) { // This function is only called when the graph is enabled. let data = self.data.as_ref().unwrap(); @@ -253,7 +242,7 @@ key ); - let task_deps = if key.kind.is_eval_always() { + let task_deps = if cx.dep_context().is_eval_always(key.kind) { None } else { Some(Lock::new(TaskDeps { @@ -268,15 +257,14 @@ let edges = task_deps.map_or_else(|| smallvec![], |lock| lock.into_inner().reads); let dcx = cx.dep_context(); - let mut hcx = dcx.create_stable_hashing_context(); let hashing_timer = dcx.profiler().incr_result_hashing(); - let current_fingerprint = hash_result(&mut hcx, &result); + let current_fingerprint = hash_result.map(|f| { + let mut hcx = dcx.create_stable_hashing_context(); + f(&mut hcx, &result) + }); let print_status = cfg!(debug_assertions) && dcx.sess().opts.debugging_opts.dep_tasks; - // Get timer for profiling `DepNode` interning - let node_intern_timer = - self.node_intern_event_id.map(|eid| dcx.profiler().generic_activity_with_event_id(eid)); // Intern the new `DepNode`. let (dep_node_index, prev_and_color) = data.current.intern_node( dcx.profiler(), @@ -286,7 +274,6 @@ current_fingerprint, print_status, ); - drop(node_intern_timer); hashing_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -315,7 +302,7 @@ where OP: FnOnce() -> R, { - debug_assert!(!dep_kind.is_eval_always()); + debug_assert!(!cx.is_eval_always(dep_kind)); if let Some(ref data) = self.data { let task_deps = Lock::new(TaskDeps::default()); @@ -492,7 +479,7 @@ tcx: Ctxt, dep_node: &DepNode<K>, ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> { - debug_assert!(!dep_node.kind.is_eval_always()); + debug_assert!(!tcx.dep_context().is_eval_always(dep_node.kind)); // Return None if the dep graph is disabled let data = self.data.as_ref()?; @@ -552,7 +539,7 @@ // We don't know the state of this dependency. If it isn't // an eval_always node, let's try to mark it green recursively. - if !dep_dep_node.kind.is_eval_always() { + if !tcx.dep_context().is_eval_always(dep_dep_node.kind) { debug!( "try_mark_previous_green({:?}) --- state of dependency {:?} ({}) \ is unknown, trying to mark it green", @@ -575,7 +562,7 @@ "try_mark_previous_green({:?}) --- trying to force dependency {:?}", dep_node, dep_dep_node ); - if !tcx.try_force_from_dep_node(dep_dep_node) { + if !tcx.dep_context().try_force_from_dep_node(*dep_dep_node) { // The DepNode could not be forced. debug!( "try_mark_previous_green({:?}) - END - dependency {:?} could not be forced", @@ -642,7 +629,7 @@ } // We never try to mark eval_always nodes as green - debug_assert!(!dep_node.kind.is_eval_always()); + debug_assert!(!tcx.dep_context().is_eval_always(dep_node.kind)); debug_assert_eq!(data.previous.index_to_node(prev_dep_node_index), *dep_node); @@ -740,8 +727,7 @@ // // This method will only load queries that will end up in the disk cache. // Other queries will not be executed. - pub fn exec_cache_promotions<Ctxt: QueryContext<DepKind = K>>(&self, qcx: Ctxt) { - let tcx = qcx.dep_context(); + pub fn exec_cache_promotions<Ctxt: DepContext<DepKind = K>>(&self, tcx: Ctxt) { let _prof_timer = tcx.profiler().generic_activity("incr_comp_query_cache_promotion"); let data = self.data.as_ref().unwrap(); @@ -749,7 +735,7 @@ match data.colors.get(prev_index) { Some(DepNodeColor::Green(_)) => { let dep_node = data.previous.index_to_node(prev_index); - qcx.try_load_from_on_disk_cache(&dep_node); + tcx.try_load_from_on_disk_cache(dep_node); } None | Some(DepNodeColor::Red) => { // We can skip red nodes because a node can only be marked @@ -876,10 +862,17 @@ /// debugging and only active with `debug_assertions`. total_read_count: AtomicU64, total_duplicate_read_count: AtomicU64, + + /// The cached event id for profiling node interning. This saves us + /// from having to look up the event id every time we intern a node + /// which may incur too much overhead. + /// This will be None if self-profiling is disabled. + node_intern_event_id: Option<EventId>, } impl<K: DepKind> CurrentDepGraph<K> { fn new( + profiler: &SelfProfilerRef, prev_graph_node_count: usize, encoder: FileEncoder, record_graph: bool, @@ -908,6 +901,10 @@ let new_node_count_estimate = 102 * prev_graph_node_count / 100 + 200; + let node_intern_event_id = profiler + .get_or_alloc_cached_string("incr_comp_intern_dep_graph_node") + .map(EventId::from_label); + CurrentDepGraph { encoder: Steal::new(GraphEncoder::new( encoder, @@ -927,6 +924,7 @@ forbidden_edge, total_read_count: AtomicU64::new(0), total_duplicate_read_count: AtomicU64::new(0), + node_intern_event_id, } } @@ -970,6 +968,10 @@ ) -> (DepNodeIndex, Option<(SerializedDepNodeIndex, DepNodeColor)>) { let print_status = cfg!(debug_assertions) && print_status; + // Get timer for profiling `DepNode` interning + let _node_intern_timer = + self.node_intern_event_id.map(|eid| profiler.generic_activity_with_event_id(eid)); + if let Some(prev_index) = prev_graph.node_to_index_opt(&key) { // Determine the color and index of the new `DepNode`. if let Some(fingerprint) = fingerprint {
diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index dcda572..047fc9f 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs
@@ -32,6 +32,17 @@ /// Access the compiler session. fn sess(&self) -> &Session; + + /// Return whether this kind always require evaluation. + fn is_eval_always(&self, kind: Self::DepKind) -> bool; + + fn fingerprint_style(&self, kind: Self::DepKind) -> FingerprintStyle; + + /// Try to force a dep node to execute and see if it's green. + fn try_force_from_dep_node(&self, dep_node: DepNode<Self::DepKind>) -> bool; + + /// Load data from the on-disk cache. + fn try_load_from_on_disk_cache(&self, dep_node: DepNode<Self::DepKind>); } pub trait HasDepContext: Copy { @@ -51,7 +62,7 @@ } /// Describes the contents of the fingerprint generated by a given query. -#[derive(PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum FingerprintStyle { /// The fingerprint is actually a DefPathHash. DefPathHash, @@ -75,12 +86,6 @@ pub trait DepKind: Copy + fmt::Debug + Eq + Hash + Send + Encodable<FileEncoder> + 'static { const NULL: Self; - /// Return whether this kind always require evaluation. - fn is_eval_always(&self) -> bool; - - /// Return whether this kind requires additional parameters to be executed. - fn has_params(&self) -> bool; - /// Implementation of `std::fmt::Debug` for `DepNode`. fn debug_node(node: &DepNode<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result; @@ -93,6 +98,4 @@ fn read_deps<OP>(op: OP) where OP: for<'a> FnOnce(Option<&'a Lock<TaskDeps<Self>>>); - - fn fingerprint_style(&self) -> FingerprintStyle; }
diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index f5f67fc..47197a1 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs
@@ -222,7 +222,7 @@ index } - fn finish(self) -> FileEncodeResult { + fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult { let Self { mut encoder, total_node_count, total_edge_count, result, stats: _ } = self; let () = result?; @@ -235,7 +235,11 @@ IntEncodedWithFixedSize(edge_count).encode(&mut encoder)?; debug!("position: {:?}", encoder.position()); // Drop the encoder so that nothing is written after the counts. - encoder.flush() + let result = encoder.flush(); + // FIXME(rylev): we hardcode the dep graph file name so we don't need a dependency on + // rustc_incremental just for that. + profiler.artifact_size("dep_graph", "dep-graph.bin", encoder.position() as u64); + result } } @@ -332,6 +336,6 @@ pub fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult { let _prof_timer = profiler.generic_activity("incr_comp_encode_dep_graph"); - self.status.into_inner().finish() + self.status.into_inner().finish(profiler) } }
diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index f2e935c..5f31fa0 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs
@@ -1,6 +1,7 @@ use crate::ich; use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; @@ -27,7 +28,6 @@ cstore: &'a dyn CrateStore, pub(super) body_resolver: BodyResolver<'a>, hash_spans: bool, - hash_bodies: bool, pub(super) node_id_hashing_mode: NodeIdHashingMode, // Very often, we are hashing something that does not need the @@ -46,24 +46,19 @@ /// We could also just store a plain reference to the `hir::Crate` but we want /// to avoid that the crate is used to get untracked access to all of the HIR. #[derive(Clone, Copy)] -pub(super) struct BodyResolver<'tcx>(&'tcx hir::Crate<'tcx>); - -impl<'tcx> BodyResolver<'tcx> { - /// Returns a reference to the `hir::Body` with the given `BodyId`. - /// **Does not do any tracking**; use carefully. - pub(super) fn body(self, id: hir::BodyId) -> &'tcx hir::Body<'tcx> { - self.0.body(id) - } +pub(super) enum BodyResolver<'tcx> { + Forbidden, + Traverse { + hash_bodies: bool, + owner: LocalDefId, + bodies: &'tcx SortedMap<hir::ItemLocalId, &'tcx hir::Body<'tcx>>, + }, } impl<'a> StableHashingContext<'a> { - /// The `krate` here is only used for mapping `BodyId`s to `Body`s. - /// Don't use it for anything else or you'll run the risk of - /// leaking data out of the tracking system. #[inline] fn new_with_or_without_spans( sess: &'a Session, - krate: &'a hir::Crate<'a>, definitions: &'a Definitions, cstore: &'a dyn CrateStore, always_ignore_spans: bool, @@ -72,13 +67,12 @@ !always_ignore_spans && !sess.opts.debugging_opts.incremental_ignore_spans; StableHashingContext { - body_resolver: BodyResolver(krate), + body_resolver: BodyResolver::Forbidden, definitions, cstore, caching_source_map: None, raw_source_map: sess.source_map(), hash_spans: hash_spans_initial, - hash_bodies: true, node_id_hashing_mode: NodeIdHashingMode::HashDefPath, } } @@ -86,13 +80,11 @@ #[inline] pub fn new( sess: &'a Session, - krate: &'a hir::Crate<'a>, definitions: &'a Definitions, cstore: &'a dyn CrateStore, ) -> Self { Self::new_with_or_without_spans( sess, - krate, definitions, cstore, /*always_ignore_spans=*/ false, @@ -102,20 +94,41 @@ #[inline] pub fn ignore_spans( sess: &'a Session, - krate: &'a hir::Crate<'a>, definitions: &'a Definitions, cstore: &'a dyn CrateStore, ) -> Self { let always_ignore_spans = true; - Self::new_with_or_without_spans(sess, krate, definitions, cstore, always_ignore_spans) + Self::new_with_or_without_spans(sess, definitions, cstore, always_ignore_spans) + } + + /// Allow hashing + #[inline] + pub fn while_hashing_hir_bodies(&mut self, hb: bool, f: impl FnOnce(&mut Self)) { + let prev = match &mut self.body_resolver { + BodyResolver::Forbidden => panic!("Hashing HIR bodies is forbidden."), + BodyResolver::Traverse { ref mut hash_bodies, .. } => { + std::mem::replace(hash_bodies, hb) + } + }; + f(self); + match &mut self.body_resolver { + BodyResolver::Forbidden => unreachable!(), + BodyResolver::Traverse { ref mut hash_bodies, .. } => *hash_bodies = prev, + } } #[inline] - pub fn while_hashing_hir_bodies<F: FnOnce(&mut Self)>(&mut self, hash_bodies: bool, f: F) { - let prev_hash_bodies = self.hash_bodies; - self.hash_bodies = hash_bodies; + pub fn with_hir_bodies( + &mut self, + hash_bodies: bool, + owner: LocalDefId, + bodies: &'a SortedMap<hir::ItemLocalId, &'a hir::Body<'a>>, + f: impl FnOnce(&mut Self), + ) { + let prev = self.body_resolver; + self.body_resolver = BodyResolver::Traverse { hash_bodies, owner, bodies }; f(self); - self.hash_bodies = prev_hash_bodies; + self.body_resolver = prev; } #[inline] @@ -153,11 +166,6 @@ } #[inline] - pub fn hash_bodies(&self) -> bool { - self.hash_bodies - } - - #[inline] pub fn source_map(&mut self) -> &mut CachingSourceMapView<'a> { match self.caching_source_map { Some(ref mut sm) => sm,
diff --git a/compiler/rustc_query_system/src/ich/impls_hir.rs b/compiler/rustc_query_system/src/ich/impls_hir.rs index 04eb263..3a0aab8 100644 --- a/compiler/rustc_query_system/src/ich/impls_hir.rs +++ b/compiler/rustc_query_system/src/ich/impls_hir.rs
@@ -1,12 +1,11 @@ //! This module contains `HashStable` implementations for various HIR data //! types in no particular order. +use crate::ich::hcx::BodyResolver; use crate::ich::{NodeIdHashingMode, StableHashingContext}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_hir as hir; -use rustc_hir::definitions::DefPathHash; -use smallvec::SmallVec; use std::mem; impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { @@ -29,8 +28,13 @@ #[inline] fn hash_body_id(&mut self, id: hir::BodyId, hasher: &mut StableHasher) { let hcx = self; - if hcx.hash_bodies() { - hcx.body_resolver.body(id).hash_stable(hcx, hasher); + match hcx.body_resolver { + BodyResolver::Forbidden => panic!("Hashing HIR bodies is forbidden."), + BodyResolver::Traverse { hash_bodies: false, .. } => {} + BodyResolver::Traverse { hash_bodies: true, owner, bodies } => { + assert_eq!(id.hir_id.owner, owner); + bodies[&id.hir_id.local_id].hash_stable(hcx, hasher); + } } } @@ -115,6 +119,16 @@ self.node_id_hashing_mode = prev_hash_node_ids; } + + #[inline] + fn hash_hir_trait_candidate(&mut self, tc: &hir::TraitCandidate, hasher: &mut StableHasher) { + self.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + let hir::TraitCandidate { def_id, import_ids } = tc; + + def_id.hash_stable(hcx, hasher); + import_ids.hash_stable(hcx, hasher); + }); + } } impl<'a> HashStable<StableHashingContext<'a>> for hir::Body<'_> { @@ -129,27 +143,3 @@ }); } } - -impl<'a> HashStable<StableHashingContext<'a>> for hir::TraitCandidate { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - let hir::TraitCandidate { def_id, import_ids } = self; - - def_id.hash_stable(hcx, hasher); - import_ids.hash_stable(hcx, hasher); - }); - } -} - -impl<'a> ToStableHashKey<StableHashingContext<'a>> for hir::TraitCandidate { - type KeyType = (DefPathHash, SmallVec<[DefPathHash; 1]>); - - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Self::KeyType { - let hir::TraitCandidate { def_id, import_ids } = self; - - ( - hcx.def_path_hash(*def_id), - import_ids.iter().map(|def_id| hcx.local_def_path_hash(*def_id)).collect(), - ) - } -}
diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index bc23de0..b1295ba 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs
@@ -3,8 +3,10 @@ #![feature(core_intrinsics)] #![feature(hash_raw_entry)] #![feature(iter_zip)] +#![feature(let_else)] #![feature(min_specialization)] #![feature(thread_local_const_init)] +#![feature(extern_types)] #[macro_use] extern crate tracing;
diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index 76a165e..d2b102b 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs
@@ -19,15 +19,16 @@ type Stored: Clone; } -pub(crate) struct QueryVtable<CTX: QueryContext, K, V> { +pub struct QueryVtable<CTX: QueryContext, K, V> { pub anon: bool, pub dep_kind: CTX::DepKind, pub eval_always: bool, + pub cache_on_disk: bool, - pub hash_result: fn(&mut StableHashingContext<'_>, &V) -> Option<Fingerprint>, + pub compute: fn(CTX::DepContext, K) -> V, + pub hash_result: Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>, pub handle_cycle_error: fn(CTX, DiagnosticBuilder<'_>) -> V, - pub cache_on_disk: fn(CTX, &K, Option<&V>) -> bool, - pub try_load_from_disk: fn(CTX, SerializedDepNodeIndex) -> Option<V>, + pub try_load_from_disk: Option<fn(CTX, SerializedDepNodeIndex) -> Option<V>>, } impl<CTX: QueryContext, K, V> QueryVtable<CTX, K, V> { @@ -38,30 +39,25 @@ DepNode::construct(tcx, self.dep_kind, key) } - pub(crate) fn hash_result( - &self, - hcx: &mut StableHashingContext<'_>, - value: &V, - ) -> Option<Fingerprint> { - (self.hash_result)(hcx, value) - } - - pub(crate) fn cache_on_disk(&self, tcx: CTX, key: &K, value: Option<&V>) -> bool { - (self.cache_on_disk)(tcx, key, value) + pub(crate) fn compute(&self, tcx: CTX::DepContext, key: K) -> V { + (self.compute)(tcx, key) } pub(crate) fn try_load_from_disk(&self, tcx: CTX, index: SerializedDepNodeIndex) -> Option<V> { - (self.try_load_from_disk)(tcx, index) + self.try_load_from_disk + .expect("QueryDescription::load_from_disk() called for an unsupported query.")( + tcx, index, + ) } } -pub trait QueryAccessors<CTX: QueryContext>: QueryConfig { - const ANON: bool; - const EVAL_ALWAYS: bool; - const DEP_KIND: CTX::DepKind; +pub trait QueryDescription<CTX: QueryContext>: QueryConfig { + const TRY_LOAD_FROM_DISK: Option<fn(CTX, SerializedDepNodeIndex) -> Option<Self::Value>>; type Cache: QueryCache<Key = Self::Key, Stored = Self::Stored, Value = Self::Value>; + fn describe(tcx: CTX, key: Self::Key) -> String; + // Don't use this method to access query results, instead use the methods on TyCtxt fn query_state<'a>(tcx: CTX) -> &'a QueryState<CTX::DepKind, Self::Key> where @@ -73,43 +69,7 @@ CTX: 'a; // Don't use this method to compute query results, instead use the methods on TyCtxt - fn compute_fn(tcx: CTX, key: &Self::Key) -> fn(CTX::DepContext, Self::Key) -> Self::Value; + fn make_vtable(tcx: CTX, key: &Self::Key) -> QueryVtable<CTX, Self::Key, Self::Value>; - fn hash_result(hcx: &mut StableHashingContext<'_>, result: &Self::Value) - -> Option<Fingerprint>; - - fn handle_cycle_error(tcx: CTX, diag: DiagnosticBuilder<'_>) -> Self::Value; -} - -pub trait QueryDescription<CTX: QueryContext>: QueryAccessors<CTX> { - fn describe(tcx: CTX, key: Self::Key) -> String; - - #[inline] - fn cache_on_disk(_: CTX, _: &Self::Key, _: Option<&Self::Value>) -> bool { - false - } - - fn try_load_from_disk(_: CTX, _: SerializedDepNodeIndex) -> Option<Self::Value> { - panic!("QueryDescription::load_from_disk() called for an unsupported query.") - } -} - -pub(crate) trait QueryVtableExt<CTX: QueryContext, K, V> { - const VTABLE: QueryVtable<CTX, K, V>; -} - -impl<CTX, Q> QueryVtableExt<CTX, Q::Key, Q::Value> for Q -where - CTX: QueryContext, - Q: QueryDescription<CTX>, -{ - const VTABLE: QueryVtable<CTX, Q::Key, Q::Value> = QueryVtable { - anon: Q::ANON, - dep_kind: Q::DEP_KIND, - eval_always: Q::EVAL_ALWAYS, - hash_result: Q::hash_result, - handle_cycle_error: Q::handle_cycle_error, - cache_on_disk: Q::cache_on_disk, - try_load_from_disk: Q::try_load_from_disk, - }; + fn cache_on_disk(tcx: CTX::DepContext, key: &Self::Key) -> bool; }
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 98b2a45..bd67303 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs
@@ -644,9 +644,7 @@ if Some(i) == num_frames { break; } - let query_info = if let Some(info) = query_map.as_ref().and_then(|map| map.get(&query)) { - info - } else { + let Some(query_info) = query_map.as_ref().and_then(|map| map.get(&query)) else { break; }; let mut diag = Diagnostic::new(
diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index dffe7f3..a2f7843 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs
@@ -12,9 +12,9 @@ }; mod config; -pub use self::config::{QueryAccessors, QueryConfig, QueryDescription}; +pub use self::config::{QueryConfig, QueryDescription, QueryVtable}; -use crate::dep_graph::{DepNode, DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; +use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; use rustc_data_structures::sync::Lock; use rustc_data_structures::thin_vec::ThinVec; @@ -122,12 +122,6 @@ fn try_collect_active_jobs(&self) -> Option<QueryMap<Self::DepKind>>; - /// Load data from the on-disk cache. - fn try_load_from_on_disk_cache(&self, dep_node: &DepNode<Self::DepKind>); - - /// Try to force a dep node to execute and see if it's green. - fn try_force_from_dep_node(&self, dep_node: &DepNode<Self::DepKind>) -> bool; - /// Load side effects associated to the node in the previous session. fn load_side_effects(&self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects;
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 07d7205..b08db39e 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -2,9 +2,9 @@ //! generate the actual methods on tcx which find and execute the provider, //! manage the caches, and so forth. -use crate::dep_graph::{DepContext, DepKind, DepNode, DepNodeIndex, DepNodeParams}; +use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams}; use crate::query::caches::QueryCache; -use crate::query::config::{QueryDescription, QueryVtable, QueryVtableExt}; +use crate::query::config::{QueryDescription, QueryVtable}; use crate::query::job::{ report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryShardJobId, }; @@ -18,6 +18,7 @@ use rustc_data_structures::sync::{Lock, LockGuard}; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{DiagnosticBuilder, FatalError}; +use rustc_session::Session; use rustc_span::{Span, DUMMY_SP}; use std::cell::Cell; use std::collections::hash_map::Entry; @@ -382,7 +383,6 @@ lookup: QueryLookup, dep_node: Option<DepNode<CTX::DepKind>>, query: &QueryVtable<CTX, C::Key, C::Value>, - compute: fn(CTX::DepContext, C::Key) -> C::Value, ) -> (C::Stored, Option<DepNodeIndex>) where C: QueryCache, @@ -398,7 +398,7 @@ query.dep_kind, ) { TryGetJob::NotYetStarted(job) => { - let (result, dep_node_index) = execute_job(tcx, key, dep_node, query, job.id, compute); + let (result, dep_node_index) = execute_job(tcx, key, dep_node, query, job.id); let result = job.complete(cache, result, dep_node_index); (result, Some(dep_node_index)) } @@ -429,7 +429,6 @@ mut dep_node_opt: Option<DepNode<CTX::DepKind>>, query: &QueryVtable<CTX, K, V>, job_id: QueryJobId<CTX::DepKind>, - compute: fn(CTX::DepContext, K) -> V, ) -> (V, DepNodeIndex) where K: Clone + DepNodeParams<CTX::DepContext>, @@ -441,7 +440,7 @@ // Fast path for when incr. comp. is off. if !dep_graph.is_fully_enabled() { let prof_timer = tcx.dep_context().profiler().query_provider(); - let result = tcx.start_query(job_id, None, || compute(*tcx.dep_context(), key)); + let result = tcx.start_query(job_id, None, || query.compute(*tcx.dep_context(), key)); let dep_node_index = dep_graph.next_virtual_depnode_index(); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); return (result, dep_node_index); @@ -455,7 +454,7 @@ // The diagnostics for this query will be promoted to the current session during // `try_mark_green()`, so we can ignore them here. if let Some(ret) = tcx.start_query(job_id, None, || { - try_load_from_disk_and_cache_in_memory(tcx, &key, &dep_node, query, compute) + try_load_from_disk_and_cache_in_memory(tcx, &key, &dep_node, query) }) { return ret; } @@ -467,14 +466,14 @@ let (result, dep_node_index) = tcx.start_query(job_id, Some(&diagnostics), || { if query.anon { return dep_graph.with_anon_task(*tcx.dep_context(), query.dep_kind, || { - compute(*tcx.dep_context(), key) + query.compute(*tcx.dep_context(), key) }); } // `to_dep_node` is expensive for some `DepKind`s. let dep_node = dep_node_opt.unwrap_or_else(|| query.to_dep_node(*tcx.dep_context(), &key)); - dep_graph.with_task(dep_node, *tcx.dep_context(), key, compute, query.hash_result) + dep_graph.with_task(dep_node, *tcx.dep_context(), key, query.compute, query.hash_result) }); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -498,7 +497,6 @@ key: &K, dep_node: &DepNode<CTX::DepKind>, query: &QueryVtable<CTX, K, V>, - compute: fn(CTX::DepContext, K) -> V, ) -> Option<(V, DepNodeIndex)> where K: Clone, @@ -515,28 +513,41 @@ // First we try to load the result from the on-disk cache. // Some things are never cached on disk. - if query.cache_on_disk(tcx, key, None) { + if query.cache_on_disk { let prof_timer = tcx.dep_context().profiler().incr_cache_loading(); let result = query.try_load_from_disk(tcx, prev_dep_node_index); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - // We always expect to find a cached result for things that - // can be forced from `DepNode`. - debug_assert!( - !dep_node.kind.fingerprint_style().reconstructible() || result.is_some(), - "missing on-disk cache entry for {:?}", - dep_node - ); - if let Some(result) = result { + let prev_fingerprint = tcx + .dep_context() + .dep_graph() + .prev_fingerprint_of(dep_node) + .unwrap_or(Fingerprint::ZERO); // If `-Zincremental-verify-ich` is specified, re-hash results from // the cache and make sure that they have the expected fingerprint. - if unlikely!(tcx.dep_context().sess().opts.debugging_opts.incremental_verify_ich) { + // + // If not, we still seek to verify a subset of fingerprints loaded + // from disk. Re-hashing results is fairly expensive, so we can't + // currently afford to verify every hash. This subset should still + // give us some coverage of potential bugs though. + let try_verify = prev_fingerprint.as_value().1 % 32 == 0; + if unlikely!( + try_verify || tcx.dep_context().sess().opts.debugging_opts.incremental_verify_ich + ) { incremental_verify_ich(*tcx.dep_context(), &result, dep_node, query); } return Some((result, dep_node_index)); } + + // We always expect to find a cached result for things that + // can be forced from `DepNode`. + debug_assert!( + !tcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(), + "missing on-disk cache entry for {:?}", + dep_node + ); } // We could not load a result from the on-disk cache, so @@ -544,7 +555,7 @@ let prof_timer = tcx.dep_context().profiler().query_provider(); // The dep-graph for this computation is already in-place. - let result = dep_graph.with_ignore(|| compute(*tcx.dep_context(), key.clone())); + let result = dep_graph.with_ignore(|| query.compute(*tcx.dep_context(), key.clone())); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -577,46 +588,94 @@ ); debug!("BEGIN verify_ich({:?})", dep_node); - let mut hcx = tcx.create_stable_hashing_context(); - - let new_hash = query.hash_result(&mut hcx, result).unwrap_or(Fingerprint::ZERO); + let new_hash = query.hash_result.map_or(Fingerprint::ZERO, |f| { + let mut hcx = tcx.create_stable_hashing_context(); + f(&mut hcx, result) + }); + let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node); debug!("END verify_ich({:?})", dep_node); - let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node); - if Some(new_hash) != old_hash { - let run_cmd = if let Some(crate_name) = &tcx.sess().opts.crate_name { - format!("`cargo clean -p {}` or `cargo clean`", crate_name) - } else { - "`cargo clean`".to_string() - }; + incremental_verify_ich_cold(tcx.sess(), DebugArg::from(&dep_node), DebugArg::from(&result)); + } +} - // When we emit an error message and panic, we try to debug-print the `DepNode` - // and query result. Unforunately, this can cause us to run additional queries, - // which may result in another fingerprint mismatch while we're in the middle - // of processing this one. To avoid a double-panic (which kills the process - // before we can print out the query static), we print out a terse - // but 'safe' message if we detect a re-entrant call to this method. - thread_local! { - static INSIDE_VERIFY_PANIC: Cell<bool> = const { Cell::new(false) }; - }; +// This DebugArg business is largely a mirror of std::fmt::ArgumentV1, which is +// currently not exposed publicly. +// +// The PR which added this attempted to use `&dyn Debug` instead, but that +// showed statistically significant worse compiler performance. It's not +// actually clear what the cause there was -- the code should be cold. If this +// can be replaced with `&dyn Debug` with on perf impact, then it probably +// should be. +extern "C" { + type Opaque; +} - let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true)); +struct DebugArg<'a> { + value: &'a Opaque, + fmt: fn(&Opaque, &mut std::fmt::Formatter<'_>) -> std::fmt::Result, +} - if old_in_panic { - tcx.sess().struct_err("internal compiler error: re-entrant incremental verify failure, suppressing message") - .emit(); - } else { - tcx.sess().struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node)) +impl<'a, T> From<&'a T> for DebugArg<'a> +where + T: std::fmt::Debug, +{ + fn from(value: &'a T) -> DebugArg<'a> { + DebugArg { + value: unsafe { std::mem::transmute(value) }, + fmt: unsafe { + std::mem::transmute(<T as std::fmt::Debug>::fmt as fn(_, _) -> std::fmt::Result) + }, + } + } +} + +impl std::fmt::Debug for DebugArg<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self.fmt)(self.value, f) + } +} + +// Note that this is marked #[cold] and intentionally takes the equivalent of +// `dyn Debug` for its arguments, as we want to avoid generating a bunch of +// different implementations for LLVM to chew on (and filling up the final +// binary, too). +#[cold] +fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: DebugArg<'_>) { + let run_cmd = if let Some(crate_name) = &sess.opts.crate_name { + format!("`cargo clean -p {}` or `cargo clean`", crate_name) + } else { + "`cargo clean`".to_string() + }; + + // When we emit an error message and panic, we try to debug-print the `DepNode` + // and query result. Unfortunately, this can cause us to run additional queries, + // which may result in another fingerprint mismatch while we're in the middle + // of processing this one. To avoid a double-panic (which kills the process + // before we can print out the query static), we print out a terse + // but 'safe' message if we detect a re-entrant call to this method. + thread_local! { + static INSIDE_VERIFY_PANIC: Cell<bool> = const { Cell::new(false) }; + }; + + let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true)); + + if old_in_panic { + sess.struct_err( + "internal compiler error: re-entrant incremental verify failure, suppressing message", + ) + .emit(); + } else { + sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node)) .help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd)) .note(&"Please follow the instructions below to create a bug report with the provided information") .note(&"See <https://github.com/rust-lang/rust/issues/84970> for more information") .emit(); - panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result); - } - - INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.set(old_in_panic)); + panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result); } + + INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.set(old_in_panic)); } /// Ensure that either this query has all green inputs or been executed. @@ -665,41 +724,6 @@ } } -#[inline(never)] -fn force_query_impl<CTX, C>( - tcx: CTX, - state: &QueryState<CTX::DepKind, C::Key>, - cache: &QueryCacheStore<C>, - key: C::Key, - dep_node: DepNode<CTX::DepKind>, - query: &QueryVtable<CTX, C::Key, C::Value>, - compute: fn(CTX::DepContext, C::Key) -> C::Value, -) -> bool -where - C: QueryCache, - C::Key: DepNodeParams<CTX::DepContext>, - CTX: QueryContext, -{ - debug_assert!(!query.anon); - - // We may be concurrently trying both execute and force a query. - // Ensure that only one of them runs the query. - let cached = cache.cache.lookup(cache, &key, |_, index| { - if unlikely!(tcx.dep_context().profiler().enabled()) { - tcx.dep_context().profiler().query_cache_hit(index.into()); - } - }); - - let lookup = match cached { - Ok(()) => return true, - Err(lookup) => lookup, - }; - - let _ = - try_execute_query(tcx, state, cache, DUMMY_SP, key, lookup, Some(dep_node), query, compute); - true -} - pub enum QueryMode { Get, Ensure, @@ -717,9 +741,9 @@ Q::Key: DepNodeParams<CTX::DepContext>, CTX: QueryContext, { - let query = &Q::VTABLE; + let query = Q::make_vtable(tcx, &key); let dep_node = if let QueryMode::Ensure = mode { - let (must_run, dep_node) = ensure_must_run(tcx, &key, query); + let (must_run, dep_node) = ensure_must_run(tcx, &key, &query); if !must_run { return None; } @@ -729,7 +753,6 @@ }; debug!("ty::query::get_query<{}>(key={:?}, span={:?})", Q::NAME, key, span); - let compute = Q::compute_fn(tcx, &key); let (result, dep_node_index) = try_execute_query( tcx, Q::query_state(tcx), @@ -738,8 +761,7 @@ key, lookup, dep_node, - query, - compute, + &query, ); if let Some(dep_node_index) = dep_node_index { tcx.dep_context().dep_graph().read_index(dep_node_index) @@ -747,36 +769,29 @@ Some(result) } -pub fn force_query<Q, CTX>(tcx: CTX, dep_node: &DepNode<CTX::DepKind>) -> bool +pub fn force_query<Q, CTX>(tcx: CTX, key: Q::Key, dep_node: DepNode<CTX::DepKind>) where Q: QueryDescription<CTX>, Q::Key: DepNodeParams<CTX::DepContext>, CTX: QueryContext, { - if Q::ANON { - return false; - } + // We may be concurrently trying both execute and force a query. + // Ensure that only one of them runs the query. + let cache = Q::query_cache(tcx); + let cached = cache.cache.lookup(cache, &key, |_, index| { + if unlikely!(tcx.dep_context().profiler().enabled()) { + tcx.dep_context().profiler().query_cache_hit(index.into()); + } + }); - if !<Q::Key as DepNodeParams<CTX::DepContext>>::fingerprint_style().reconstructible() { - return false; - } - - let key = if let Some(key) = - <Q::Key as DepNodeParams<CTX::DepContext>>::recover(*tcx.dep_context(), &dep_node) - { - key - } else { - return false; + let lookup = match cached { + Ok(()) => return, + Err(lookup) => lookup, }; - let compute = Q::compute_fn(tcx, &key); - force_query_impl( - tcx, - Q::query_state(tcx), - Q::query_cache(tcx), - key, - *dep_node, - &Q::VTABLE, - compute, - ) + let query = Q::make_vtable(tcx, &key); + let state = Q::query_state(tcx); + debug_assert!(!query.anon); + + try_execute_query(tcx, state, cache, DUMMY_SP, key, lookup, Some(dep_node), &query); }
diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index f1d3315d..bd27c16 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml
@@ -23,6 +23,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_metadata = { path = "../rustc_metadata" } +rustc_query_system = { path = "../rustc_query_system" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2a562a0..3cf9d32 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -15,7 +15,7 @@ use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind}; -use rustc_ast::{Block, FnKind, ForeignItem, ForeignItemKind, ImplKind, Item, ItemKind, NodeId}; +use rustc_ast::{Block, Fn, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId}; use rustc_ast_lowering::ResolverAstLowering; use rustc_attr as attr; use rustc_data_structures::sync::Lrc; @@ -145,17 +145,11 @@ } else { def_key.disambiguated_data.data.get_opt_name().expect("module without name") }; - let expn_id = if def_kind == DefKind::Mod { - self.cstore().module_expansion_untracked(def_id, &self.session) - } else { - // FIXME: Parent expansions for enums and traits are not kept in metadata. - ExpnId::root() - }; Some(self.new_module( parent, ModuleKind::Def(def_kind, def_id, name), - expn_id, + self.cstore().module_expansion_untracked(def_id, &self.session), self.cstore().get_span_untracked(def_id, &self.session), // FIXME: Account for `#[no_implicit_prelude]` attributes. parent.map_or(false, |module| module.no_implicit_prelude), @@ -886,7 +880,7 @@ } // These items do not add names to modules. - ItemKind::Impl(box ImplKind { of_trait: Some(..), .. }) => { + ItemKind::Impl(box Impl { of_trait: Some(..), .. }) => { self.r.trait_impl_items.insert(local_def_id); } ItemKind::Impl { .. } | ItemKind::ForeignMod(..) | ItemKind::GlobalAsm(..) => {} @@ -973,6 +967,7 @@ | DefKind::Use | DefKind::ForeignMod | DefKind::AnonConst + | DefKind::InlineConst | DefKind::Field | DefKind::LifetimeParam | DefKind::GlobalAsm @@ -1199,15 +1194,9 @@ // Mark the given macro as unused unless its name starts with `_`. // Macro uses will remove items from this set, and the remaining // items will be reported as `unused_macros`. - fn insert_unused_macro( - &mut self, - ident: Ident, - def_id: LocalDefId, - node_id: NodeId, - span: Span, - ) { + fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) { if !ident.as_str().starts_with('_') { - self.r.unused_macros.insert(def_id, (node_id, span)); + self.r.unused_macros.insert(def_id, (node_id, ident)); } } @@ -1251,7 +1240,7 @@ self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport)); } else { self.r.check_reserved_macro_name(ident, res); - self.insert_unused_macro(ident, def_id, item.id, span); + self.insert_unused_macro(ident, def_id, item.id); } self.r.visibilities.insert(def_id, vis); self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding( @@ -1272,7 +1261,7 @@ _ => self.resolve_visibility(&item.vis), }; if vis != ty::Visibility::Public { - self.insert_unused_macro(ident, def_id, item.id, span); + self.insert_unused_macro(ident, def_id, item.id); } self.r.define(module, ident, MacroNS, (res, vis, span, expansion)); self.r.visibilities.insert(def_id, vis); @@ -1386,7 +1375,7 @@ if ctxt == AssocCtxt::Trait { let (def_kind, ns) = match item.kind { AssocItemKind::Const(..) => (DefKind::AssocConst, ValueNS), - AssocItemKind::Fn(box FnKind(_, ref sig, _, _)) => { + AssocItemKind::Fn(box Fn { ref sig, .. }) => { if sig.decl.has_self() { self.r.has_self.insert(def_id); }
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 760b746..6369912 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -32,7 +32,6 @@ use rustc_ast_lowering::ResolverAstLowering; use rustc_data_structures::fx::FxHashSet; use rustc_errors::pluralize; -use rustc_middle::ty; use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_IMPORTS}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::{MultiSpan, Span, DUMMY_SP}; @@ -228,7 +227,7 @@ for import in self.potentially_unused_imports.iter() { match import.kind { _ if import.used.get() - || import.vis.get() == ty::Visibility::Public + || import.vis.get().is_public() || import.span.is_dummy() => { if let ImportKind::MacroUse = import.kind {
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 05675e0..2e4cb4f 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -11,7 +11,7 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::PrimTy; use rustc_middle::bug; -use rustc_middle::ty::{self, DefIdTree}; +use rustc_middle::ty::DefIdTree; use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::lev_distance::find_best_match_for_name; @@ -66,6 +66,8 @@ pub descr: &'static str, pub path: Path, pub accessible: bool, + /// An extra note that should be issued if this item is suggested + pub note: Option<String>, } /// Adjust the impl span so that just the `impl` keyword is taken by removing @@ -418,6 +420,10 @@ err.span_label(span, label); if let Some((suggestions, msg, applicability)) = suggestion { + if suggestions.is_empty() { + err.help(&msg); + return err; + } err.multipart_suggestion(&msg, suggestions, applicability); } @@ -444,12 +450,24 @@ // let foo =... // ^^^ given this Span // ------- get this Span to have an applicable suggestion + + // edit: + // only do this if the const and usage of the non-constant value are on the same line + // the further the two are apart, the higher the chance of the suggestion being wrong + // also make sure that the pos for the suggestion is not 0 (ICE #90878) + let sp = self.session.source_map().span_extend_to_prev_str(ident.span, current, true); - if sp.lo().0 == 0 { + + let pos_for_suggestion = sp.lo().0.saturating_sub(current.len() as u32); + + if sp.lo().0 == 0 + || pos_for_suggestion == 0 + || self.session.source_map().is_multiline(sp) + { err.span_label(ident.span, &format!("this would need to be a `{}`", sugg)); } else { - let sp = sp.with_lo(BytePos(sp.lo().0 - current.len() as u32)); + let sp = sp.with_lo(BytePos(pos_for_suggestion)); err.span_suggestion( sp, &format!("consider using `{}` instead of `{}`", sugg, current), @@ -725,7 +743,7 @@ suggestions.extend( BUILTIN_ATTRIBUTES .iter() - .map(|(name, ..)| TypoSuggestion::typo_from_res(*name, res)), + .map(|attr| TypoSuggestion::typo_from_res(attr.name, res)), ); } } @@ -829,11 +847,22 @@ return; } + // #90113: Do not count an inaccessible reexported item as a candidate. + if let NameBindingKind::Import { binding, .. } = name_binding.kind { + if this.is_accessible_from(binding.vis, parent_scope.module) + && !this.is_accessible_from(name_binding.vis, parent_scope.module) + { + return; + } + } + // collect results based on the filter function // avoid suggesting anything from the same module in which we are resolving + // avoid suggesting anything with a hygienic name if ident.name == lookup_ident.name && ns == namespace && !ptr::eq(in_module, parent_scope.module) + && !ident.span.normalize_to_macros_2_0().from_expansion() { let res = name_binding.res(); if filter_fn(res) { @@ -863,11 +892,38 @@ } if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { + // See if we're recommending TryFrom, TryInto, or FromIterator and add + // a note about editions + let note = if let Some(did) = did { + let requires_note = !did.is_local() + && this.cstore().item_attrs(did, this.session).iter().any( + |attr| { + if attr.has_name(sym::rustc_diagnostic_item) { + [sym::TryInto, sym::TryFrom, sym::FromIterator] + .map(|x| Some(x)) + .contains(&attr.value_str()) + } else { + false + } + }, + ); + + requires_note.then(|| { + format!( + "'{}' is included in the prelude starting in Edition 2021", + path_names_to_string(&path) + ) + }) + } else { + None + }; + candidates.push(ImportSuggestion { did, descr: res.descr(), path, accessible: child_accessible, + note, }); } } @@ -1156,14 +1212,9 @@ (b1, b2, misc1, misc2, false) }; - let mut err = struct_span_err!( - self.session, - ident.span, - E0659, - "`{ident}` is ambiguous ({why})", - why = kind.descr() - ); + let mut err = struct_span_err!(self.session, ident.span, E0659, "`{ident}` is ambiguous"); err.span_label(ident.span, "ambiguous name"); + err.note(&format!("ambiguous because of {}", kind.descr())); let mut could_refer_to = |b: &NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| { let what = self.binding_description(b, ident, misc == AmbiguityErrorMisc::FromPrelude); @@ -1269,7 +1320,7 @@ ); let def_span = self.session.source_map().guess_head_span(binding.span); let mut note_span = MultiSpan::from_span(def_span); - if !first && binding.vis == ty::Visibility::Public { + if !first && binding.vis.is_public() { note_span.push_span_label(def_span, "consider importing it directly".into()); } err.span_note(note_span, &msg); @@ -1327,7 +1378,7 @@ if fst.ident.span.rust_2018() && !fst.ident.is_path_segment_keyword() => { // Insert a placeholder that's later replaced by `self`/`super`/etc. - path.insert(0, Segment::from_ident(Ident::invalid())); + path.insert(0, Segment::from_ident(Ident::empty())); } _ => return None, } @@ -1471,9 +1522,7 @@ module: ModuleOrUniformRoot<'b>, ident: Ident, ) -> Option<(Option<Suggestion>, Vec<String>)> { - let mut crate_module = if let ModuleOrUniformRoot::Module(module) = module { - module - } else { + let ModuleOrUniformRoot::Module(mut crate_module) = module else { return None; }; @@ -1762,12 +1811,14 @@ return; } - let mut accessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new(); - let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new(); + let mut accessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> = + Vec::new(); + let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> = + Vec::new(); candidates.iter().for_each(|c| { (if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings }) - .push((path_names_to_string(&c.path), c.descr, c.did)) + .push((path_names_to_string(&c.path), c.descr, c.did, &c.note)) }); // we want consistent results across executions, but candidates are produced @@ -1790,6 +1841,10 @@ let instead = if instead { " instead" } else { "" }; let mut msg = format!("consider importing {} {}{}", determiner, kind, instead); + for note in accessible_path_strings.iter().map(|cand| cand.3.as_ref()).flatten() { + err.note(note); + } + if let Some(span) = use_placement_span { for candidate in &mut accessible_path_strings { // produce an additional newline to separate the new use statement @@ -1818,7 +1873,7 @@ assert!(!inaccessible_path_strings.is_empty()); if inaccessible_path_strings.len() == 1 { - let (name, descr, def_id) = &inaccessible_path_strings[0]; + let (name, descr, def_id, note) = &inaccessible_path_strings[0]; let msg = format!("{} `{}` exists but is inaccessible", descr, name); if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { @@ -1830,12 +1885,15 @@ } else { err.note(&msg); } + if let Some(note) = (*note).as_deref() { + err.note(note); + } } else { - let (_, descr_first, _) = &inaccessible_path_strings[0]; + let (_, descr_first, _, _) = &inaccessible_path_strings[0]; let descr = if inaccessible_path_strings .iter() .skip(1) - .all(|(_, descr, _)| descr == descr_first) + .all(|(_, descr, _, _)| descr == descr_first) { descr_first.to_string() } else { @@ -1846,7 +1904,7 @@ let mut has_colon = false; let mut spans = Vec::new(); - for (name, _, def_id) in &inaccessible_path_strings { + for (name, _, def_id, _) in &inaccessible_path_strings { if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { let span = definitions.def_span(local_def_id); let span = session.source_map().guess_head_span(span); @@ -1866,6 +1924,10 @@ multi_span.push_span_label(span, format!("`{}`: not accessible", name)); } + for note in inaccessible_path_strings.iter().map(|cand| cand.3.as_ref()).flatten() { + err.note(note); + } + err.span_note(multi_span, &msg); } }
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 515b2c3..bf4cece 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs
@@ -164,7 +164,7 @@ import: Import { kind: ImportKind::ExternCrate { .. }, .. }, .. }, - ) => import.vis.get() == ty::Visibility::Public, + ) => import.vis.get().is_public(), _ => false, } } @@ -978,7 +978,7 @@ // HACK(eddyb) `lint_if_path_starts_with_module` needs at least // 2 segments, so the `resolve_path` above won't trigger it. let mut full_path = import.module_path.clone(); - full_path.push(Segment::from_ident(Ident::invalid())); + full_path.push(Segment::from_ident(Ident::empty())); self.r.lint_if_path_starts_with_module( import.crate_lint(), &full_path, @@ -1180,11 +1180,17 @@ let mut reexport_error = None; let mut any_successful_reexport = false; + let mut crate_private_reexport = false; self.r.per_ns(|this, ns| { if let Ok(binding) = source_bindings[ns].get() { let vis = import.vis.get(); if !binding.vis.is_at_least(vis, &*this) { reexport_error = Some((ns, binding)); + if let ty::Visibility::Restricted(binding_def_id) = binding.vis { + if binding_def_id.is_top_level_module() { + crate_private_reexport = true; + } + } } else { any_successful_reexport = true; } @@ -1207,24 +1213,34 @@ import.span, &msg, ); - } else if ns == TypeNS { - struct_span_err!( - self.r.session, - import.span, - E0365, - "`{}` is private, and cannot be re-exported", - ident - ) - .span_label(import.span, format!("re-export of private `{}`", ident)) - .note(&format!("consider declaring type or module `{}` with `pub`", ident)) - .emit(); } else { - let msg = format!("`{}` is private, and cannot be re-exported", ident); - let note_msg = - format!("consider marking `{}` as `pub` in the imported module", ident,); - struct_span_err!(self.r.session, import.span, E0364, "{}", &msg) - .span_note(import.span, ¬e_msg) - .emit(); + let error_msg = if crate_private_reexport { + format!( + "`{}` is only public within the crate, and cannot be re-exported outside", + ident + ) + } else { + format!("`{}` is private, and cannot be re-exported", ident) + }; + + if ns == TypeNS { + let label_msg = if crate_private_reexport { + format!("re-export of crate public `{}`", ident) + } else { + format!("re-export of private `{}`", ident) + }; + + struct_span_err!(self.r.session, import.span, E0365, "{}", error_msg) + .span_label(import.span, label_msg) + .note(&format!("consider declaring type or module `{}` with `pub`", ident)) + .emit(); + } else { + let note_msg = + format!("consider marking `{}` as `pub` in the imported module", ident); + struct_span_err!(self.r.session, import.span, E0364, "{}", error_msg) + .span_note(import.span, ¬e_msg) + .emit(); + } } }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 95633257..12123c9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs
@@ -431,6 +431,10 @@ /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { + fn visit_attribute(&mut self, _: &'ast Attribute) { + // We do not want to resolve expressions that appear in attributes, + // as they do not correspond to actual code. + } fn visit_item(&mut self, item: &'ast Item) { let prev = replace(&mut self.diagnostic_metadata.current_item, Some(item)); // Always report errors in items we just entered. @@ -498,8 +502,8 @@ } fn visit_foreign_item(&mut self, foreign_item: &'ast ForeignItem) { match foreign_item.kind { - ForeignItemKind::Fn(box FnKind(_, _, ref generics, _)) - | ForeignItemKind::TyAlias(box TyAliasKind(_, ref generics, ..)) => { + ForeignItemKind::Fn(box Fn { ref generics, .. }) + | ForeignItemKind::TyAlias(box TyAlias { ref generics, .. }) => { self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { visit::walk_foreign_item(this, foreign_item); }); @@ -953,8 +957,8 @@ debug!("(resolving item) resolving {} ({:?})", name, item.kind); match item.kind { - ItemKind::TyAlias(box TyAliasKind(_, ref generics, _, _)) - | ItemKind::Fn(box FnKind(_, _, ref generics, _)) => { + ItemKind::TyAlias(box TyAlias { ref generics, .. }) + | ItemKind::Fn(box Fn { ref generics, .. }) => { self.compute_num_lifetime_params(item.id, generics); self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { visit::walk_item(this, item) @@ -968,7 +972,7 @@ self.resolve_adt(item, generics); } - ItemKind::Impl(box ImplKind { + ItemKind::Impl(box Impl { ref generics, ref of_trait, ref self_ty, @@ -979,7 +983,7 @@ self.resolve_implementation(generics, of_trait, &self_ty, item.id, impl_items); } - ItemKind::Trait(box TraitKind(.., ref generics, ref bounds, ref trait_items)) => { + ItemKind::Trait(box Trait { ref generics, ref bounds, ref items, .. }) => { self.compute_num_lifetime_params(item.id, generics); // Create a new rib for the trait-wide type parameters. self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { @@ -994,8 +998,8 @@ }); }; - this.with_trait_items(trait_items, |this| { - for item in trait_items { + this.with_trait_items(items, |this| { + for item in items { match &item.kind { AssocItemKind::Const(_, ty, default) => { this.visit_ty(ty); @@ -1015,10 +1019,10 @@ ); } } - AssocItemKind::Fn(box FnKind(_, _, generics, _)) => { + AssocItemKind::Fn(box Fn { generics, .. }) => { walk_assoc_item(this, generics, item); } - AssocItemKind::TyAlias(box TyAliasKind(_, generics, _, _)) => { + AssocItemKind::TyAlias(box TyAlias { generics, .. }) => { walk_assoc_item(this, generics, item); } AssocItemKind::MacCall(_) => { @@ -1338,7 +1342,7 @@ }, ); } - AssocItemKind::Fn(box FnKind(.., generics, _)) => { + AssocItemKind::Fn(box Fn { generics, .. }) => { debug!("resolve_implementation AssocItemKind::Fn"); // We also need a new scope for the impl item type parameters. this.with_generic_param_rib( @@ -1363,12 +1367,9 @@ }, ); } - AssocItemKind::TyAlias(box TyAliasKind( - _, - generics, - _, - _, - )) => { + AssocItemKind::TyAlias(box TyAlias { + generics, .. + }) => { debug!("resolve_implementation AssocItemKind::TyAlias"); // We also need a new scope for the impl item type parameters. this.with_generic_param_rib( @@ -1994,7 +1995,7 @@ if ns == ValueNS { let item_name = path.last().unwrap().ident; let traits = self.traits_in_scope(item_name, ns); - self.r.trait_map.as_mut().unwrap().insert(id, traits); + self.r.trait_map.insert(id, traits); } if PrimTy::from_name(path[0].ident.name).is_some() { @@ -2479,12 +2480,12 @@ // the field name so that we can do some nice error reporting // later on in typeck. let traits = self.traits_in_scope(ident, ValueNS); - self.r.trait_map.as_mut().unwrap().insert(expr.id, traits); + self.r.trait_map.insert(expr.id, traits); } ExprKind::MethodCall(ref segment, ..) => { debug!("(recording candidate traits for expr) recording traits for {}", expr.id); let traits = self.traits_in_scope(segment.ident, ValueNS); - self.r.trait_map.as_mut().unwrap().insert(expr.id, traits); + self.r.trait_map.insert(expr.id, traits); } _ => { // Nothing to do.
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 7b0dd82..d506931 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -333,7 +333,7 @@ let candidates = self .r .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected) - .drain(..) + .into_iter() .filter(|ImportSuggestion { did, .. }| { match (did, res.and_then(|res| res.opt_def_id())) { (Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did, @@ -1235,9 +1235,7 @@ if assoc_item.ident == ident { return Some(match &assoc_item.kind { ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst, - ast::AssocItemKind::Fn(box ast::FnKind(_, sig, ..)) - if sig.decl.has_self() => - { + ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) if sig.decl.has_self() => { AssocSuggestion::MethodWithSelf } ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn, @@ -1346,12 +1344,10 @@ } else { // Search in module. let mod_path = &path[..path.len() - 1]; - if let PathResult::Module(module) = + if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(mod_path, Some(TypeNS), false, span, CrateLint::No) { - if let ModuleOrUniformRoot::Module(module) = module { - self.r.add_module_candidates(module, &mut names, &filter_fn); - } + self.r.add_module_candidates(module, &mut names, &filter_fn); } } @@ -1502,6 +1498,7 @@ descr: "module", path, accessible: true, + note: None, }, )); } else { @@ -1552,8 +1549,8 @@ matches!(source, PathSource::TupleStruct(..)) || source.is_call(); if suggest_only_tuple_variants { // Suggest only tuple variants regardless of whether they have fields and do not - // suggest path with added parenthesis. - let mut suggestable_variants = variants + // suggest path with added parentheses. + let suggestable_variants = variants .iter() .filter(|(.., kind)| *kind == CtorKind::Fn) .map(|(variant, ..)| path_names_to_string(variant)) @@ -1579,7 +1576,7 @@ err.span_suggestions( span, &msg, - suggestable_variants.drain(..), + suggestable_variants.into_iter(), Applicability::MaybeIncorrect, ); } @@ -1637,7 +1634,7 @@ ); } - let mut suggestable_variants_with_placeholders = variants + let suggestable_variants_with_placeholders = variants .iter() .filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind)) .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) @@ -1662,7 +1659,7 @@ err.span_suggestions( span, msg, - suggestable_variants_with_placeholders.drain(..), + suggestable_variants_with_placeholders.into_iter(), Applicability::HasPlaceholders, ); } @@ -1813,12 +1810,10 @@ let (span, sugg) = if let Some(param) = generics.params.iter().find(|p| { !matches!( p.kind, - hir::GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } | hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Elided, - } + hir::GenericParamKind::Type { synthetic: true, .. } + | hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Elided, + } ) }) { (param.span.shrink_to_lo(), format!("{}, ", lifetime_ref)) @@ -2045,12 +2040,10 @@ if let Some(param) = generics.params.iter().find(|p| { !matches!( p.kind, - hir::GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } | hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Elided - } + hir::GenericParamKind::Type { synthetic: true, .. } + | hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Elided + } ) }) { (param.span.shrink_to_lo(), "'a, ".to_string())
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index eb6f302..39e710c 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -540,7 +540,7 @@ def_id: LocalDefId, ) -> Option<(LocalDefId, &'tcx FxHashSet<ItemLocalId>)> { match tcx.def_kind(def_id) { - DefKind::AnonConst => { + DefKind::AnonConst | DefKind::InlineConst => { let mut def_id = tcx .parent(def_id.to_def_id()) .unwrap_or_else(|| bug!("anon const or closure without a parent")); @@ -887,10 +887,7 @@ let (lifetimes, binders): (FxIndexMap<hir::ParamName, Region>, Vec<_>) = c .generic_params .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(param), - _ => None, - }) + .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) .enumerate() .map(|(late_bound_idx, param)| { let pair = Region::late(late_bound_idx as u32, &self.tcx.hir(), param); @@ -1057,9 +1054,7 @@ match param.kind { GenericParamKind::Lifetime { .. } => { let (name, reg) = Region::early(&self.tcx.hir(), &mut index, ¶m); - let def_id = if let Region::EarlyBound(_, def_id, _) = reg { - def_id - } else { + let Region::EarlyBound(_, def_id, _) = reg else { bug!(); }; // We cannot predict what lifetimes are unused in opaque type. @@ -1372,9 +1367,8 @@ let (lifetimes, binders): (FxIndexMap<hir::ParamName, Region>, Vec<_>) = bound_generic_params .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(param), - _ => None, + .filter(|param| { + matches!(param.kind, GenericParamKind::Lifetime { .. }) }) .enumerate() .map(|(late_bound_idx, param)| { @@ -1471,10 +1465,7 @@ let binders_iter = trait_ref .bound_generic_params .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(param), - _ => None, - }) + .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) .enumerate() .map(|(late_bound_idx, param)| { let pair = Region::late( @@ -1933,20 +1924,18 @@ break; } } - hir::TyKind::Path(ref qpath) => { - if let QPath::Resolved(_, path) = qpath { - let last_segment = &path.segments[path.segments.len() - 1]; - let generics = last_segment.args(); - for arg in generics.args.iter() { - if let GenericArg::Lifetime(lt) = arg { - if lt.name.ident() == name { - elide_use = Some(lt.span); - break; - } + hir::TyKind::Path(QPath::Resolved(_, path)) => { + let last_segment = &path.segments[path.segments.len() - 1]; + let generics = last_segment.args(); + for arg in generics.args.iter() { + if let GenericArg::Lifetime(lt) = arg { + if lt.name.ident() == name { + elide_use = Some(lt.span); + break; } } - break; } + break; } _ => {} } @@ -2239,19 +2228,14 @@ let binders: Vec<_> = generics .params .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } - if self.map.late_bound.contains(¶m.hir_id) => - { - Some(param) - } - _ => None, + .filter(|param| { + matches!(param.kind, GenericParamKind::Lifetime { .. }) + && self.map.late_bound.contains(¶m.hir_id) }) .enumerate() .map(|(late_bound_idx, param)| { let pair = Region::late(late_bound_idx as u32, &self.tcx.hir(), param); - let r = late_region_as_bound_region(self.tcx, &pair.1); - r + late_region_as_bound_region(self.tcx, &pair.1) }) .collect(); self.map.late_bound_vars.insert(hir_id, binders);
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 9652c48..d17e887 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs
@@ -13,8 +13,9 @@ #![feature(drain_filter)] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(iter_zip)] +#![feature(let_else)] #![feature(never_type)] #![feature(nll)] #![recursion_limit = "256"] @@ -54,13 +55,14 @@ use rustc_middle::span_bug; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, MainDefinition, ResolverOutputs}; +use rustc_query_system::ich::StableHashingContext; use rustc_session::cstore::{CrateStore, MetadataLoaderDyn}; use rustc_session::lint; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext, Transparency}; -use rustc_span::source_map::{CachingSourceMapView, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -725,23 +727,21 @@ impl AmbiguityKind { fn descr(self) -> &'static str { match self { - AmbiguityKind::Import => "name vs any other name during import resolution", - AmbiguityKind::BuiltinAttr => "built-in attribute vs any other name", - AmbiguityKind::DeriveHelper => "derive helper attribute vs any other name", + AmbiguityKind::Import => "multiple potential import sources", + AmbiguityKind::BuiltinAttr => "a name conflict with a builtin attribute", + AmbiguityKind::DeriveHelper => "a name conflict with a derive helper attribute", AmbiguityKind::MacroRulesVsModularized => { - "`macro_rules` vs non-`macro_rules` from other module" + "a conflict between a `macro_rules` name and a non-`macro_rules` name from another module" } AmbiguityKind::GlobVsOuter => { - "glob import vs any other name from outer scope during import/macro resolution" + "a conflict between a name from a glob import and an outer scope during import or macro resolution" } - AmbiguityKind::GlobVsGlob => "glob import vs glob import in the same module", + AmbiguityKind::GlobVsGlob => "multiple glob imports of a name in the same module", AmbiguityKind::GlobVsExpanded => { - "glob import vs macro-expanded name in the same \ - module during import/macro resolution" + "a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution" } AmbiguityKind::MoreExpandedVsOuter => { - "macro-expanded name vs less macro-expanded name \ - from outer scope during import/macro resolution" + "a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution" } } } @@ -930,7 +930,7 @@ /// `CrateNum` resolutions of `extern crate` items. extern_crate_map: FxHashMap<LocalDefId, CrateNum>, export_map: ExportMap, - trait_map: Option<NodeMap<Vec<TraitCandidate>>>, + trait_map: NodeMap<Vec<TraitCandidate>>, /// A map from nodes to anonymous modules. /// Anonymous modules are pseudo-modules that are implicitly created around items @@ -988,7 +988,7 @@ non_macro_attr: Lrc<SyntaxExtension>, local_macro_def_scopes: FxHashMap<LocalDefId, Module<'a>>, ast_transform_scopes: FxHashMap<LocalExpnId, Module<'a>>, - unused_macros: FxHashMap<LocalDefId, (NodeId, Span)>, + unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>, proc_macro_stubs: FxHashSet<LocalDefId>, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: @@ -1177,6 +1177,10 @@ &mut self.definitions } + fn create_stable_hashing_context(&self) -> StableHashingContext<'_> { + StableHashingContext::new(self.session, &self.definitions, self.crate_loader.cstore()) + } + fn lint_buffer(&mut self) -> &mut LintBuffer { &mut self.lint_buffer } @@ -1185,8 +1189,8 @@ self.next_node_id() } - fn take_trait_map(&mut self) -> NodeMap<Vec<TraitCandidate>> { - std::mem::replace(&mut self.trait_map, None).unwrap() + fn take_trait_map(&mut self, node: NodeId) -> Option<Vec<TraitCandidate>> { + self.trait_map.remove(&node) } fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> { @@ -1245,37 +1249,6 @@ } } -struct ExpandHasher<'a, 'b> { - source_map: CachingSourceMapView<'a>, - resolver: &'a Resolver<'b>, -} - -impl<'a, 'b> rustc_span::HashStableContext for ExpandHasher<'a, 'b> { - #[inline] - fn hash_spans(&self) -> bool { - true - } - - #[inline] - fn def_span(&self, id: LocalDefId) -> Span { - self.resolver.def_span(id) - } - - #[inline] - fn def_path_hash(&self, def_id: DefId) -> DefPathHash { - self.resolver.def_path_hash(def_id) - } - - #[inline] - fn span_data_to_lines_and_cols( - &mut self, - span: &rustc_span::SpanData, - ) -> Option<(Lrc<rustc_span::SourceFile>, usize, rustc_span::BytePos, usize, rustc_span::BytePos)> - { - self.source_map.span_data_to_lines_and_cols(span) - } -} - impl<'a> Resolver<'a> { pub fn new( session: &'a Session, @@ -1363,7 +1336,7 @@ label_res_map: Default::default(), extern_crate_map: Default::default(), export_map: FxHashMap::default(), - trait_map: Some(NodeMap::default()), + trait_map: NodeMap::default(), underscore_disambiguator: 0, empty_module, module_map, @@ -1456,13 +1429,6 @@ self.arenas.new_module(parent, kind, expn_id, span, no_implicit_prelude, module_map) } - fn create_stable_hashing_context(&self) -> ExpandHasher<'_, 'a> { - ExpandHasher { - source_map: CachingSourceMapView::new(self.session.source_map()), - resolver: self, - } - } - pub fn next_node_id(&mut self) -> NodeId { let next = self .next_node_id @@ -2557,19 +2523,29 @@ } else { ( format!("use of undeclared crate or module `{}`", ident), - self.find_similarly_named_module_or_crate( - ident.name, - &parent_scope.module, - ) - .map(|sugg| { - ( - vec![(ident.span, sugg.to_string())], + if ident.name == sym::alloc { + Some(( + vec![], String::from( - "there is a crate or module with a similar name", + "add `extern crate alloc` to use the `alloc` crate", ), Applicability::MaybeIncorrect, + )) + } else { + self.find_similarly_named_module_or_crate( + ident.name, + &parent_scope.module, ) - }), + .map(|sugg| { + ( + vec![(ident.span, sugg.to_string())], + String::from( + "there is a crate or module with a similar name", + ), + Applicability::MaybeIncorrect, + ) + }) + }, ) } } else {
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 4f6e23d..28dbce0 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs
@@ -315,8 +315,13 @@ } fn check_unused_macros(&mut self) { - for (_, &(node_id, span)) in self.unused_macros.iter() { - self.lint_buffer.buffer_lint(UNUSED_MACROS, node_id, span, "unused macro definition"); + for (_, &(node_id, ident)) in self.unused_macros.iter() { + self.lint_buffer.buffer_lint( + UNUSED_MACROS, + node_id, + ident.span, + &format!("unused macro definition: `{}`", ident.as_str()), + ); } } @@ -1128,6 +1133,7 @@ feature, reason, issue, + None, is_soft, span, soft_handler,
diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index f4567b3..f1a5282 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs
@@ -236,7 +236,7 @@ id, span, name: ident.to_string(), - qualname: format!("{}::{}", qualname, ident.to_string()), + qualname: format!("{}::{}", qualname, ident), value: typ, parent: None, children: vec![], @@ -889,7 +889,7 @@ // Rust uses the id of the pattern for var lookups, so we'll use it too. if !self.span.filter_generated(ident.span) { - let qualname = format!("{}${}", ident.to_string(), hir_id); + let qualname = format!("{}${}", ident, hir_id); let id = id_from_hir_id(hir_id, &self.save_ctxt); let span = self.span_from_span(ident.span);
diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index 543cd02..c7f8fe3 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs
@@ -739,6 +739,7 @@ | HirDefKind::ForeignMod | HirDefKind::LifetimeParam | HirDefKind::AnonConst + | HirDefKind::InlineConst | HirDefKind::Use | HirDefKind::Field | HirDefKind::GlobalAsm
diff --git a/compiler/rustc_serialize/src/json.rs b/compiler/rustc_serialize/src/json.rs index e5369b4..df78e1b 100644 --- a/compiler/rustc_serialize/src/json.rs +++ b/compiler/rustc_serialize/src/json.rs
@@ -589,6 +589,13 @@ } } + fn emit_fieldless_enum_variant<const ID: usize>( + &mut self, + name: &str, + ) -> Result<(), Self::Error> { + escape_str(self.writer, name) + } + fn emit_enum_variant_arg<F>(&mut self, first: bool, f: F) -> EncodeResult where F: FnOnce(&mut Encoder<'a>) -> EncodeResult, @@ -885,6 +892,13 @@ } } + fn emit_fieldless_enum_variant<const ID: usize>( + &mut self, + name: &str, + ) -> Result<(), Self::Error> { + escape_str(self.writer, name) + } + fn emit_enum_variant_arg<F>(&mut self, first: bool, f: F) -> EncodeResult where F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult,
diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 6671c7c..96a2231 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs
@@ -58,6 +58,20 @@ f(self) } + // We put the field index in a const generic to allow the emit_usize to be + // compiled into a more efficient form. In practice, the variant index is + // known at compile-time, and that knowledge allows much more efficient + // codegen than we'd otherwise get. LLVM isn't always able to make the + // optimization that would otherwise be necessary here, likely due to the + // multiple levels of inlining and const-prop that are needed. + #[inline] + fn emit_fieldless_enum_variant<const ID: usize>( + &mut self, + _v_name: &str, + ) -> Result<(), Self::Error> { + self.emit_usize(ID) + } + #[inline] fn emit_enum_variant_arg<F>(&mut self, _first: bool, f: F) -> Result<(), Self::Error> where @@ -500,8 +514,8 @@ d.read_seq(|d, len| { assert!(len == N); let mut v = [0u8; N]; - for x in &mut v { - *x = d.read_seq_elt(|d| Decodable::decode(d))?; + for i in 0..len { + v[i] = d.read_seq_elt(|d| Decodable::decode(d))?; } Ok(v) })
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index ac4bce7..ab3c122 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs
@@ -37,7 +37,7 @@ use std::path::{Path, PathBuf}; use std::str::{self, FromStr}; -/// The different settings that the `-Z strip` flag can have. +/// The different settings that the `-C strip` flag can have. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum Strip { /// Do not strip at all. @@ -165,6 +165,18 @@ Disabled, } +/// Used with `-Z assert-incr-state`. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum IncrementalStateAssertion { + /// Found and loaded an existing session directory. + /// + /// Note that this says nothing about whether any particular query + /// will be found to be red or green. + Loaded, + /// Did not load an existing session directory. + NotLoaded, +} + impl LinkerPluginLto { pub fn enabled(&self) -> bool { match *self { @@ -174,6 +186,20 @@ } } +/// The different settings that can be enabled via the `-Z location-detail` flag. +#[derive(Clone, PartialEq, Hash, Debug)] +pub struct LocationDetail { + pub file: bool, + pub line: bool, + pub column: bool, +} + +impl LocationDetail { + pub fn all() -> Self { + Self { file: true, line: true, column: true } + } +} + #[derive(Clone, PartialEq, Hash, Debug)] pub enum SwitchWithOptPath { Enabled(Option<PathBuf>), @@ -309,9 +335,10 @@ } /// Parameter to control path trimming. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] pub enum TrimmedDefPaths { /// `try_print_trimmed_def_path` never prints a trimmed path and never calls the expensive query + #[default] Never, /// `try_print_trimmed_def_path` calls the expensive query, the query doesn't call `delay_good_path_bug` Always, @@ -319,12 +346,6 @@ GoodPath, } -impl Default for TrimmedDefPaths { - fn default() -> Self { - Self::Never - } -} - /// Use tree-based collections to cheaply get a deterministic `Hash` implementation. /// *Do not* switch `BTreeMap` out for an unsorted container type! That would break /// dependency tracking for command-line arguments. Also only hash keys, since tracking @@ -512,6 +533,7 @@ TlsModels, TargetSpec, NativeStaticLibs, + StackProtectorStrategies, } #[derive(Copy, Clone)] @@ -564,6 +586,7 @@ pub out_directory: PathBuf, filestem: String, pub single_output_file: Option<PathBuf>, + pub temps_directory: Option<PathBuf>, pub outputs: OutputTypes, } @@ -578,12 +601,14 @@ out_directory: PathBuf, out_filestem: String, single_output_file: Option<PathBuf>, + temps_directory: Option<PathBuf>, extra: String, outputs: OutputTypes, ) -> Self { OutputFilenames { out_directory, single_output_file, + temps_directory, outputs, filestem: format!("{}{}", out_filestem, extra), } @@ -594,7 +619,14 @@ .get(&flavor) .and_then(|p| p.to_owned()) .or_else(|| self.single_output_file.clone()) - .unwrap_or_else(|| self.temp_path(flavor, None)) + .unwrap_or_else(|| self.output_path(flavor)) + } + + /// Gets the output path where a compilation artifact of the given type + /// should be placed on disk. + pub fn output_path(&self, flavor: OutputType) -> PathBuf { + let extension = flavor.extension(); + self.with_directory_and_extension(&self.out_directory, &extension) } /// Gets the path where a compilation artifact of the given type for the @@ -629,11 +661,17 @@ extension.push_str(ext); } - self.with_extension(&extension) + let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory); + + self.with_directory_and_extension(&temps_directory, &extension) } pub fn with_extension(&self, extension: &str) -> PathBuf { - let mut path = self.out_directory.join(&self.filestem); + self.with_directory_and_extension(&self.out_directory, extension) + } + + fn with_directory_and_extension(&self, directory: &PathBuf, extension: &str) -> PathBuf { + let mut path = directory.join(&self.filestem); path.set_extension(extension); path } @@ -674,6 +712,7 @@ impl Default for Options { fn default() -> Options { Options { + assert_incr_state: None, crate_types: Vec::new(), optimize: OptLevel::No, debuginfo: DebugInfo::None, @@ -900,7 +939,7 @@ pub(super) fn build_target_config( opts: &Options, target_override: Option<Target>, - sysroot: &PathBuf, + sysroot: &Path, ) -> Target { let target_result = target_override.map_or_else( || Target::search(&opts.target_triple, sysroot), @@ -1067,8 +1106,8 @@ "print", "Compiler information to print on stdout", "[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\ - target-cpus|target-features|relocation-models|\ - code-models|tls-models|target-spec-json|native-static-libs]", + target-cpus|target-features|relocation-models|code-models|\ + tls-models|target-spec-json|native-static-libs|stack-protector-strategies]", ), opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), @@ -1484,6 +1523,7 @@ "code-models" => PrintRequest::CodeModels, "tls-models" => PrintRequest::TlsModels, "native-static-libs" => PrintRequest::NativeStaticLibs, + "stack-protector-strategies" => PrintRequest::StackProtectorStrategies, "target-spec-json" => { if dopts.unstable_options { PrintRequest::TargetSpec @@ -1596,6 +1636,21 @@ } } +crate fn parse_assert_incr_state( + opt_assertion: &Option<String>, + error_format: ErrorOutputType, +) -> Option<IncrementalStateAssertion> { + match opt_assertion { + Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded), + Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded), + Some(s) => early_error( + error_format, + &format!("unexpected incremental state assertion value: {}", s), + ), + None => None, + } +} + fn parse_native_lib_kind( matches: &getopts::Matches, kind: &str, @@ -1985,6 +2040,9 @@ let incremental = cg.incremental.as_ref().map(PathBuf::from); + let assert_incr_state = + parse_assert_incr_state(&debugging_opts.assert_incr_state, error_format); + if debugging_opts.profile && incremental.is_some() { early_error( error_format, @@ -2149,6 +2207,7 @@ }; Options { + assert_incr_state, crate_types, optimize: opt_level, debuginfo, @@ -2422,7 +2481,7 @@ use super::LdImpl; use super::{ CFGuard, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage, LinkerPluginLto, - LtoCli, OptLevel, OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, + LocationDetail, LtoCli, OptLevel, OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths, }; use crate::lint; @@ -2432,7 +2491,9 @@ use rustc_span::edition::Edition; use rustc_span::RealFileName; use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; - use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, TargetTriple, TlsModel}; + use rustc_target::spec::{ + RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, + }; use std::collections::hash_map::DefaultHasher; use std::collections::BTreeMap; use std::hash::Hash; @@ -2506,6 +2567,7 @@ Edition, LinkerPluginLto, SplitDebuginfo, + StackProtector, SwitchWithOptPath, SymbolManglingVersion, SourceFileHashAlgorithm, @@ -2513,6 +2575,7 @@ Option<LdImpl>, OutputType, RealFileName, + LocationDetail, ); impl<T1, T2> DepTrackingHash for (T1, T2)
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 6c86f86..399b616 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs
@@ -1,4 +1,5 @@ #![feature(crate_visibility_modifier)] +#![feature(derive_default_enum)] #![feature(min_specialization)] #![feature(once_cell)] #![recursion_limit = "256"]
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b3d36b3..4165e75 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs
@@ -4,9 +4,10 @@ use crate::lint; use crate::search_paths::SearchPath; use crate::utils::NativeLib; - use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy, SanitizerSet}; -use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TargetTriple, TlsModel}; +use rustc_target::spec::{ + RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, +}; use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; @@ -150,6 +151,7 @@ /// If `Some`, enable incremental compilation, using the given /// directory to store intermediate results. incremental: Option<PathBuf> [UNTRACKED], + assert_incr_state: Option<IncrementalStateAssertion> [UNTRACKED], debugging_opts: DebuggingOptions [SUBSTRUCT], prints: Vec<PrintRequest> [UNTRACKED], @@ -219,7 +221,7 @@ /// generated code to parse an option into its respective field in the struct. There are a few /// hand-written parsers for parsing specific types of values in this module. macro_rules! options { - ($struct_name:ident, $stat:ident, $prefix:expr, $outputname:expr, + ($struct_name:ident, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr, $($( #[$attr:meta] )* $opt:ident : $t:ty = ( $init:expr, $parse:ident, @@ -264,13 +266,15 @@ } pub const $stat: OptionDescrs<$struct_name> = - &[ $( (stringify!($opt), $opt, desc::$parse, $desc) ),* ]; + &[ $( (stringify!($opt), $optmod::$opt, desc::$parse, $desc) ),* ]; + mod $optmod { $( - fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool { - parse::$parse(&mut redirect_field!(cg.$opt), v) + pub(super) fn $opt(cg: &mut super::$struct_name, v: Option<&str>) -> bool { + super::parse::$parse(&mut redirect_field!(cg.$opt), v) } )* + } ) } @@ -351,8 +355,7 @@ pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; pub const parse_opt_panic_strategy: &str = parse_panic_strategy; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; - pub const parse_sanitizers: &str = - "comma separated list of sanitizers: `address`, `hwaddress`, `leak`, `memory` or `thread`"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory` or `thread`"; pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub const parse_cfguard: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; @@ -368,6 +371,8 @@ "either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted"; pub const parse_linker_plugin_lto: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or the path to the linker plugin"; + pub const parse_location_detail: &str = + "comma seperated list of location details to track: `file`, `line`, or `column`"; pub const parse_switch_with_opt_path: &str = "an optional path to the profiling data output directory"; pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`"; @@ -382,6 +387,8 @@ pub const parse_split_debuginfo: &str = "one of supported split-debuginfo modes (`off`, `packed`, or `unpacked`)"; pub const parse_gcc_ld: &str = "one of: no value, `lld`"; + pub const parse_stack_protector: &str = + "one of (`none` (default), `basic`, `strong`, or `all`)"; } mod parse { @@ -484,6 +491,25 @@ } } + crate fn parse_location_detail(ld: &mut LocationDetail, v: Option<&str>) -> bool { + if let Some(v) = v { + ld.line = false; + ld.file = false; + ld.column = false; + for s in v.split(',') { + match s { + "file" => ld.file = true, + "line" => ld.line = true, + "column" => ld.column = true, + _ => return false, + } + } + true + } else { + false + } + } + crate fn parse_opt_comma_list(slot: &mut Option<Vec<String>>, v: Option<&str>) -> bool { match v { Some(s) => { @@ -584,6 +610,7 @@ for s in v.split(',') { *slot |= match s { "address" => SanitizerSet::ADDRESS, + "cfi" => SanitizerSet::CFI, "leak" => SanitizerSet::LEAK, "memory" => SanitizerSet::MEMORY, "thread" => SanitizerSet::THREAD, @@ -860,7 +887,7 @@ match v { Some(s) => { if !slot.is_empty() { - slot.push_str(","); + slot.push(','); } slot.push_str(s); true @@ -894,10 +921,18 @@ } true } + + crate fn parse_stack_protector(slot: &mut StackProtector, v: Option<&str>) -> bool { + match v.and_then(|s| StackProtector::from_str(s).ok()) { + Some(ssp) => *slot = ssp, + _ => return false, + } + true + } } options! { - CodegenOptions, CG_OPTIONS, "C", "codegen", + CodegenOptions, CG_OPTIONS, cgopts, "C", "codegen", // This list is in alphabetical order. // @@ -992,6 +1027,8 @@ "use soft float ABI (*eabihf targets only) (default: no)"), split_debuginfo: Option<SplitDebuginfo> = (None, parse_split_debuginfo, [TRACKED], "how to handle split-debuginfo, a platform-specific option"), + strip: Strip = (Strip::None, parse_strip, [UNTRACKED], + "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), target_cpu: Option<String> = (None, parse_opt_string, [TRACKED], "select target processor (`rustc --print target-cpus` for details)"), target_feature: String = (String::new(), parse_target_feature, [TRACKED], @@ -1006,7 +1043,7 @@ } options! { - DebuggingOptions, DB_OPTIONS, "Z", "debugging", + DebuggingOptions, DB_OPTIONS, dbopts, "Z", "debugging", // This list is in alphabetical order. // @@ -1021,6 +1058,9 @@ "make cfg(version) treat the current version as incomplete (default: no)"), asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior) (default: no)"), + assert_incr_state: Option<String> = (None, parse_opt_string, [UNTRACKED], + "assert that the incremental cache is in given state: \ + either `loaded` or `not-loaded`."), ast_json: bool = (false, parse_bool, [UNTRACKED], "print the AST as JSON and halt (default: no)"), ast_json_noexpand: bool = (false, parse_bool, [UNTRACKED], @@ -1091,8 +1131,6 @@ fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), - force_overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED], - "force overflow checks on or off"), force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], "force all crates to be `rustc_private` unstable (default: no)"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], @@ -1152,6 +1190,9 @@ "a list LLVM plugins to enable (space separated)"), llvm_time_trace: bool = (false, parse_bool, [UNTRACKED], "generate JSON tracing data file from LLVM data (default: no)"), + location_detail: LocationDetail = (LocationDetail::all(), parse_location_detail, [TRACKED], + "comma seperated list of location details to be tracked when using caller_location \ + valid options are `file`, `line`, and `column` (default: all)"), ls: bool = (false, parse_bool, [UNTRACKED], "list the symbols defined by a library crate (default: no)"), macro_backtrace: bool = (false, parse_bool, [UNTRACKED], @@ -1169,7 +1210,7 @@ move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED], "the size at which the `large_assignments` lint starts to be emitted"), mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED], - "emit noalias metadata for mutable references (default: yes for LLVM >= 12, otherwise no)"), + "emit noalias metadata for mutable references (default: yes)"), new_llvm_pass_manager: Option<bool> = (None, parse_opt_bool, [TRACKED], "use new LLVM pass manager (default: no)"), nll_facts: bool = (false, parse_bool, [UNTRACKED], @@ -1190,6 +1231,8 @@ "compile without linking"), no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED], "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), + no_unique_section_names: bool = (false, parse_bool, [TRACKED], + "do not use unique names for text and data sections when -Z function-sections is used"), no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED], "prevent automatic injection of the profiler_builtins crate"), normalize_docs: bool = (false, parse_bool, [TRACKED], @@ -1207,6 +1250,8 @@ and set the maximum total size of a const allocation for which this is allowed (default: never)"), perf_stats: bool = (false, parse_bool, [UNTRACKED], "print some performance-related statistics (default: no)"), + pick_stable_methods_before_any_unstable: bool = (true, parse_bool, [TRACKED], + "try to pick stable methods first before picking any unstable methods (default: yes)"), plt: Option<bool> = (None, parse_opt_bool, [TRACKED], "whether to use the PLT when calling into shared libraries; only has effect for PIC code on systems with ELF binaries @@ -1283,7 +1328,7 @@ "specify the events recorded by the self profiler; for example: `-Z self-profile-events=default,query-keys` all options: none, all, default, generic-activity, query-provider, query-cache-hit - query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm"), + query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"), share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED], "make the current crate share its generic instantiations"), show_span: Option<String> = (None, parse_opt_string, [TRACKED], @@ -1295,6 +1340,8 @@ "exclude spans when debug-printing compiler state (default: no)"), src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED], "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), + stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED], + "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED], @@ -1305,6 +1352,8 @@ "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), teach: bool = (false, parse_bool, [TRACKED], "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"), tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index a007b53..d5b5203 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs
@@ -119,8 +119,13 @@ pub config: CrateConfig, pub edition: Edition, pub missing_fragment_specifiers: Lock<FxHashMap<Span, NodeId>>, - /// Places where raw identifiers were used. This is used for feature-gating raw identifiers. + /// Places where raw identifiers were used. This is used to avoid complaining about idents + /// clashing with keywords in new editions. pub raw_identifier_spans: Lock<Vec<Span>>, + /// Places where identifiers that contain invalid Unicode codepoints but that look like they + /// should be. Useful to avoid bad tokenization when encountering emoji. We group them to + /// provide a single error per unique incorrect identifier. + pub bad_unicode_identifiers: Lock<FxHashMap<Symbol, Vec<Span>>>, source_map: Lrc<SourceMap>, pub buffered_lints: Lock<Vec<BufferedEarlyLint>>, /// Contains the spans of block expressions that could have been incomplete based on the @@ -160,6 +165,7 @@ edition: ExpnId::root().expn_data().edition, missing_fragment_specifiers: Default::default(), raw_identifier_spans: Lock::new(Vec::new()), + bad_unicode_identifiers: Lock::new(Default::default()), source_map, buffered_lints: Lock::new(vec![]), ambiguous_block_expr_parse: Lock::new(FxHashMap::default()), @@ -174,9 +180,14 @@ } } - pub fn with_silent_emitter() -> Self { + pub fn with_silent_emitter(fatal_note: Option<String>) -> Self { let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let handler = Handler::with_emitter(false, None, Box::new(SilentEmitter)); + let fatal_handler = Handler::with_tty_emitter(ColorConfig::Auto, false, None, None); + let handler = Handler::with_emitter( + false, + None, + Box::new(SilentEmitter { fatal_handler, fatal_note }), + ); ParseSess::with_span_handler(handler, sm) }
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index b6ba6cc..5410955 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs
@@ -27,7 +27,9 @@ use rustc_span::{sym, SourceFileHashAlgorithm, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; -use rustc_target::spec::{SanitizerSet, SplitDebuginfo, Target, TargetTriple, TlsModel}; +use rustc_target::spec::{ + SanitizerSet, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel, +}; use std::cell::{self, RefCell}; use std::env; @@ -411,7 +413,7 @@ self.diagnostic().abort_if_errors(); } pub fn compile_status(&self) -> Result<(), ErrorReported> { - if self.has_errors() { + if self.diagnostic().has_errors_or_lint_errors() { self.diagnostic().emit_stashed_diagnostics(); Err(ErrorReported) } else { @@ -672,12 +674,11 @@ pub fn is_nightly_build(&self) -> bool { self.opts.unstable_features.is_nightly_build() } + pub fn is_sanitizer_cfi_enabled(&self) -> bool { + self.opts.debugging_opts.sanitizer.contains(SanitizerSet::CFI) + } pub fn overflow_checks(&self) -> bool { - self.opts - .cg - .overflow_checks - .or(self.opts.debugging_opts.force_overflow_checks) - .unwrap_or(self.opts.debug_assertions) + self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) } /// Check whether this compile session and crate type use static crt. @@ -729,6 +730,14 @@ self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) } + pub fn stack_protector(&self) -> StackProtector { + if self.target.options.supports_stack_protector { + self.opts.debugging_opts.stack_protector + } else { + StackProtector::None + } + } + pub fn target_can_use_split_dwarf(&self) -> bool { !self.target.is_like_windows && !self.target.is_like_osx } @@ -1398,6 +1407,25 @@ disable it using `-C target-feature=-crt-static`", ); } + + // LLVM CFI requires LTO. + if sess.is_sanitizer_cfi_enabled() { + if sess.opts.cg.lto == config::LtoCli::Unspecified + || sess.opts.cg.lto == config::LtoCli::No + || sess.opts.cg.lto == config::LtoCli::Thin + { + sess.err("`-Zsanitizer=cfi` requires `-Clto`"); + } + } + + if sess.opts.debugging_opts.stack_protector != StackProtector::None { + if !sess.target.options.supports_stack_protector { + sess.warn(&format!( + "`-Z stack-protector={}` is not supported for target {} and will be ignored", + sess.opts.debugging_opts.stack_protector, sess.opts.target_triple + )) + } + } } /// Holds data on the current incremental compilation session, if there is one.
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index aa15feb..d590776 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs
@@ -709,7 +709,7 @@ /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty. /// } - /// n(f); + /// n!(f); /// macro n($j:ident) { /// use foo::*; /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n` @@ -1099,18 +1099,11 @@ OpaqueTy, Async, Await, - ForLoop(ForLoopLoc), + ForLoop, LetElse, WhileLoop, } -/// A location in the desugaring of a `for` loop -#[derive(Clone, Copy, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)] -pub enum ForLoopLoc { - Head, - IntoIter, -} - impl DesugaringKind { /// The description wording should combine well with "desugaring of {}". pub fn descr(self) -> &'static str { @@ -1121,7 +1114,7 @@ DesugaringKind::QuestionMark => "operator `?`", DesugaringKind::TryBlock => "`try` block", DesugaringKind::OpaqueTy => "`impl Trait`", - DesugaringKind::ForLoop(_) => "`for` loop", + DesugaringKind::ForLoop => "`for` loop", DesugaringKind::LetElse => "`let...else`", DesugaringKind::WhileLoop => "`while` loop", }
diff --git a/compiler/rustc_span/src/lev_distance.rs b/compiler/rustc_span/src/lev_distance.rs index cea7871..c10968e 100644 --- a/compiler/rustc_span/src/lev_distance.rs +++ b/compiler/rustc_span/src/lev_distance.rs
@@ -58,34 +58,28 @@ let lookup = &lookup.as_str(); let max_dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3); - let (case_insensitive_match, levenshtein_match) = name_vec + // Priority of matches: + // 1. Exact case insensitive match + // 2. Levenshtein distance match + // 3. Sorted word match + if let Some(case_insensitive_match) = + name_vec.iter().find(|candidate| candidate.as_str().to_uppercase() == lookup.to_uppercase()) + { + return Some(*case_insensitive_match); + } + let levenshtein_match = name_vec .iter() .filter_map(|&name| { let dist = lev_distance(lookup, &name.as_str()); if dist <= max_dist { Some((name, dist)) } else { None } }) // Here we are collecting the next structure: - // (case_insensitive_match, (levenshtein_match, levenshtein_distance)) - .fold((None, None), |result, (candidate, dist)| { - ( - if candidate.as_str().to_uppercase() == lookup.to_uppercase() { - Some(candidate) - } else { - result.0 - }, - match result.1 { - None => Some((candidate, dist)), - Some((c, d)) => Some(if dist < d { (candidate, dist) } else { (c, d) }), - }, - ) + // (levenshtein_match, levenshtein_distance) + .fold(None, |result, (candidate, dist)| match result { + None => Some((candidate, dist)), + Some((c, d)) => Some(if dist < d { (candidate, dist) } else { (c, d) }), }); - // Priority of matches: - // 1. Exact case insensitive match - // 2. Levenshtein distance match - // 3. Sorted word match - if let Some(candidate) = case_insensitive_match { - Some(candidate) - } else if levenshtein_match.is_some() { + if levenshtein_match.is_some() { levenshtein_match.map(|(candidate, _)| candidate) } else { find_match_by_sorted_words(name_vec, lookup)
diff --git a/compiler/rustc_span/src/lev_distance/tests.rs b/compiler/rustc_span/src/lev_distance/tests.rs index 11822e9..b32f8d3 100644 --- a/compiler/rustc_span/src/lev_distance/tests.rs +++ b/compiler/rustc_span/src/lev_distance/tests.rs
@@ -31,15 +31,11 @@ assert_eq!(find_best_match_for_name(&input, Symbol::intern("1111111111"), None), None); - let input = vec![Symbol::intern("aAAA")]; - assert_eq!( - find_best_match_for_name(&input, Symbol::intern("AAAA"), None), - Some(Symbol::intern("aAAA")) - ); - let input = vec![Symbol::intern("AAAA")]; - // Returns None because `lev_distance > max_dist / 3` - assert_eq!(find_best_match_for_name(&input, Symbol::intern("aaaa"), None), None); + assert_eq!( + find_best_match_for_name(&input, Symbol::intern("aaaa"), None), + Some(Symbol::intern("AAAA")) + ); let input = vec![Symbol::intern("AAAA")]; assert_eq!(
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 032ae73..66c0114 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs
@@ -41,7 +41,7 @@ use edition::Edition; pub mod hygiene; use hygiene::Transparency; -pub use hygiene::{DesugaringKind, ExpnKind, ForLoopLoc, MacroKind}; +pub use hygiene::{DesugaringKind, ExpnKind, MacroKind}; pub use hygiene::{ExpnData, ExpnHash, ExpnId, LocalExpnId, SyntaxContext}; pub mod def_id; use def_id::{CrateNum, DefId, DefPathHash, LocalDefId, LOCAL_CRATE}; @@ -194,10 +194,8 @@ encoder.emit_enum(|encoder| match *self { RealFileName::LocalPath(ref local_path) => { encoder.emit_enum_variant("LocalPath", 0, 1, |encoder| { - Ok({ - encoder - .emit_enum_variant_arg(true, |encoder| local_path.encode(encoder))?; - }) + encoder.emit_enum_variant_arg(true, |encoder| local_path.encode(encoder))?; + Ok(()) }) } @@ -206,12 +204,9 @@ // For privacy and build reproducibility, we must not embed host-dependant path in artifacts // if they have been remapped by --remap-path-prefix assert!(local_path.is_none()); - Ok({ - encoder - .emit_enum_variant_arg(true, |encoder| local_path.encode(encoder))?; - encoder - .emit_enum_variant_arg(false, |encoder| virtual_name.encode(encoder))?; - }) + encoder.emit_enum_variant_arg(true, |encoder| local_path.encode(encoder))?; + encoder.emit_enum_variant_arg(false, |encoder| virtual_name.encode(encoder))?; + Ok(()) }), }) } @@ -1940,6 +1935,7 @@ #[derive(Debug)] pub struct SourceFileAndLine { pub sf: Lrc<SourceFile>, + /// Index of line, starting from 0. pub line: usize, } #[derive(Debug)]
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 74958c4..7414d20 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs
@@ -593,14 +593,19 @@ } pub fn span_to_margin(&self, sp: Span) -> Option<usize> { - match self.span_to_prev_source(sp) { - Err(_) => None, - Ok(source) => { - let last_line = source.rsplit_once('\n').unwrap_or(("", &source)).1; + Some(self.indentation_before(sp)?.len()) + } - Some(last_line.len() - last_line.trim_start().len()) - } - } + pub fn indentation_before(&self, sp: Span) -> Option<String> { + self.span_to_source(sp, |src, start_index, _| { + let before = &src[..start_index]; + let last_line = before.rsplit_once('\n').map_or(before, |(_, last)| last); + Ok(last_line + .split_once(|c: char| !c.is_whitespace()) + .map_or(last_line, |(indent, _)| indent) + .to_string()) + }) + .ok() } /// Returns the source snippet as `String` before the given `Span`.
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 16205ad..247d69d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs
@@ -269,8 +269,8 @@ __D, __H, __S, - __next, __try_var, + _args, _d, _e, _task_context, @@ -307,6 +307,7 @@ alloc_layout, alloc_zeroed, allocator, + allocator_api, allocator_internals, allow, allow_fail, @@ -327,6 +328,9 @@ as_ptr, as_str, asm, + asm_const, + asm_experimental_arch, + asm_sym, assert, assert_inhabited, assert_macro, @@ -355,7 +359,6 @@ await_macro, bang, begin_panic, - begin_panic_fmt, bench, bin, bind_by_move_pattern_guards, @@ -408,6 +411,7 @@ cfg_target_thread_local, cfg_target_vendor, cfg_version, + cfi, char, client, clippy, @@ -675,6 +679,7 @@ gen_future, gen_kill, generator, + generator_return, generator_state, generators, generic_arg_infer, @@ -728,6 +733,7 @@ inlateout, inline, inline_const, + inline_const_pat, inout, instruction_set, intel, @@ -1128,7 +1134,6 @@ rustc_partition_reused, rustc_peek, rustc_peek_definite_init, - rustc_peek_indirectly_mutable, rustc_peek_liveness, rustc_peek_maybe_init, rustc_peek_maybe_uninit, @@ -1143,14 +1148,15 @@ rustc_specialization_trait, rustc_stable, rustc_std_internal_symbol, + rustc_strict_coherence, rustc_symbol_name, - rustc_synthetic, rustc_test_marker, rustc_then_this_would_need, rustc_trivial_field_reads, rustc_unsafe_specialization_marker, rustc_variance, rustdoc, + rustdoc_internals, rustfmt, rvalue_static_promotion, s, @@ -1340,6 +1346,7 @@ type_alias_enum_variants, type_alias_impl_trait, type_ascription, + type_changing_struct_update, type_id, type_length_limit, type_macros, @@ -1454,7 +1461,7 @@ } #[inline] - pub fn invalid() -> Ident { + pub fn empty() -> Ident { Ident::with_dummy_span(kw::Empty) }
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 220c9f7..bb7b452 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -103,8 +103,9 @@ use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Instance, TyCtxt}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::config::SymbolManglingVersion; +use rustc_target::abi::call::FnAbi; use tracing::debug; @@ -150,6 +151,11 @@ ty::SymbolName::new(tcx, &symbol_name) } +/// This function computes the typeid for the given function ABI. +pub fn typeid_for_fnabi(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String { + v0::mangle_typeid_for_fnabi(tcx, fn_abi) +} + /// Computes the symbol name for the given instance. This function will call /// `compute_instantiating_crate` if it needs to factor the instantiating crate /// into the symbol name.
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 521730d..0363ddb 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -9,6 +9,7 @@ use rustc_middle::ty::print::{Print, Printer}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{self, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeFoldable, UintTy}; +use rustc_target::abi::call::FnAbi; use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; @@ -55,6 +56,41 @@ std::mem::take(&mut cx.out) } +pub(super) fn mangle_typeid_for_fnabi( + _tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, +) -> String { + // LLVM uses type metadata to allow IR modules to aggregate pointers by their types.[1] This + // type metadata is used by LLVM Control Flow Integrity to test whether a given pointer is + // associated with a type identifier (i.e., test type membership). + // + // Clang uses the Itanium C++ ABI's[2] virtual tables and RTTI typeinfo structure name[3] as + // type metadata identifiers for function pointers. The typeinfo name encoding is a + // two-character code (i.e., “TS”) prefixed to the type encoding for the function. + // + // For cross-language LLVM CFI support, a compatible encoding must be used by either + // + // a. Using a superset of types that encompasses types used by Clang (i.e., Itanium C++ ABI's + // type encodings[4]), or at least types used at the FFI boundary. + // b. Reducing the types to the least common denominator between types used by Clang (or at + // least types used at the FFI boundary) and Rust compilers (if even possible). + // c. Creating a new ABI for cross-language CFI and using it for Clang and Rust compilers (and + // possibly other compilers). + // + // Option (b) may weaken the protection for Rust-compiled only code, so it should be provided + // as an alternative to a Rust-specific encoding for when mixing Rust and C and C++ -compiled + // code. Option (c) would require changes to Clang to use the new ABI. + // + // [1] https://llvm.org/docs/TypeMetadata.html + // [2] https://itanium-cxx-abi.github.io/cxx-abi/abi.html + // [3] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-special-vtables + // [4] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-type + // + // FIXME(rcvalle): See comment above. + let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize; + format!("typeid{}", arg_count) +} + struct BinderLevel { /// The range of distances from the root of what's /// being printed, to the lifetimes in a binder.
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index d9eb299..4768c9e 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -68,8 +68,10 @@ const NonNull = 1 << 3; const ReadOnly = 1 << 4; const InReg = 1 << 5; - // NoAlias on &mut arguments can only be used with LLVM >= 12 due to miscompiles - // in earlier versions. FIXME: Remove this distinction once possible. + // Due to past miscompiles in LLVM, we use a separate attribute for + // &mut arguments, so that the codegen backend can decide whether + // or not to actually emit the attribute. It can also be controlled + // with the `-Zmutable-noalias` debugging option. const NoAliasMutRef = 1 << 6; } }
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 99699c5..bff1324 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs
@@ -189,6 +189,7 @@ S390x, SpirV, Wasm32, + Wasm64, Bpf, } @@ -212,6 +213,7 @@ "s390x" => Ok(Self::S390x), "spirv" => Ok(Self::SpirV), "wasm32" => Ok(Self::Wasm32), + "wasm64" => Ok(Self::Wasm64), "bpf" => Ok(Self::Bpf), _ => Err(()), } @@ -318,7 +320,7 @@ InlineAsmArch::SpirV => { Self::SpirV(SpirVInlineAsmReg::parse(arch, has_feature, target, &name)?) } - InlineAsmArch::Wasm32 => { + InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { Self::Wasm(WasmInlineAsmReg::parse(arch, has_feature, target, &name)?) } InlineAsmArch::Bpf => { @@ -529,7 +531,9 @@ } InlineAsmArch::S390x => Self::S390x(S390xInlineAsmRegClass::parse(arch, name)?), InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::Wasm32 => Self::Wasm(WasmInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { + Self::Wasm(WasmInlineAsmRegClass::parse(arch, name)?) + } InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(arch, name)?), }) } @@ -725,7 +729,7 @@ spirv::fill_reg_map(arch, has_feature, target, &mut map); map } - InlineAsmArch::Wasm32 => { + InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { let mut map = wasm::regclass_map(); wasm::fill_reg_map(arch, has_feature, target, &mut map); map
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs index dc91f12..f01ff02 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs
@@ -2,11 +2,11 @@ pub fn target() -> Target { let mut base = super::apple_base::opts("macos"); - base.cpu = "apple-a12".to_string(); + base.cpu = "apple-a14".to_string(); base.max_atomic_width = Some(128); // FIXME: The leak sanitizer currently fails the tests, see #88132. - base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD; + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD; base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".to_string(), "arm64".to_string()]); base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); @@ -14,14 +14,13 @@ // Clang automatically chooses a more specific target based on // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work // correctly, we do too. - let arch = "aarch64"; - let llvm_target = super::apple_base::macos_llvm_target(&arch); + let llvm_target = super::apple_base::macos_llvm_target("arm64"); Target { llvm_target, pointer_width: 64, data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), - arch: arch.to_string(), + arch: "aarch64".to_string(), options: TargetOptions { mcount: "\u{1}mcount".to_string(), frame_pointer: FramePointer::NonLeaf,
diff --git a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs index 71ee6de..a393858 100644 --- a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs
@@ -8,6 +8,7 @@ data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), options: TargetOptions { + features: "+outline-atomics".to_string(), max_atomic_width: Some(128), mcount: "\u{1}_mcount".to_string(), endian: Endian::Big,
diff --git a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs index e05360e..e75100f 100644 --- a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs +++ b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs
@@ -12,6 +12,7 @@ arch: "aarch64".to_string(), options: TargetOptions { abi: "ilp32".to_string(), + features: "+outline-atomics".to_string(), mcount: "\u{1}_mcount".to_string(), endian: Endian::Big, ..base
diff --git a/compiler/rustc_target/src/spec/aarch64_fuchsia.rs b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs index 56d71df..05e0c65 100644 --- a/compiler/rustc_target/src/spec/aarch64_fuchsia.rs +++ b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs
@@ -8,7 +8,7 @@ arch: "aarch64".to_string(), options: TargetOptions { max_atomic_width: Some(128), - supported_sanitizers: SanitizerSet::ADDRESS, + supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::CFI, ..super::fuchsia_base::opts() }, }
diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs index 409cab7..1e9abbb 100644 --- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs
@@ -14,7 +14,7 @@ // As documented in https://developer.android.com/ndk/guides/cpu-features.html // the neon (ASIMD) and FP must exist on all android aarch64 targets. features: "+neon,+fp-armv8".to_string(), - supported_sanitizers: SanitizerSet::HWADDRESS, + supported_sanitizers: SanitizerSet::CFI | SanitizerSet::HWADDRESS, ..super::android_base::opts() }, }
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs index 0caecd2..03ee7ba 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs
@@ -9,6 +9,7 @@ options: TargetOptions { max_atomic_width: Some(128), supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::CFI | SanitizerSet::MEMORY | SanitizerSet::THREAD, ..super::freebsd_base::opts()
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs index 3e92ecb..850381f 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs
@@ -7,9 +7,11 @@ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), options: TargetOptions { + features: "+outline-atomics".to_string(), mcount: "\u{1}_mcount".to_string(), max_atomic_width: Some(128), supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs index 8522405..1c931d5 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs
@@ -8,6 +8,7 @@ arch: "aarch64".to_string(), options: TargetOptions { abi: "ilp32".to_string(), + features: "+outline-atomics".to_string(), max_atomic_width: Some(128), mcount: "\u{1}_mcount".to_string(), ..super::linux_gnu_base::opts()
diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index a21b784..db6aee5 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs
@@ -13,8 +13,10 @@ // warnings about the usage of ELF TLS. // // Here we detect what version is being requested, defaulting to 10.7. ELF - // TLS is flagged as enabled if it looks to be supported. - let version = macos_deployment_target(); + // TLS is flagged as enabled if it looks to be supported. The architecture + // only matters for default deployment target which is 11.0 for ARM64 and + // 10.7 for everything else. + let has_elf_tls = macos_deployment_target("x86_64") >= (10, 7); TargetOptions { os: os.to_string(), @@ -31,7 +33,7 @@ has_rpath: true, dll_suffix: ".dylib".to_string(), archive_format: "darwin".to_string(), - has_elf_tls: version >= (10, 7), + has_elf_tls, abi_return_struct_as_int: true, emit_debug_gdb_scripts: false, eh_frame_header: false, @@ -63,12 +65,17 @@ .and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok()) } -fn macos_deployment_target() -> (u32, u32) { - deployment_target("MACOSX_DEPLOYMENT_TARGET").unwrap_or((10, 7)) +fn macos_default_deployment_target(arch: &str) -> (u32, u32) { + if arch == "arm64" { (11, 0) } else { (10, 7) } +} + +fn macos_deployment_target(arch: &str) -> (u32, u32) { + deployment_target("MACOSX_DEPLOYMENT_TARGET") + .unwrap_or_else(|| macos_default_deployment_target(arch)) } pub fn macos_llvm_target(arch: &str) -> String { - let (major, minor) = macos_deployment_target(); + let (major, minor) = macos_deployment_target(arch); format!("{}-apple-macosx{}.{}.0", arch, major, minor) }
diff --git a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs index 01f5c19..afe8bbb 100644 --- a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs +++ b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
@@ -1,4 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; +use crate::spec::{LinkArgs, LinkerFlavor, RelocModel, Target, TargetOptions}; /// A base target for Nintendo 3DS devices using the devkitARM toolchain. /// @@ -36,7 +36,6 @@ features: "+vfp2".to_string(), pre_link_args, exe_suffix: ".elf".to_string(), - panic_strategy: PanicStrategy::Abort, ..Default::default() }, }
diff --git a/compiler/rustc_target/src/spec/hermit_kernel_base.rs b/compiler/rustc_target/src/spec/hermit_kernel_base.rs index c55a46e..ce3dad2 100644 --- a/compiler/rustc_target/src/spec/hermit_kernel_base.rs +++ b/compiler/rustc_target/src/spec/hermit_kernel_base.rs
@@ -1,4 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions, TlsModel}; +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions}; pub fn opts() -> TargetOptions { let mut pre_link_args = LinkArgs::new(); @@ -8,17 +8,14 @@ ); TargetOptions { - os: "hermit".to_string(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), disable_redzone: true, linker: Some("rust-lld".to_owned()), executables: true, - has_elf_tls: true, pre_link_args, panic_strategy: PanicStrategy::Abort, position_independent_executables: true, static_position_independent_executables: true, - tls_model: TlsModel::InitialExec, ..Default::default() } }
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ff5dfa3..0d49c7f 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs
@@ -602,6 +602,7 @@ const MEMORY = 1 << 2; const THREAD = 1 << 3; const HWADDRESS = 1 << 4; + const CFI = 1 << 5; } } @@ -612,6 +613,7 @@ fn as_str(self) -> Option<&'static str> { Some(match self { SanitizerSet::ADDRESS => "address", + SanitizerSet::CFI => "cfi", SanitizerSet::LEAK => "leak", SanitizerSet::MEMORY => "memory", SanitizerSet::THREAD => "thread", @@ -644,6 +646,7 @@ fn into_iter(self) -> Self::IntoIter { [ SanitizerSet::ADDRESS, + SanitizerSet::CFI, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD, @@ -709,6 +712,59 @@ } } +/// Controls use of stack canaries. +#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)] +pub enum StackProtector { + /// Disable stack canary generation. + None, + + /// On LLVM, mark all generated LLVM functions with the `ssp` attribute (see + /// llvm/docs/LangRef.rst). This triggers stack canary generation in + /// functions which contain an array of a byte-sized type with more than + /// eight elements. + Basic, + + /// On LLVM, mark all generated LLVM functions with the `sspstrong` + /// attribute (see llvm/docs/LangRef.rst). This triggers stack canary + /// generation in functions which either contain an array, or which take + /// the address of a local variable. + Strong, + + /// Generate stack canaries in all functions. + All, +} + +impl StackProtector { + fn as_str(&self) -> &'static str { + match self { + StackProtector::None => "none", + StackProtector::Basic => "basic", + StackProtector::Strong => "strong", + StackProtector::All => "all", + } + } +} + +impl FromStr for StackProtector { + type Err = (); + + fn from_str(s: &str) -> Result<StackProtector, ()> { + Ok(match s { + "none" => StackProtector::None, + "basic" => StackProtector::Basic, + "strong" => StackProtector::Strong, + "all" => StackProtector::All, + _ => return Err(()), + }) + } +} + +impl fmt::Display for StackProtector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + macro_rules! supported_targets { ( $(($( $triple:literal, )+ $module:ident ),)+ ) => { $(mod $module;)+ @@ -954,6 +1010,8 @@ ("armv6k-nintendo-3ds", armv6k_nintendo_3ds), ("armv7-unknown-linux-uclibceabihf", armv7_unknown_linux_uclibceabihf), + + ("x86_64-unknown-none", x86_64_unknown_none), } /// Warnings encountered when parsing the target `json`. @@ -1352,6 +1410,13 @@ /// Minimum number of bits in #[repr(C)] enum. Defaults to 32. pub c_enum_min_bits: u64, + + /// Whether or not the DWARF `.debug_aranges` section should be generated. + pub generate_arange_section: bool, + + /// Whether the target supports stack canary checks. `true` by default, + /// since this is most common among tier 1 and tier 2 targets. + pub supports_stack_protector: bool, } impl Default for TargetOptions { @@ -1457,6 +1522,8 @@ supported_sanitizers: SanitizerSet::empty(), default_adjusted_cabi: None, c_enum_min_bits: 32, + generate_arange_section: true, + supports_stack_protector: true, } } } @@ -1522,6 +1589,7 @@ AmdGpuKernel => self.arch == "amdgcn", AvrInterrupt | AvrNonBlockingInterrupt => self.arch == "avr", Wasm => ["wasm32", "wasm64"].contains(&&self.arch[..]), + Thiscall { .. } => self.arch == "x86", // On windows these fall-back to platform native calling convention (C) when the // architecture is not supported. // @@ -1552,15 +1620,13 @@ // > convention is used. // // -- https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions - Stdcall { .. } | Fastcall | Thiscall { .. } | Vectorcall if self.is_like_windows => { - true - } + Stdcall { .. } | Fastcall | Vectorcall if self.is_like_windows => true, // Outside of Windows we want to only support these calling conventions for the // architectures for which these calling conventions are actually well defined. - Stdcall { .. } | Fastcall | Thiscall { .. } if self.arch == "x86" => true, + Stdcall { .. } | Fastcall if self.arch == "x86" => true, Vectorcall if ["x86", "x86_64"].contains(&&self.arch[..]) => true, // Return a `None` for other cases so that we know to emit a future compat lint. - Stdcall { .. } | Fastcall | Thiscall { .. } | Vectorcall => return None, + Stdcall { .. } | Fastcall | Vectorcall => return None, }) } @@ -1805,6 +1871,7 @@ for s in a { base.$key_name |= match s.as_string() { Some("address") => SanitizerSet::ADDRESS, + Some("cfi") => SanitizerSet::CFI, Some("leak") => SanitizerSet::LEAK, Some("memory") => SanitizerSet::MEMORY, Some("thread") => SanitizerSet::THREAD, @@ -2042,6 +2109,8 @@ key!(supported_sanitizers, SanitizerSet)?; key!(default_adjusted_cabi, Option<Abi>)?; key!(c_enum_min_bits, u64); + key!(generate_arange_section, bool); + key!(supports_stack_protector, bool); if base.is_builtin { // This can cause unfortunate ICEs later down the line. @@ -2066,7 +2135,7 @@ /// JSON decoding. pub fn search( target_triple: &TargetTriple, - sysroot: &PathBuf, + sysroot: &Path, ) -> Result<(Target, TargetWarnings), String> { use rustc_serialize::json; use std::env; @@ -2281,6 +2350,8 @@ target_option_val!(split_debuginfo); target_option_val!(supported_sanitizers); target_option_val!(c_enum_min_bits); + target_option_val!(generate_arange_section); + target_option_val!(supports_stack_protector); if let Some(abi) = self.default_adjusted_cabi { d.insert("default-adjusted-cabi".to_string(), Abi::name(abi).to_json());
diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs index 083262c..ba32a31 100644 --- a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
@@ -44,6 +44,10 @@ // produce kernel functions that call other kernel functions. // This behavior is not supported by PTX ISA. merge_functions: MergeFunctions::Disabled, + + // The LLVM backend does not support stack canaries for this target + supports_stack_protector: false, + ..Default::default() }, }
diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs index 86b1a75..69a404e 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
@@ -37,7 +37,7 @@ is_like_emscripten: true, panic_strategy: PanicStrategy::Unwind, post_link_args, - families: vec!["unix".to_string()], + families: vec!["unix".to_string(), "wasm".to_string()], ..options }; Target {
diff --git a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs index fb6526c..6b7dfbb 100644 --- a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
@@ -23,11 +23,15 @@ // For now this target just never has an entry symbol no matter the output // type, so unconditionally pass this. clang_args.push("-Wl,--no-entry".to_string()); - options - .pre_link_args - .get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)) - .unwrap() - .push("--no-entry".to_string()); + + let lld_args = options.pre_link_args.get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)).unwrap(); + lld_args.push("--no-entry".to_string()); + lld_args.push("-mwasm64".to_string()); + + // Any engine that implements wasm64 will surely implement the rest of these + // features since they were all merged into the official spec by the time + // wasm64 was designed. + options.features = "+bulk-memory,+mutable-globals,+sign-ext,+nontrapping-fptoint".to_string(); Target { llvm_target: "wasm64-unknown-unknown".to_string(),
diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs index 4c954a1..24e9c62 100644 --- a/compiler/rustc_target/src/spec/wasm_base.rs +++ b/compiler/rustc_target/src/spec/wasm_base.rs
@@ -128,6 +128,12 @@ // gdb scripts don't work on wasm blobs emit_debug_gdb_scripts: false, + // There's more discussion of this at + // https://bugs.llvm.org/show_bug.cgi?id=52442 but the general result is + // that this isn't useful for wasm and has tricky issues with + // representation, so this is disabled. + generate_arange_section: false, + ..Default::default() } }
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs index 60fd429..22fdaab 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs
@@ -13,7 +13,8 @@ base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; - base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD; + base.supported_sanitizers = + SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::THREAD; // Clang automatically chooses a more specific target based on // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
diff --git a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs index aa65ebe..c253c0c 100644 --- a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs +++ b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs
@@ -6,7 +6,7 @@ base.max_atomic_width = Some(64); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; - base.supported_sanitizers = SanitizerSet::ADDRESS; + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI; Target { llvm_target: "x86_64-fuchsia".to_string(),
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs b/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs index 34b6d29..6aa0728 100644 --- a/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs +++ b/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs
@@ -8,7 +8,7 @@ base.max_atomic_width = Some(64); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; - base.supported_sanitizers = SanitizerSet::ADDRESS; + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI; Target { llvm_target: "x86_64-pc-solaris".to_string(),
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs index b5fc15f..24cc7ae 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs
@@ -7,7 +7,8 @@ base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; - base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supported_sanitizers = + SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::MEMORY | SanitizerSet::THREAD; Target { llvm_target: "x86_64-unknown-freebsd".to_string(),
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs index ec196a7..79ccf63 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs
@@ -5,7 +5,7 @@ base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string(), "-std=c99".to_string()]); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.supported_sanitizers = SanitizerSet::ADDRESS; + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI; Target { // LLVM does not currently have a separate illumos target,
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs index 085079e..c2484f2 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs
@@ -7,8 +7,11 @@ base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; - base.supported_sanitizers = - SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supported_sanitizers = SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::LEAK + | SanitizerSet::MEMORY + | SanitizerSet::THREAD; Target { llvm_target: "x86_64-unknown-linux-gnu".to_string(),
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs index 5ad243a..a5e7980 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs
@@ -8,8 +8,11 @@ // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; base.static_position_independent_executables = true; - base.supported_sanitizers = - SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supported_sanitizers = SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::LEAK + | SanitizerSet::MEMORY + | SanitizerSet::THREAD; Target { llvm_target: "x86_64-unknown-linux-musl".to_string(),
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs index 9ba8628..bdb2be4 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs
@@ -7,8 +7,11 @@ base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; - base.supported_sanitizers = - SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supported_sanitizers = SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::LEAK + | SanitizerSet::MEMORY + | SanitizerSet::THREAD; Target { llvm_target: "x86_64-unknown-netbsd".to_string(),
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs new file mode 100644 index 0000000..722409d --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
@@ -0,0 +1,41 @@ +// Generic x86-64 target for bare-metal code - Floating point disabled +// +// Can be used in conjunction with the `target-feature` and +// `target-cpu` compiler flags to opt-in more hardware-specific +// features. + +use super::{ + CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, RelroLevel, StackProbeType, + Target, TargetOptions, +}; + +pub fn target() -> Target { + let opts = TargetOptions { + cpu: "x86-64".to_string(), + max_atomic_width: Some(64), + // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved + stack_probes: StackProbeType::Call, + position_independent_executables: true, + static_position_independent_executables: true, + relro_level: RelroLevel::Full, + relocation_model: RelocModel::Pic, + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + linker: Some("rust-lld".to_owned()), + features: + "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" + .to_string(), + executables: true, + disable_redzone: true, + panic_strategy: PanicStrategy::Abort, + code_model: Some(CodeModel::Kernel), + ..Default::default() + }; + Target { + llvm_target: "x86_64-unknown-none-elf".to_string(), + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + options: opts, + } +}
diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs index 53afe4c..4c80483 100644 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ b/compiler/rustc_trait_selection/src/autoderef.rs
@@ -152,11 +152,12 @@ }, cause, ); - if let Err(e) = fulfillcx.select_where_possible(&self.infcx) { + let errors = fulfillcx.select_where_possible(&self.infcx); + if !errors.is_empty() { // This shouldn't happen, except for evaluate/fulfill mismatches, // but that's not a reason for an ICE (`predicate_may_hold` is conservative // by design). - debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", e); + debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", errors); return None; } let obligations = fulfillcx.pending_obligations();
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 017a7c4..1820e33 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -14,9 +14,11 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(drain_filter)] +#![feature(derive_default_enum)] #![feature(hash_drain_filter)] #![feature(in_band_lifetimes)] #![feature(iter_zip)] +#![feature(let_else)] #![feature(never_type)] #![feature(crate_visibility_modifier)] #![feature(control_flow_enum)]
diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index c220546..75d57d7 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs
@@ -1,61 +1,14 @@ -use crate::traits::{self, ObligationCause, PredicateObligation}; +use crate::traits; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic; -use rustc_infer::infer::free_regions::FreeRegionRelations; -use rustc_infer::infer::opaque_types::OpaqueTypeDecl; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{self, InferCtxt, InferOk}; -use rustc_middle::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst}; +use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts}; use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt}; use rustc_span::Span; -use std::ops::ControlFlow; - -/// Whether member constraints should be generated for all opaque types -#[derive(Debug)] -pub enum GenerateMemberConstraints { - /// The default, used by typeck - WhenRequired, - /// The borrow checker needs member constraints in any case where we don't - /// have a `'static` bound. This is because the borrow checker has more - /// flexibility in the values of regions. For example, given `f<'a, 'b>` - /// the borrow checker can have an inference variable outlive `'a` and `'b`, - /// but not be equal to `'static`. - IfNoStaticBound, -} - pub trait InferCtxtExt<'tcx> { - fn instantiate_opaque_types<T: TypeFoldable<'tcx>>( - &self, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value: T, - value_span: Span, - ) -> InferOk<'tcx, T>; - - fn constrain_opaque_types<FRR: FreeRegionRelations<'tcx>>(&self, free_region_relations: &FRR); - - fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>( - &self, - opaque_type_key: OpaqueTypeKey<'tcx>, - opaque_defn: &OpaqueTypeDecl<'tcx>, - mode: GenerateMemberConstraints, - free_region_relations: &FRR, - ); - - /*private*/ - fn generate_member_constraint( - &self, - concrete_ty: Ty<'tcx>, - opaque_defn: &OpaqueTypeDecl<'tcx>, - opaque_type_key: OpaqueTypeKey<'tcx>, - first_own_region_index: usize, - ); - fn infer_opaque_definition_from_instantiation( &self, opaque_type_key: OpaqueTypeKey<'tcx>, @@ -65,416 +18,6 @@ } impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { - /// Replaces all opaque types in `value` with fresh inference variables - /// and creates appropriate obligations. For example, given the input: - /// - /// impl Iterator<Item = impl Debug> - /// - /// this method would create two type variables, `?0` and `?1`. It would - /// return the type `?0` but also the obligations: - /// - /// ?0: Iterator<Item = ?1> - /// ?1: Debug - /// - /// Moreover, it returns an `OpaqueTypeMap` that would map `?0` to - /// info about the `impl Iterator<..>` type and `?1` to info about - /// the `impl Debug` type. - /// - /// # Parameters - /// - /// - `parent_def_id` -- the `DefId` of the function in which the opaque type - /// is defined - /// - `body_id` -- the body-id with which the resulting obligations should - /// be associated - /// - `param_env` -- the in-scope parameter environment to be used for - /// obligations - /// - `value` -- the value within which we are instantiating opaque types - /// - `value_span` -- the span where the value came from, used in error reporting - fn instantiate_opaque_types<T: TypeFoldable<'tcx>>( - &self, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value: T, - value_span: Span, - ) -> InferOk<'tcx, T> { - debug!( - "instantiate_opaque_types(value={:?}, body_id={:?}, \ - param_env={:?}, value_span={:?})", - value, body_id, param_env, value_span, - ); - let mut instantiator = - Instantiator { infcx: self, body_id, param_env, value_span, obligations: vec![] }; - let value = instantiator.instantiate_opaque_types_in_map(value); - InferOk { value, obligations: instantiator.obligations } - } - - /// Given the map `opaque_types` containing the opaque - /// `impl Trait` types whose underlying, hidden types are being - /// inferred, this method adds constraints to the regions - /// appearing in those underlying hidden types to ensure that they - /// at least do not refer to random scopes within the current - /// function. These constraints are not (quite) sufficient to - /// guarantee that the regions are actually legal values; that - /// final condition is imposed after region inference is done. - /// - /// # The Problem - /// - /// Let's work through an example to explain how it works. Assume - /// the current function is as follows: - /// - /// ```text - /// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) - /// ``` - /// - /// Here, we have two `impl Trait` types whose values are being - /// inferred (the `impl Bar<'a>` and the `impl - /// Bar<'b>`). Conceptually, this is sugar for a setup where we - /// define underlying opaque types (`Foo1`, `Foo2`) and then, in - /// the return type of `foo`, we *reference* those definitions: - /// - /// ```text - /// type Foo1<'x> = impl Bar<'x>; - /// type Foo2<'x> = impl Bar<'x>; - /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } - /// // ^^^^ ^^ - /// // | | - /// // | substs - /// // def_id - /// ``` - /// - /// As indicating in the comments above, each of those references - /// is (in the compiler) basically a substitution (`substs`) - /// applied to the type of a suitable `def_id` (which identifies - /// `Foo1` or `Foo2`). - /// - /// Now, at this point in compilation, what we have done is to - /// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with - /// fresh inference variables C1 and C2. We wish to use the values - /// of these variables to infer the underlying types of `Foo1` and - /// `Foo2`. That is, this gives rise to higher-order (pattern) unification - /// constraints like: - /// - /// ```text - /// for<'a> (Foo1<'a> = C1) - /// for<'b> (Foo1<'b> = C2) - /// ``` - /// - /// For these equation to be satisfiable, the types `C1` and `C2` - /// can only refer to a limited set of regions. For example, `C1` - /// can only refer to `'static` and `'a`, and `C2` can only refer - /// to `'static` and `'b`. The job of this function is to impose that - /// constraint. - /// - /// Up to this point, C1 and C2 are basically just random type - /// inference variables, and hence they may contain arbitrary - /// regions. In fact, it is fairly likely that they do! Consider - /// this possible definition of `foo`: - /// - /// ```text - /// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { - /// (&*x, &*y) - /// } - /// ``` - /// - /// Here, the values for the concrete types of the two impl - /// traits will include inference variables: - /// - /// ```text - /// &'0 i32 - /// &'1 i32 - /// ``` - /// - /// Ordinarily, the subtyping rules would ensure that these are - /// sufficiently large. But since `impl Bar<'a>` isn't a specific - /// type per se, we don't get such constraints by default. This - /// is where this function comes into play. It adds extra - /// constraints to ensure that all the regions which appear in the - /// inferred type are regions that could validly appear. - /// - /// This is actually a bit of a tricky constraint in general. We - /// want to say that each variable (e.g., `'0`) can only take on - /// values that were supplied as arguments to the opaque type - /// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in - /// scope. We don't have a constraint quite of this kind in the current - /// region checker. - /// - /// # The Solution - /// - /// We generally prefer to make `<=` constraints, since they - /// integrate best into the region solver. To do that, we find the - /// "minimum" of all the arguments that appear in the substs: that - /// is, some region which is less than all the others. In the case - /// of `Foo1<'a>`, that would be `'a` (it's the only choice, after - /// all). Then we apply that as a least bound to the variables - /// (e.g., `'a <= '0`). - /// - /// In some cases, there is no minimum. Consider this example: - /// - /// ```text - /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } - /// ``` - /// - /// Here we would report a more complex "in constraint", like `'r - /// in ['a, 'b, 'static]` (where `'r` is some region appearing in - /// the hidden type). - /// - /// # Constrain regions, not the hidden concrete type - /// - /// Note that generating constraints on each region `Rc` is *not* - /// the same as generating an outlives constraint on `Tc` iself. - /// For example, if we had a function like this: - /// - /// ```rust - /// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { - /// (x, y) - /// } - /// - /// // Equivalent to: - /// type FooReturn<'a, T> = impl Foo<'a>; - /// fn foo<'a, T>(..) -> FooReturn<'a, T> { .. } - /// ``` - /// - /// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` - /// is an inference variable). If we generated a constraint that - /// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- - /// but this is not necessary, because the opaque type we - /// create will be allowed to reference `T`. So we only generate a - /// constraint that `'0: 'a`. - /// - /// # The `free_region_relations` parameter - /// - /// The `free_region_relations` argument is used to find the - /// "minimum" of the regions supplied to a given opaque type. - /// It must be a relation that can answer whether `'a <= 'b`, - /// where `'a` and `'b` are regions that appear in the "substs" - /// for the opaque type references (the `<'a>` in `Foo1<'a>`). - /// - /// Note that we do not impose the constraints based on the - /// generic regions from the `Foo1` definition (e.g., `'x`). This - /// is because the constraints we are imposing here is basically - /// the concern of the one generating the constraining type C1, - /// which is the current function. It also means that we can - /// take "implied bounds" into account in some cases: - /// - /// ```text - /// trait SomeTrait<'a, 'b> { } - /// fn foo<'a, 'b>(_: &'a &'b u32) -> impl SomeTrait<'a, 'b> { .. } - /// ``` - /// - /// Here, the fact that `'b: 'a` is known only because of the - /// implied bounds from the `&'a &'b u32` parameter, and is not - /// "inherent" to the opaque type definition. - /// - /// # Parameters - /// - /// - `opaque_types` -- the map produced by `instantiate_opaque_types` - /// - `free_region_relations` -- something that can be used to relate - /// the free regions (`'a`) that appear in the impl trait. - fn constrain_opaque_types<FRR: FreeRegionRelations<'tcx>>(&self, free_region_relations: &FRR) { - let opaque_types = self.inner.borrow().opaque_types.clone(); - for (opaque_type_key, opaque_defn) in opaque_types { - self.constrain_opaque_type( - opaque_type_key, - &opaque_defn, - GenerateMemberConstraints::WhenRequired, - free_region_relations, - ); - } - } - - /// See `constrain_opaque_types` for documentation. - #[instrument(level = "debug", skip(self, free_region_relations))] - fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>( - &self, - opaque_type_key: OpaqueTypeKey<'tcx>, - opaque_defn: &OpaqueTypeDecl<'tcx>, - mode: GenerateMemberConstraints, - free_region_relations: &FRR, - ) { - let def_id = opaque_type_key.def_id; - - let tcx = self.tcx; - - let concrete_ty = self.resolve_vars_if_possible(opaque_defn.concrete_ty); - - debug!(?concrete_ty); - - let first_own_region = match opaque_defn.origin { - hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::AsyncFn => { - // We lower - // - // 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>. - // - // For these types we only iterate over `'l0..lm` below. - tcx.generics_of(def_id).parent_count - } - // These opaque type inherit all lifetime parameters from their - // parent, so we have to check them all. - hir::OpaqueTyOrigin::TyAlias => 0, - }; - - let span = tcx.def_span(def_id); - - // Check if the `impl Trait` bounds include region bounds. - // For example, this would be true for: - // - // fn foo<'a, 'b, 'c>() -> impl Trait<'c> + 'a + 'b - // - // but false for: - // - // fn foo<'c>() -> impl Trait<'c> - // - // unless `Trait` was declared like: - // - // trait Trait<'c>: 'c - // - // in which case it would be true. - // - // This is used during regionck to decide whether we need to - // impose any additional constraints to ensure that region - // variables in `concrete_ty` wind up being constrained to - // something from `substs` (or, at minimum, things that outlive - // the fn body). (Ultimately, writeback is responsible for this - // check.) - let bounds = tcx.explicit_item_bounds(def_id); - debug!("{:#?}", bounds); - let bounds = bounds.iter().map(|(bound, _)| bound.subst(tcx, opaque_type_key.substs)); - debug!("{:#?}", bounds); - let opaque_type = tcx.mk_opaque(def_id, opaque_type_key.substs); - - let required_region_bounds = required_region_bounds(tcx, opaque_type, bounds); - if !required_region_bounds.is_empty() { - for required_region in required_region_bounds { - concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx, - op: |r| self.sub_regions(infer::CallReturn(span), required_region, r), - }); - } - if let GenerateMemberConstraints::IfNoStaticBound = mode { - self.generate_member_constraint( - concrete_ty, - opaque_defn, - opaque_type_key, - first_own_region, - ); - } - return; - } - - // There were no `required_region_bounds`, - // so we have to search for a `least_region`. - // Go through all the regions used as arguments to the - // opaque type. These are the parameters to the opaque - // type; so in our example above, `substs` would contain - // `['a]` for the first impl trait and `'b` for the - // second. - let mut least_region = None; - - for subst_arg in &opaque_type_key.substs[first_own_region..] { - let subst_region = match subst_arg.unpack() { - GenericArgKind::Lifetime(r) => r, - GenericArgKind::Type(_) | GenericArgKind::Const(_) => continue, - }; - - // Compute the least upper bound of it with the other regions. - debug!(?least_region); - debug!(?subst_region); - match least_region { - None => least_region = Some(subst_region), - Some(lr) => { - if free_region_relations.sub_free_regions(self.tcx, lr, subst_region) { - // keep the current least region - } else if free_region_relations.sub_free_regions(self.tcx, subst_region, lr) { - // switch to `subst_region` - least_region = Some(subst_region); - } else { - // There are two regions (`lr` and - // `subst_region`) which are not relatable. We - // can't find a best choice. Therefore, - // instead of creating a single bound like - // `'r: 'a` (which is our preferred choice), - // we will create a "in bound" like `'r in - // ['a, 'b, 'c]`, where `'a..'c` are the - // regions that appear in the impl trait. - - return self.generate_member_constraint( - concrete_ty, - opaque_defn, - opaque_type_key, - first_own_region, - ); - } - } - } - } - - let least_region = least_region.unwrap_or(tcx.lifetimes.re_static); - debug!(?least_region); - - if let GenerateMemberConstraints::IfNoStaticBound = mode { - if least_region != tcx.lifetimes.re_static { - self.generate_member_constraint( - concrete_ty, - opaque_defn, - opaque_type_key, - first_own_region, - ); - } - } - concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx, - op: |r| self.sub_regions(infer::CallReturn(span), least_region, r), - }); - } - - /// As a fallback, we sometimes generate an "in constraint". For - /// a case like `impl Foo<'a, 'b>`, where `'a` and `'b` cannot be - /// related, we would generate a constraint `'r in ['a, 'b, - /// 'static]` for each region `'r` that appears in the hidden type - /// (i.e., it must be equal to `'a`, `'b`, or `'static`). - /// - /// `conflict1` and `conflict2` are the two region bounds that we - /// detected which were unrelated. They are used for diagnostics. - fn generate_member_constraint( - &self, - concrete_ty: Ty<'tcx>, - opaque_defn: &OpaqueTypeDecl<'tcx>, - opaque_type_key: OpaqueTypeKey<'tcx>, - first_own_region: usize, - ) { - // Create the set of choice regions: each region in the hidden - // type can be equal to any of the region parameters of the - // opaque type definition. - let choice_regions: Lrc<Vec<ty::Region<'tcx>>> = Lrc::new( - opaque_type_key.substs[first_own_region..] - .iter() - .filter_map(|arg| match arg.unpack() { - GenericArgKind::Lifetime(r) => Some(r), - GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, - }) - .chain(std::iter::once(self.tcx.lifetimes.re_static)) - .collect(), - ); - - concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx: self.tcx, - op: |r| { - self.member_constraint( - opaque_type_key.def_id, - opaque_defn.definition_span, - concrete_ty, - r, - &choice_regions, - ) - }, - }); - } - /// 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 @@ -490,7 +33,7 @@ /// purpose of this function is to do that translation. /// /// (*) C1 and C2 were introduced in the comments on - /// `constrain_opaque_types`. Read that comment for more context. + /// `constrain_opaque_type`. Read that comment for more context. /// /// # Parameters /// @@ -536,83 +79,6 @@ } } -// Visitor that requires that (almost) all regions in the type visited outlive -// `least_region`. We cannot use `push_outlives_components` because regions in -// closure signatures are not included in their outlives components. We need to -// ensure all regions outlive the given bound so that we don't end up with, -// say, `ReVar` appearing in a return type and causing ICEs when other -// functions end up with region constraints involving regions from other -// functions. -// -// We also cannot use `for_each_free_region` because for closures it includes -// the regions parameters from the enclosing item. -// -// We ignore any type parameters because impl trait values are assumed to -// capture all the in-scope type parameters. -struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP> { - tcx: TyCtxt<'tcx>, - op: OP, -} - -impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> -where - OP: FnMut(ty::Region<'tcx>), -{ - fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { - Some(self.tcx) - } - - fn visit_binder<T: TypeFoldable<'tcx>>( - &mut self, - t: &ty::Binder<'tcx, T>, - ) -> ControlFlow<Self::BreakTy> { - t.as_ref().skip_binder().visit_with(self); - ControlFlow::CONTINUE - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - match *r { - // ignore bound regions, keep visiting - ty::ReLateBound(_, _) => ControlFlow::CONTINUE, - _ => { - (self.op)(r); - ControlFlow::CONTINUE - } - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - // We're only interested in types involving regions - if !ty.flags().intersects(ty::TypeFlags::HAS_POTENTIAL_FREE_REGIONS) { - return ControlFlow::CONTINUE; - } - - match ty.kind() { - ty::Closure(_, ref substs) => { - // Skip lifetime parameters of the enclosing item(s) - - substs.as_closure().tupled_upvars_ty().visit_with(self); - substs.as_closure().sig_as_fn_ptr_ty().visit_with(self); - } - - ty::Generator(_, ref substs, _) => { - // Skip lifetime parameters of the enclosing item(s) - // Also skip the witness type, because that has no free regions. - - substs.as_generator().tupled_upvars_ty().visit_with(self); - substs.as_generator().return_ty().visit_with(self); - substs.as_generator().yield_ty().visit_with(self); - substs.as_generator().resume_ty().visit_with(self); - } - _ => { - ty.super_visit_with(self); - } - } - - ControlFlow::CONTINUE - } -} - struct ReverseMapper<'tcx> { tcx: TyCtxt<'tcx>, @@ -855,235 +321,6 @@ } } -struct Instantiator<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value_span: Span, - obligations: Vec<PredicateObligation<'tcx>>, -} - -impl<'a, 'tcx> Instantiator<'a, 'tcx> { - fn instantiate_opaque_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T { - let tcx = self.infcx.tcx; - value.fold_with(&mut BottomUpFolder { - tcx, - ty_op: |ty| { - if ty.references_error() { - return tcx.ty_error(); - } else if let ty::Opaque(def_id, substs) = ty.kind() { - // Check that this is `impl Trait` type is - // declared by `parent_def_id` -- i.e., one whose - // value we are inferring. At present, this is - // always true during the first phase of - // type-check, but not always true later on during - // NLL. Once we support named opaque types more fully, - // this same scenario will be able to arise during all phases. - // - // Here is an example using type alias `impl Trait` - // that indicates the distinction we are checking for: - // - // ```rust - // mod a { - // pub type Foo = impl Iterator; - // pub fn make_foo() -> Foo { .. } - // } - // - // mod b { - // fn foo() -> a::Foo { a::make_foo() } - // } - // ``` - // - // Here, the return type of `foo` references an - // `Opaque` indeed, but not one whose value is - // presently being inferred. You can get into a - // similar situation with closure return types - // today: - // - // ```rust - // fn foo() -> impl Iterator { .. } - // fn bar() { - // let x = || foo(); // returns the Opaque assoc with `foo` - // } - // ``` - if let Some(def_id) = def_id.as_local() { - let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let parent_def_id = self.infcx.defining_use_anchor; - let def_scope_default = || { - let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id); - parent_def_id == tcx.hir().local_def_id(opaque_parent_hir_id) - }; - let (in_definition_scope, origin) = - match tcx.hir().expect_item(opaque_hir_id).kind { - // Anonymous `impl Trait` - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - impl_trait_fn: Some(parent), - origin, - .. - }) => (parent == parent_def_id.to_def_id(), origin), - // Named `type Foo = impl Bar;` - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - impl_trait_fn: None, - origin, - .. - }) => ( - may_define_opaque_type(tcx, parent_def_id, opaque_hir_id), - origin, - ), - _ => (def_scope_default(), hir::OpaqueTyOrigin::TyAlias), - }; - if in_definition_scope { - let opaque_type_key = - OpaqueTypeKey { def_id: def_id.to_def_id(), substs }; - return self.fold_opaque_ty(ty, opaque_type_key, origin); - } - - debug!( - "instantiate_opaque_types_in_map: \ - encountered opaque outside its definition scope \ - def_id={:?}", - def_id, - ); - } - } - - ty - }, - lt_op: |lt| lt, - ct_op: |ct| ct, - }) - } - - #[instrument(skip(self), level = "debug")] - fn fold_opaque_ty( - &mut self, - ty: Ty<'tcx>, - opaque_type_key: OpaqueTypeKey<'tcx>, - origin: hir::OpaqueTyOrigin, - ) -> Ty<'tcx> { - let infcx = self.infcx; - let tcx = infcx.tcx; - let OpaqueTypeKey { def_id, substs } = opaque_type_key; - - // Use the same type variable if the exact same opaque type appears more - // than once in the return type (e.g., if it's passed to a type alias). - if let Some(opaque_defn) = infcx.inner.borrow().opaque_types.get(&opaque_type_key) { - debug!("re-using cached concrete type {:?}", opaque_defn.concrete_ty.kind()); - return opaque_defn.concrete_ty; - } - - let ty_var = infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: self.value_span, - }); - - // Ideally, we'd get the span where *this specific `ty` came - // from*, but right now we just use the span from the overall - // value being folded. In simple cases like `-> impl Foo`, - // these are the same span, but not in cases like `-> (impl - // Foo, impl Bar)`. - let definition_span = self.value_span; - - { - let mut infcx = self.infcx.inner.borrow_mut(); - infcx.opaque_types.insert( - OpaqueTypeKey { def_id, substs }, - OpaqueTypeDecl { opaque_type: ty, definition_span, concrete_ty: ty_var, origin }, - ); - infcx.opaque_types_vars.insert(ty_var, ty); - } - - debug!("generated new type inference var {:?}", ty_var.kind()); - - let item_bounds = tcx.explicit_item_bounds(def_id); - - self.obligations.reserve(item_bounds.len()); - for (predicate, _) in item_bounds { - debug!(?predicate); - let predicate = predicate.subst(tcx, substs); - debug!(?predicate); - - // We can't normalize associated types from `rustc_infer`, but we can eagerly register inference variables for them. - let predicate = predicate.fold_with(&mut BottomUpFolder { - tcx, - ty_op: |ty| match ty.kind() { - ty::Projection(projection_ty) => infcx.infer_projection( - self.param_env, - *projection_ty, - ObligationCause::misc(self.value_span, self.body_id), - 0, - &mut self.obligations, - ), - _ => ty, - }, - lt_op: |lt| lt, - ct_op: |ct| ct, - }); - debug!(?predicate); - - if let ty::PredicateKind::Projection(projection) = predicate.kind().skip_binder() { - if projection.ty.references_error() { - // No point on adding these obligations since there's a type error involved. - return tcx.ty_error(); - } - } - // Change the predicate to refer to the type variable, - // which will be the concrete type instead of the opaque type. - // This also instantiates nested instances of `impl Trait`. - let predicate = self.instantiate_opaque_types_in_map(predicate); - - let cause = - traits::ObligationCause::new(self.value_span, self.body_id, traits::OpaqueType); - - // Require that the predicate holds for the concrete type. - debug!(?predicate); - self.obligations.push(traits::Obligation::new(cause, self.param_env, predicate)); - } - - ty_var - } -} - -/// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `def_id`. -/// -/// Example: -/// ```rust -/// pub mod foo { -/// pub mod bar { -/// pub trait Bar { .. } -/// -/// pub type Baz = impl Bar; -/// -/// fn f1() -> Baz { .. } -/// } -/// -/// fn f2() -> bar::Baz { .. } -/// } -/// ``` -/// -/// Here, `def_id` is the `LocalDefId` of the defining use of the opaque type (e.g., `f1` or `f2`), -/// and `opaque_hir_id` is the `HirId` of the definition of the opaque type `Baz`. -/// For the above example, this function returns `true` for `f1` and `false` for `f2`. -fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: LocalDefId, opaque_hir_id: hir::HirId) -> bool { - let mut hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - - // Named opaque types can be defined by any siblings or children of siblings. - let scope = tcx.hir().get_defining_scope(opaque_hir_id); - // We walk up the node tree until we hit the root or the scope of the opaque type. - while hir_id != scope && hir_id != hir::CRATE_HIR_ID { - hir_id = tcx.hir().get_parent_item(hir_id); - } - // Syntactically, we are allowed to define the concrete type if: - let res = hir_id == scope; - trace!( - "may_define_opaque_type(def={:?}, opaque_node={:?}) = {}", - tcx.hir().find(hir_id), - tcx.hir().get(opaque_hir_id), - res - ); - res -} - /// 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
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 622c9edc..54f7b91 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -187,9 +187,11 @@ // an additional sanity check. let mut fulfill = FulfillmentContext::new(); fulfill.register_bound(&infcx, full_env, ty, trait_did, ObligationCause::dummy()); - fulfill.select_all_or_error(&infcx).unwrap_or_else(|e| { - panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, e) - }); + let errors = fulfill.select_all_or_error(&infcx); + + if !errors.is_empty() { + panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors); + } let body_id_map: FxHashMap<_, _> = infcx .inner @@ -286,6 +288,8 @@ substs: infcx.tcx.mk_substs_trait(ty, &[]), }, constness: ty::BoundConstness::NotConst, + // Auto traits are positive + polarity: ty::ImplPolarity::Positive, })); let computed_preds = param_env.caller_bounds().iter();
diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index ec62ee4..2ccb253 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
@@ -49,34 +49,32 @@ self.obligations.insert(obligation); } - fn select_all_or_error( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { - self.select_where_possible(infcx)?; + fn select_all_or_error(&mut self, infcx: &InferCtxt<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> { + { + let errors = self.select_where_possible(infcx); - if self.obligations.is_empty() { - Ok(()) - } else { - let errors = self - .obligations - .iter() - .map(|obligation| FulfillmentError { - obligation: obligation.clone(), - code: FulfillmentErrorCode::CodeAmbiguity, - // FIXME - does Chalk have a notation of 'root obligation'? - // This is just for diagnostics, so it's okay if this is wrong - root_obligation: obligation.clone(), - }) - .collect(); - Err(errors) + if !errors.is_empty() { + return errors; + } } + + // any remaining obligations are errors + self.obligations + .iter() + .map(|obligation| FulfillmentError { + obligation: obligation.clone(), + code: FulfillmentErrorCode::CodeAmbiguity, + // FIXME - does Chalk have a notation of 'root obligation'? + // This is just for diagnostics, so it's okay if this is wrong + root_obligation: obligation.clone(), + }) + .collect() } fn select_where_possible( &mut self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { + ) -> Vec<FulfillmentError<'tcx>> { assert!(!infcx.is_in_snapshot()); let mut errors = Vec::new(); @@ -147,7 +145,7 @@ } } - if errors.is_empty() { Ok(()) } else { Err(errors) } + errors } fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index f06f0e3..bdd4fdd 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs
@@ -120,7 +120,8 @@ // In principle, we only need to do this so long as `result` // contains unbound type parameters. It could be a slight // optimization to stop iterating early. - if let Err(errors) = fulfill_cx.select_all_or_error(infcx) { + let errors = fulfill_cx.select_all_or_error(infcx); + if !errors.is_empty() { infcx.tcx.sess.delay_span_bug( rustc_span::DUMMY_SP, &format!("Encountered errors `{:?}` resolving bounds after type-checking", errors),
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 668a74b..42d3194 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -5,9 +5,12 @@ //! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html use crate::infer::{CombinedSnapshot, InferOk, TyCtxtInferExt}; +use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::SkipLeakCheck; -use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionContext}; +use crate::traits::{ + self, Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext, +}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::Subst; @@ -158,6 +161,19 @@ b_def_id: DefId, snapshot: &CombinedSnapshot<'_, 'tcx>, ) -> Option<OverlapResult<'tcx>> { + fn loose_check(selcx: &mut SelectionContext<'cx, 'tcx>, o: &PredicateObligation<'tcx>) -> bool { + !selcx.predicate_may_hold_fatal(o) + } + + fn strict_check(selcx: &SelectionContext<'cx, 'tcx>, o: &PredicateObligation<'tcx>) -> bool { + let infcx = selcx.infcx(); + let tcx = infcx.tcx; + o.flip_polarity(tcx) + .as_ref() + .map(|o| selcx.infcx().predicate_must_hold_modulo_regions(o)) + .unwrap_or(false) + } + // For the purposes of this check, we don't bring any placeholder // types into scope; instead, we replace the generic types with // fresh type variables, and hence we do our evaluations in an @@ -184,8 +200,29 @@ debug!("overlap: unification check succeeded"); - // Are any of the obligations unsatisfiable? If so, no overlap. + // There's no overlap if obligations are unsatisfiable or if the obligation negated is + // satisfied. + // + // For example, given these two impl headers: + // + // `impl<'a> From<&'a str> for Box<dyn Error>` + // `impl<E> From<E> for Box<dyn Error> where E: Error` + // + // So we have: + // + // `Box<dyn Error>: From<&'?a str>` + // `Box<dyn Error>: From<?E>` + // + // After equating the two headers: + // + // `Box<dyn Error> = Box<dyn Error>` + // So, `?E = &'?a str` and then given the where clause `&'?a str: Error`. + // + // If the obligation `&'?a str: Error` holds, it means that there's overlap. If that doesn't + // hold we need to check if `&'?a str: !Error` holds, if doesn't hold there's overlap because + // at some point an impl for `&'?a str: Error` could be added. let infcx = selcx.infcx(); + let tcx = infcx.tcx; let opt_failing_obligation = a_impl_header .predicates .iter() @@ -199,7 +236,17 @@ predicate: p, }) .chain(obligations) - .find(|o| !selcx.predicate_may_hold_fatal(o)); + .find(|o| { + // if both impl headers are set to strict coherence it means that this will be accepted + // only if it's stated that T: !Trait. So only prove that the negated obligation holds. + if tcx.has_attr(a_def_id, sym::rustc_strict_coherence) + && tcx.has_attr(b_def_id, sym::rustc_strict_coherence) + { + strict_check(selcx, o) + } else { + loose_check(selcx, o) || tcx.features().negative_impls && strict_check(selcx, o) + } + }); // FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported // to the canonical trait query form, `infcx.predicate_may_hold`, once // the new system supports intercrate mode (which coherence needs).
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 1193d10..6b5d37c 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -80,9 +80,8 @@ Concrete, } let mut failure_kind = FailureKind::Concrete; - walk_abstract_const::<!, _>(tcx, ct, |node| match node.root() { + walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) { Node::Leaf(leaf) => { - let leaf = leaf.subst(tcx, ct.substs); if leaf.has_infer_types_or_consts() { failure_kind = FailureKind::MentionsInfer; } else if leaf.definitely_has_param_types_or_consts(tcx) { @@ -92,7 +91,6 @@ ControlFlow::CONTINUE } Node::Cast(_, _, ty) => { - let ty = ty.subst(tcx, ct.substs); if ty.has_infer_types_or_consts() { failure_kind = FailureKind::MentionsInfer; } else if ty.definitely_has_param_types_or_consts(tcx) { @@ -153,7 +151,7 @@ if concrete.is_ok() && uv.substs(infcx.tcx).definitely_has_param_types_or_consts(infcx.tcx) { match infcx.tcx.def_kind(uv.def.did) { - DefKind::AnonConst => { + DefKind::AnonConst | DefKind::InlineConst => { let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def); if mir_body.is_polymorphic { @@ -187,8 +185,8 @@ pub struct AbstractConst<'tcx> { // FIXME: Consider adding something like `IndexSlice` // and use this here. - pub inner: &'tcx [Node<'tcx>], - pub substs: SubstsRef<'tcx>, + inner: &'tcx [Node<'tcx>], + substs: SubstsRef<'tcx>, } impl<'tcx> AbstractConst<'tcx> { @@ -218,8 +216,14 @@ } #[inline] - pub fn root(self) -> Node<'tcx> { - self.inner.last().copied().unwrap() + pub fn root(self, tcx: TyCtxt<'tcx>) -> Node<'tcx> { + let node = self.inner.last().copied().unwrap(); + match node { + Node::Leaf(leaf) => Node::Leaf(leaf.subst(tcx, self.substs)), + Node::Cast(kind, operand, ty) => Node::Cast(kind, operand, ty.subst(tcx, self.substs)), + // Don't perform substitution on the following as they can't directly contain generic params + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => node, + } } } @@ -491,7 +495,7 @@ // we want to look into them or treat them as opaque projections. // // Right now we do neither of that and simply always fail to unify them. - DefKind::AnonConst => (), + DefKind::AnonConst | DefKind::InlineConst => (), _ => return Ok(None), } @@ -542,7 +546,7 @@ f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow<R>, ) -> ControlFlow<R> { f(ct)?; - let root = ct.root(); + let root = ct.root(tcx); match root { Node::Leaf(_) => ControlFlow::CONTINUE, Node::Binop(_, l, r) => { @@ -570,16 +574,14 @@ // We substitute generics repeatedly to allow AbstractConsts to unify where a // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - while let Node::Leaf(a_ct) = a.root() { - let a_ct = a_ct.subst(tcx, a.substs); + while let Node::Leaf(a_ct) = a.root(tcx) { match AbstractConst::from_const(tcx, a_ct) { Ok(Some(a_act)) => a = a_act, Ok(None) => break, Err(_) => return true, } } - while let Node::Leaf(b_ct) = b.root() { - let b_ct = b_ct.subst(tcx, b.substs); + while let Node::Leaf(b_ct) = b.root(tcx) { match AbstractConst::from_const(tcx, b_ct) { Ok(Some(b_act)) => b = b_act, Ok(None) => break, @@ -587,10 +589,8 @@ } } - match (a.root(), b.root()) { + match (a.root(tcx), b.root(tcx)) { (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - let a_ct = a_ct.subst(tcx, a.substs); - let b_ct = b_ct.subst(tcx, b.substs); if a_ct.ty != b_ct.ty { return false; }
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 225ff5e..f8df0e2 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -34,6 +34,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::normalize::AtExt as _; +use crate::traits::specialize::to_pretty_impl_header; use on_unimplemented::InferCtxtExt as _; use suggestions::InferCtxtExt as _; @@ -241,6 +242,15 @@ let mut span = obligation.cause.span; let mut err = match *error { + SelectionError::Ambiguous(ref impls) => { + let mut err = self.tcx.sess.struct_span_err( + obligation.cause.span, + &format!("multiple applicable `impl`s for `{}`", obligation.predicate), + ); + self.annotate_source_of_ambiguity(&mut err, impls, obligation.predicate); + err.emit(); + return; + } SelectionError::Unimplemented => { // If this obligation was generated as a result of well-formedness checking, see if we // can get a better error message by performing HIR-based well-formedness checking. @@ -256,19 +266,16 @@ } } if let ObligationCauseCode::CompareImplMethodObligation { - item_name, impl_item_def_id, trait_item_def_id, } | ObligationCauseCode::CompareImplTypeObligation { - item_name, impl_item_def_id, trait_item_def_id, } = obligation.cause.code { self.report_extra_impl_obligation( span, - item_name, impl_item_def_id, trait_item_def_id, &format!("`{}`", obligation.predicate), @@ -1138,6 +1145,13 @@ obligation: &PredicateObligation<'tcx>, ); + fn annotate_source_of_ambiguity( + &self, + err: &mut DiagnosticBuilder<'tcx>, + impls: &[DefId], + predicate: ty::Predicate<'tcx>, + ); + fn maybe_suggest_unsized_generics( &self, err: &mut DiagnosticBuilder<'tcx>, @@ -1475,6 +1489,9 @@ } } } + ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { + self.get_parent_trait_ref(&parent_code) + } _ => None, } } @@ -1549,11 +1566,8 @@ ?predicate, ?obligation.cause.code, ); - // Ambiguity errors are often caused as fallout from earlier - // errors. So just ignore them if this infcx is tainted. - if self.is_tainted_by_errors() { - return; - } + // Ambiguity errors are often caused as fallout from earlier errors. + // We ignore them if this `infcx` is tainted in some cases below. let bound_predicate = predicate.kind(); let mut err = match bound_predicate.skip_binder() { @@ -1601,10 +1615,19 @@ // check upstream for type errors and don't add the obligations to // begin with in those cases. if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { - self.emit_inference_failure_err(body_id, span, subst, vec![], ErrorCode::E0282) + if !self.is_tainted_by_errors() { + self.emit_inference_failure_err( + body_id, + span, + subst, + vec![], + ErrorCode::E0282, + ) .emit(); + } return; } + let impl_candidates = self.find_similar_impl_candidates(trait_ref); let mut err = self.emit_inference_failure_err( body_id, @@ -1613,7 +1636,29 @@ impl_candidates, ErrorCode::E0283, ); - err.note(&format!("cannot satisfy `{}`", predicate)); + + let obligation = Obligation::new( + obligation.cause.clone(), + obligation.param_env, + trait_ref.to_poly_trait_predicate(), + ); + let mut selcx = SelectionContext::with_query_mode( + &self, + crate::traits::TraitQueryMode::Standard, + ); + match selcx.select_from_obligation(&obligation) { + Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => { + self.annotate_source_of_ambiguity(&mut err, &impls, predicate); + } + _ => { + if self.is_tainted_by_errors() { + err.cancel(); + return; + } + err.note(&format!("cannot satisfy `{}`", predicate)); + } + } + if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); } else if let ( @@ -1674,7 +1719,10 @@ ty::PredicateKind::WellFormed(arg) => { // Same hacky approach as above to avoid deluging user // with error messages. - if arg.references_error() || self.tcx.sess.has_errors() { + if arg.references_error() + || self.tcx.sess.has_errors() + || self.is_tainted_by_errors() + { return; } @@ -1682,7 +1730,10 @@ } ty::PredicateKind::Subtype(data) => { - if data.references_error() || self.tcx.sess.has_errors() { + if data.references_error() + || self.tcx.sess.has_errors() + || self.is_tainted_by_errors() + { // no need to overload user in such cases return; } @@ -1694,7 +1745,7 @@ ty::PredicateKind::Projection(data) => { let self_ty = data.projection_ty.self_ty(); let ty = data.ty; - if predicate.references_error() { + if predicate.references_error() || self.is_tainted_by_errors() { return; } if self_ty.needs_infer() && ty.needs_infer() { @@ -1722,7 +1773,7 @@ } _ => { - if self.tcx.sess.has_errors() { + if self.tcx.sess.has_errors() || self.is_tainted_by_errors() { return; } let mut err = struct_span_err!( @@ -1740,6 +1791,96 @@ err.emit(); } + fn annotate_source_of_ambiguity( + &self, + err: &mut DiagnosticBuilder<'tcx>, + impls: &[DefId], + 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(self.tcx.sess.source_map().guess_head_span(span)), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { + post.push(header); + } + } + } + } + let msg = format!("multiple `impl`s satisfying `{}` found", predicate); + let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); + crate_names.sort(); + crate_names.dedup(); + post.sort(); + post.dedup(); + + if self.is_tainted_by_errors() + && crate_names.len() == 1 + && crate_names[0] == "`core`" + && spans.len() == 0 + { + // Avoid complaining about other inference issues for expressions like + // `42 >> 1`, where the types are still `{integer}`, but we want to + // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too? + err.cancel(); + return; + } + let post = if post.len() > 4 { + format!( + ":\n{}\nand {} more", + post.iter().map(|p| format!("- {}", p)).take(4).collect::<Vec<_>>().join("\n"), + post.len() - 4, + ) + } else if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { + format!(":\n{}", post.iter().map(|p| format!("- {}", p)).collect::<Vec<_>>().join("\n"),) + } else if post.len() == 1 { + format!(": `{}`", post[0]) + } else { + String::new() + }; + + match (spans.len(), crates.len(), crate_names.len()) { + (0, 0, 0) => { + err.note(&format!("cannot satisfy `{}`", predicate)); + } + (0, _, 1) => { + err.note(&format!("{} in the `{}` crate{}", msg, crates[0], post,)); + } + (0, _, _) => { + err.note(&format!( + "{} in the following crates: {}{}", + msg, + crate_names.join(", "), + post, + )); + } + (_, 0, 0) => { + let span: MultiSpan = spans.into(); + err.span_note(span, &msg); + } + (_, 1, 1) => { + let span: MultiSpan = spans.into(); + err.span_note(span, &msg); + err.note( + &format!("and another `impl` found in the `{}` crate{}", crates[0], post,), + ); + } + _ => { + let span: MultiSpan = spans.into(); + err.span_note(span, &msg); + err.note(&format!( + "and more `impl`s found in the following crates: {}{}", + crate_names.join(", "), + post, + )); + } + } + } + /// Returns `true` if the trait predicate may apply for *some* assignment /// to the type parameters. fn predicate_can_apply( @@ -1856,23 +1997,31 @@ let sized_trait = self.tcx.lang_items().sized_trait(); debug!("maybe_suggest_unsized_generics: generics.params={:?}", generics.params); debug!("maybe_suggest_unsized_generics: generics.where_clause={:?}", generics.where_clause); - let param = generics - .params - .iter() - .filter(|param| param.span == span) - .filter(|param| { - // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit - // `Sized` bound is there intentionally and we don't need to suggest relaxing it. - param - .bounds - .iter() - .all(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) != sized_trait) - }) - .next(); + let param = generics.params.iter().filter(|param| param.span == span).find(|param| { + // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit + // `Sized` bound is there intentionally and we don't need to suggest relaxing it. + param + .bounds + .iter() + .all(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) != sized_trait) + }); let param = match param { Some(param) => param, _ => return, }; + let param_def_id = self.tcx.hir().local_def_id(param.hir_id).to_def_id(); + let preds = generics.where_clause.predicates.iter(); + let explicitly_sized = preds + .filter_map(|pred| match pred { + hir::WherePredicate::BoundPredicate(bp) => Some(bp), + _ => None, + }) + .filter(|bp| bp.is_param_bound(param_def_id)) + .flat_map(|bp| bp.bounds) + .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); + if explicitly_sized { + return; + } debug!("maybe_suggest_unsized_generics: param={:?}", param); match node { hir::Node::Item(
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 1a8f863..a90140a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -27,7 +27,7 @@ use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, DesugaringKind, ExpnKind, ForLoopLoc, MultiSpan, Span, DUMMY_SP}; +use rustc_span::{BytePos, DesugaringKind, ExpnKind, MultiSpan, Span, DUMMY_SP}; use rustc_target::spec::abi; use std::fmt; @@ -151,7 +151,7 @@ outer_generator: Option<DefId>, trait_ref: ty::TraitRef<'tcx>, target_ty: Ty<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, + typeck_results: Option<&ty::TypeckResults<'tcx>>, obligation: &PredicateObligation<'tcx>, next_code: Option<&ObligationCauseCode<'tcx>>, ); @@ -290,27 +290,25 @@ } else { // Trivial case: `T` needs an extra bound: `T: Bound`. let (sp, suggestion) = match ( - generics.params.iter().find(|p| { - !matches!(p.kind, hir::GenericParamKind::Type { synthetic: Some(_), .. }) - }), + generics + .params + .iter() + .find(|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: true, .. })), super_traits, ) { (_, None) => predicate_constraint( generics, trait_ref.without_const().to_predicate(tcx).to_string(), ), - (None, Some((ident, []))) => ( - ident.span.shrink_to_hi(), - format!(": {}", trait_ref.print_only_trait_path().to_string()), - ), - (_, Some((_, [.., bounds]))) => ( - bounds.span().shrink_to_hi(), - format!(" + {}", trait_ref.print_only_trait_path().to_string()), - ), - (Some(_), Some((_, []))) => ( - generics.span.shrink_to_hi(), - format!(": {}", trait_ref.print_only_trait_path().to_string()), - ), + (None, Some((ident, []))) => { + (ident.span.shrink_to_hi(), format!(": {}", trait_ref.print_only_trait_path())) + } + (_, Some((_, [.., bounds]))) => { + (bounds.span().shrink_to_hi(), format!(" + {}", trait_ref.print_only_trait_path())) + } + (Some(_), Some((_, []))) => { + (generics.span.shrink_to_hi(), format!(": {}", trait_ref.print_only_trait_path())) + } }; err.span_suggestion_verbose( @@ -687,7 +685,7 @@ &obligation.cause.code { parent_code.clone() - } else if let ExpnKind::Desugaring(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) = + } else if let ExpnKind::Desugaring(DesugaringKind::ForLoop) = span.ctxt().outer_expn_data().kind { Lrc::new(obligation.cause.code.clone()) @@ -709,36 +707,29 @@ } let param_env = obligation.param_env; - let trait_ref = poly_trait_ref.skip_binder(); - - let found_ty = trait_ref.self_ty(); - let found_ty_str = found_ty.to_string(); - let imm_borrowed_found_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, found_ty); - let imm_substs = self.tcx.mk_substs_trait(imm_borrowed_found_ty, &[]); - let mut_borrowed_found_ty = self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, found_ty); - let mut_substs = self.tcx.mk_substs_trait(mut_borrowed_found_ty, &[]); // Try to apply the original trait binding obligation by borrowing. - let mut try_borrowing = |new_imm_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - new_mut_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + let mut try_borrowing = |old_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, blacklist: &[DefId]| -> bool { - if blacklist.contains(&expected_trait_ref.def_id()) { + if blacklist.contains(&old_ref.def_id()) { return false; } - let imm_result = self.predicate_must_hold_modulo_regions(&Obligation::new( - ObligationCause::dummy(), - param_env, - new_imm_trait_ref.without_const().to_predicate(self.tcx), - )); - - let mut_result = self.predicate_must_hold_modulo_regions(&Obligation::new( - ObligationCause::dummy(), - param_env, - new_mut_trait_ref.without_const().to_predicate(self.tcx), - )); + let orig_ty = old_ref.self_ty().skip_binder(); + let mk_result = |new_ty| { + let new_ref = old_ref.rebind(ty::TraitRef::new( + old_ref.def_id(), + self.tcx.mk_substs_trait(new_ty, &old_ref.skip_binder().substs[1..]), + )); + self.predicate_must_hold_modulo_regions(&Obligation::new( + ObligationCause::dummy(), + param_env, + new_ref.without_const().to_predicate(self.tcx), + )) + }; + let imm_result = mk_result(self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, orig_ty)); + let mut_result = mk_result(self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, orig_ty)); if imm_result || mut_result { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { @@ -750,8 +741,8 @@ let msg = format!( "the trait bound `{}: {}` is not satisfied", - found_ty_str, - expected_trait_ref.print_only_trait_path(), + orig_ty.to_string(), + old_ref.print_only_trait_path(), ); if has_custom_message { err.note(&msg); @@ -767,15 +758,14 @@ span, &format!( "expected an implementor of trait `{}`", - expected_trait_ref.print_only_trait_path(), + old_ref.print_only_trait_path(), ), ); // This if is to prevent a special edge-case if matches!( span.ctxt().outer_expn_data().kind, - ExpnKind::Root - | ExpnKind::Desugaring(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) + ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) ) { // We don't want a borrowing suggestion on the fields in structs, // ``` @@ -810,21 +800,11 @@ }; if let ObligationCauseCode::ImplDerivedObligation(obligation) = &*code { - let expected_trait_ref = obligation.parent_trait_ref; - let new_imm_trait_ref = poly_trait_ref - .rebind(ty::TraitRef::new(obligation.parent_trait_ref.def_id(), imm_substs)); - let new_mut_trait_ref = poly_trait_ref - .rebind(ty::TraitRef::new(obligation.parent_trait_ref.def_id(), mut_substs)); - return try_borrowing(new_imm_trait_ref, new_mut_trait_ref, expected_trait_ref, &[]); + try_borrowing(obligation.parent_trait_ref, &[]) } else if let ObligationCauseCode::BindingObligation(_, _) | ObligationCauseCode::ItemObligation(_) = &*code { - return try_borrowing( - poly_trait_ref.rebind(ty::TraitRef::new(trait_ref.def_id, imm_substs)), - poly_trait_ref.rebind(ty::TraitRef::new(trait_ref.def_id, mut_substs)), - *poly_trait_ref, - &never_suggest_borrow[..], - ); + try_borrowing(*poly_trait_ref, &never_suggest_borrow[..]) } else { false } @@ -1038,13 +1018,11 @@ let hir = self.tcx.hir(); let parent_node = hir.get_parent_node(obligation.cause.body_id); let node = hir.find(parent_node); - let (sig, body_id) = if let Some(hir::Node::Item(hir::Item { + let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })) = node - { - (sig, body_id) - } else { + else { return false; }; let body = hir.body(*body_id); @@ -1427,6 +1405,9 @@ while let Some(code) = next_code { debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code); match code { + ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { + next_code = Some(parent_code.as_ref()); + } ObligationCauseCode::DerivedObligation(derived_obligation) | ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) | ObligationCauseCode::ImplDerivedObligation(derived_obligation) => { @@ -1465,11 +1446,7 @@ } // Only continue if a generator was found. - debug!( - "maybe_note_obligation_cause_for_async_await: generator={:?} trait_ref={:?} \ - target_ty={:?}", - generator, trait_ref, target_ty - ); + debug!(?generator, ?trait_ref, ?target_ty, "maybe_note_obligation_cause_for_async_await"); let (generator_did, trait_ref, target_ty) = match (generator, trait_ref, target_ty) { (Some(generator_did), Some(trait_ref), Some(target_ty)) => { (generator_did, trait_ref, target_ty) @@ -1479,16 +1456,8 @@ let span = self.tcx.def_span(generator_did); - // Do not ICE on closure typeck (#66868). - if !generator_did.is_local() { - return false; - } - - // Get the typeck results from the infcx if the generator is the function we are - // currently type-checking; otherwise, get them by performing a query. - // This is needed to avoid cycles. let in_progress_typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()); - let generator_did_root = self.tcx.closure_base_def_id(generator_did); + let generator_did_root = self.tcx.typeck_root_def_id(generator_did); debug!( "maybe_note_obligation_cause_for_async_await: generator_did={:?} \ generator_did_root={:?} in_progress_typeck_results.hir_owner={:?} span={:?}", @@ -1497,14 +1466,6 @@ in_progress_typeck_results.as_ref().map(|t| t.hir_owner), span ); - let query_typeck_results; - let typeck_results: &TypeckResults<'tcx> = match &in_progress_typeck_results { - Some(t) if t.hir_owner.to_def_id() == generator_did_root => t, - _ => { - query_typeck_results = self.tcx.typeck(generator_did.expect_local()); - &query_typeck_results - } - }; let generator_body = generator_did .as_local() @@ -1547,51 +1508,59 @@ let mut interior_or_upvar_span = None; let mut interior_extra_info = None; - if let Some(upvars) = self.tcx.upvars_mentioned(generator_did) { - interior_or_upvar_span = upvars.iter().find_map(|(upvar_id, upvar)| { - let upvar_ty = typeck_results.node_type(*upvar_id); - let upvar_ty = self.resolve_vars_if_possible(upvar_ty); - if ty_matches(ty::Binder::dummy(upvar_ty)) { - Some(GeneratorInteriorOrUpvar::Upvar(upvar.span)) - } else { - None - } - }); + // Get the typeck results from the infcx if the generator is the function we are currently + // type-checking; otherwise, get them by performing a query. This is needed to avoid + // cycles. If we can't use resolved types because the generator comes from another crate, + // we still provide a targeted error but without all the relevant spans. + let query_typeck_results; + let typeck_results: Option<&TypeckResults<'tcx>> = match &in_progress_typeck_results { + Some(t) if t.hir_owner.to_def_id() == generator_did_root => Some(&t), + _ if generator_did.is_local() => { + query_typeck_results = self.tcx.typeck(generator_did.expect_local()); + Some(&query_typeck_results) + } + _ => None, // Do not ICE on closure typeck (#66868). }; + if let Some(typeck_results) = typeck_results { + if let Some(upvars) = self.tcx.upvars_mentioned(generator_did) { + interior_or_upvar_span = upvars.iter().find_map(|(upvar_id, upvar)| { + let upvar_ty = typeck_results.node_type(*upvar_id); + let upvar_ty = self.resolve_vars_if_possible(upvar_ty); + if ty_matches(ty::Binder::dummy(upvar_ty)) { + Some(GeneratorInteriorOrUpvar::Upvar(upvar.span)) + } else { + None + } + }); + }; - // The generator interior types share the same binders - if let Some(cause) = - typeck_results.generator_interior_types.as_ref().skip_binder().iter().find( - |ty::GeneratorInteriorTypeCause { ty, .. }| { - ty_matches(typeck_results.generator_interior_types.rebind(ty)) - }, - ) - { - // Check to see if any awaited expressions have the target type. - let from_awaited_ty = visitor - .awaits - .into_iter() - .map(|id| hir.expect_expr(id)) - .find(|await_expr| { - let ty = typeck_results.expr_ty_adjusted(&await_expr); - debug!( - "maybe_note_obligation_cause_for_async_await: await_expr={:?}", - await_expr - ); - ty_matches(ty::Binder::dummy(ty)) - }) - .map(|expr| expr.span); - let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } = cause; + // The generator interior types share the same binders + if let Some(cause) = + typeck_results.generator_interior_types.as_ref().skip_binder().iter().find( + |ty::GeneratorInteriorTypeCause { ty, .. }| { + ty_matches(typeck_results.generator_interior_types.rebind(ty)) + }, + ) + { + // Check to see if any awaited expressions have the target type. + let from_awaited_ty = visitor + .awaits + .into_iter() + .map(|id| hir.expect_expr(id)) + .find(|await_expr| { + ty_matches(ty::Binder::dummy(typeck_results.expr_ty_adjusted(&await_expr))) + }) + .map(|expr| expr.span); + let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } = + cause; - interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span)); - interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty)); - }; + interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span)); + interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty)); + }; + } else { + interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(span)); + } - debug!( - "maybe_note_obligation_cause_for_async_await: interior_or_upvar={:?} \ - generator_interior_types={:?}", - interior_or_upvar_span, typeck_results.generator_interior_types - ); if let Some(interior_or_upvar_span) = interior_or_upvar_span { self.note_obligation_cause_for_async_await( err, @@ -1622,7 +1591,7 @@ outer_generator: Option<DefId>, trait_ref: ty::TraitRef<'tcx>, target_ty: Ty<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, + typeck_results: Option<&ty::TypeckResults<'tcx>>, obligation: &PredicateObligation<'tcx>, next_code: Option<&ObligationCauseCode<'tcx>>, ) { @@ -1833,7 +1802,7 @@ // Look at the last interior type to get a span for the `.await`. debug!( "note_obligation_cause_for_async_await generator_interior_types: {:#?}", - typeck_results.generator_interior_types + typeck_results.as_ref().map(|t| &t.generator_interior_types) ); explain_yield(interior_span, yield_span, scope_span); } @@ -1854,10 +1823,14 @@ // ^^^^^^^ a temporary `&T` created inside this method call due to `&self` // ``` // - let is_region_borrow = typeck_results - .expr_adjustments(expr) - .iter() - .any(|adj| adj.is_region_borrow()); + let is_region_borrow = if let Some(typeck_results) = typeck_results { + typeck_results + .expr_adjustments(expr) + .iter() + .any(|adj| adj.is_region_borrow()) + } else { + false + }; // ```rust // struct Foo(*const u8); @@ -1870,15 +1843,16 @@ DefKind::Fn | DefKind::Ctor(..) => target_ty.is_unsafe_ptr(), _ => false, }; - - if (typeck_results.is_method_call(e) && is_region_borrow) - || is_raw_borrow_inside_fn_like_call - { - err.span_help( - parent_span, - "consider moving this into a `let` \ + if let Some(typeck_results) = typeck_results { + if (typeck_results.is_method_call(e) && is_region_borrow) + || is_raw_borrow_inside_fn_like_call + { + err.span_help( + parent_span, + "consider moving this into a `let` \ binding to create a shorter lived borrow", - ); + ); + } } } } @@ -1983,15 +1957,9 @@ region, object_ty, )); } - ObligationCauseCode::ItemObligation(item_def_id) => { - let item_name = tcx.def_path_str(item_def_id); - let msg = format!("required by `{}`", item_name); - let sp = tcx - .hir() - .span_if_local(item_def_id) - .unwrap_or_else(|| tcx.def_span(item_def_id)); - let sp = tcx.sess.source_map().guess_head_span(sp); - err.span_note(sp, &msg); + ObligationCauseCode::ItemObligation(_item_def_id) => { + // We hold the `DefId` of the item introducing the obligation, but displaying it + // doesn't add user usable information. It always point at an associated item. } ObligationCauseCode::BindingObligation(item_def_id, span) => { let item_name = tcx.def_path_str(item_def_id); @@ -2363,11 +2331,8 @@ ) }); } - ObligationCauseCode::CompareImplMethodObligation { - item_name, - trait_item_def_id, - .. - } => { + ObligationCauseCode::CompareImplMethodObligation { trait_item_def_id, .. } => { + let item_name = self.tcx.item_name(trait_item_def_id); let msg = format!( "the requirement `{}` appears on the impl method `{}` but not on the \ corresponding trait method", @@ -2392,9 +2357,8 @@ } err.span_note(assoc_span, &msg); } - ObligationCauseCode::CompareImplTypeObligation { - item_name, trait_item_def_id, .. - } => { + ObligationCauseCode::CompareImplTypeObligation { trait_item_def_id, .. } => { + let item_name = self.tcx.item_name(trait_item_def_id); let msg = format!( "the requirement `{}` appears on the associated impl type `{}` but not on the \ corresponding associated trait type", @@ -2484,13 +2448,7 @@ obligation.param_env, ); - let item_def_id = self - .tcx - .associated_items(future_trait) - .in_definition_order() - .next() - .unwrap() - .def_id; + let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; // `<T as Future>::Output` let projection_ty = ty::ProjectionTy { // `T`
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 465d146..e121837 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -126,10 +126,7 @@ } /// Attempts to select obligations using `selcx`. - fn select( - &mut self, - selcx: &mut SelectionContext<'a, 'tcx>, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { + fn select(&mut self, selcx: &mut SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> { let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); let _enter = span.enter(); @@ -163,7 +160,7 @@ errors.len() ); - if errors.is_empty() { Ok(()) } else { Err(errors) } + errors } } @@ -223,41 +220,36 @@ .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); } - fn select_all_or_error( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { - self.select_where_possible(infcx)?; + fn select_all_or_error(&mut self, infcx: &InferCtxt<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> { + { + let errors = self.select_where_possible(infcx); + if !errors.is_empty() { + return errors; + } + } - let errors: Vec<_> = self - .predicates - .to_errors(CodeAmbiguity) - .into_iter() - .map(to_fulfillment_error) - .collect(); - if errors.is_empty() { Ok(()) } else { Err(errors) } + self.predicates.to_errors(CodeAmbiguity).into_iter().map(to_fulfillment_error).collect() } fn select_all_with_constness_or_error( &mut self, infcx: &InferCtxt<'_, 'tcx>, constness: rustc_hir::Constness, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { - self.select_with_constness_where_possible(infcx, constness)?; + ) -> Vec<FulfillmentError<'tcx>> { + { + let errors = self.select_with_constness_where_possible(infcx, constness); + if !errors.is_empty() { + return errors; + } + } - let errors: Vec<_> = self - .predicates - .to_errors(CodeAmbiguity) - .into_iter() - .map(to_fulfillment_error) - .collect(); - if errors.is_empty() { Ok(()) } else { Err(errors) } + self.predicates.to_errors(CodeAmbiguity).into_iter().map(to_fulfillment_error).collect() } fn select_where_possible( &mut self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { + ) -> Vec<FulfillmentError<'tcx>> { let mut selcx = SelectionContext::new(infcx); self.select(&mut selcx) } @@ -266,7 +258,7 @@ &mut self, infcx: &InferCtxt<'_, 'tcx>, constness: hir::Constness, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { + ) -> Vec<FulfillmentError<'tcx>> { let mut selcx = SelectionContext::with_constness(infcx, constness); self.select(&mut selcx) }
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index cedd1aa..fa8890fc 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -33,7 +33,8 @@ | ty::Char | ty::RawPtr(..) | ty::Never - | ty::Ref(_, _, hir::Mutability::Not) => return Ok(()), + | ty::Ref(_, _, hir::Mutability::Not) + | ty::Array(..) => return Ok(()), ty::Adt(adt, substs) => (adt, substs),
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index b31d6d6..4bc22d5 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -65,7 +65,8 @@ pub use self::structural_match::search_for_structural_match_violation; pub use self::structural_match::NonStructuralMatchTy; pub use self::util::{ - elaborate_obligations, elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs, + elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span, + elaborate_trait_ref, elaborate_trait_refs, }; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; pub use self::util::{ @@ -81,9 +82,14 @@ pub use rustc_infer::traits::*; /// Whether to skip the leak check, as part of a future compatibility warning step. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +/// +/// The "default" for skip-leak-check corresponds to the current +/// behavior (do not skip the leak check) -- not the behavior we are +/// transitioning into. +#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] pub enum SkipLeakCheck { Yes, + #[default] No, } @@ -93,15 +99,6 @@ } } -/// The "default" for skip-leak-check corresponds to the current -/// behavior (do not skip the leak check) -- not the behavior we are -/// transitioning into. -impl Default for SkipLeakCheck { - fn default() -> Self { - SkipLeakCheck::No - } -} - /// The mode that trait queries run in. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum TraitQueryMode { @@ -180,8 +177,8 @@ // Note: we only assume something is `Copy` if we can // *definitively* show that it implements `Copy`. Otherwise, // assume it is move; linear is always ok. - match fulfill_cx.select_all_or_error(infcx) { - Ok(()) => { + match fulfill_cx.select_all_or_error(infcx).as_slice() { + [] => { debug!( "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success", ty, @@ -189,12 +186,12 @@ ); true } - Err(e) => { + errors => { debug!( - "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} errors={:?}", - ty, - infcx.tcx.def_path_str(def_id), - e + ?ty, + bound = %infcx.tcx.def_path_str(def_id), + ?errors, + "type_known_to_meet_bound_modulo_regions" ); false } @@ -410,7 +407,10 @@ } debug!("fully_normalize: select_all_or_error start"); - fulfill_cx.select_all_or_error(infcx)?; + let errors = fulfill_cx.select_all_or_error(infcx); + if !errors.is_empty() { + return Err(errors); + } debug!("fully_normalize: select_all_or_error complete"); let resolved_value = infcx.resolve_vars_if_possible(normalized_value); debug!("fully_normalize: resolved_value={:?}", resolved_value); @@ -441,7 +441,9 @@ fulfill_cx.register_predicate_obligation(&infcx, obligation); } - fulfill_cx.select_all_or_error(&infcx).is_err() + let errors = fulfill_cx.select_all_or_error(&infcx); + + !errors.is_empty() }); debug!("impossible_predicates = {:?}", result); result @@ -748,6 +750,9 @@ ) -> usize { let (trait_to_be_found, trait_owning_vtable) = key; + // #90177 + let trait_to_be_found_erased = tcx.erase_regions(trait_to_be_found); + let vtable_segment_callback = { let mut vtable_base = 0; @@ -757,7 +762,7 @@ vtable_base += COMMON_VTABLE_ENTRIES.len(); } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - if trait_ref == trait_to_be_found { + if tcx.erase_regions(trait_ref) == trait_to_be_found_erased { return ControlFlow::Break(vtable_base); } vtable_base += util::count_own_vtable_entries(tcx, trait_ref); @@ -804,6 +809,7 @@ ty::Binder::dummy(ty::TraitPredicate { trait_ref, constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, }), );
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 0bb00df..afc5465 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -647,9 +647,7 @@ debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty); let traits = (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait()); - let (unsize_did, dispatch_from_dyn_did) = if let (Some(u), Some(cu)) = traits { - (u, cu) - } else { + let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else { debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits"); return false; }; @@ -839,14 +837,13 @@ // constants which are not considered const evaluatable. use rustc_middle::thir::abstract_const::Node; if let Ok(Some(ct)) = AbstractConst::new(self.tcx, uv.shrink()) { - const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node.root() { - Node::Leaf(leaf) => { - let leaf = leaf.subst(self.tcx, ct.substs); - self.visit_const(leaf) - } - Node::Cast(_, _, ty) => self.visit_ty(ty), - Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { - ControlFlow::CONTINUE + const_evaluatable::walk_abstract_const(self.tcx, ct, |node| { + match node.root(self.tcx) { + Node::Leaf(leaf) => self.visit_const(leaf), + Node::Cast(_, _, ty) => self.visit_ty(ty), + Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { + ControlFlow::CONTINUE + } } }) } else {
diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index 209fd83..85ca4db 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
@@ -164,9 +164,7 @@ ) -> Result<Option<Self>, ErrorReported> { let attrs = tcx.get_attrs(impl_def_id); - let attr = if let Some(item) = tcx.sess.find_by_name(&attrs, sym::rustc_on_unimplemented) { - item - } else { + let Some(attr) = tcx.sess.find_by_name(&attrs, sym::rustc_on_unimplemented) else { return Ok(None); };
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index db8a6d9..4a23206 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -20,6 +20,7 @@ use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use crate::traits::error_reporting::InferCtxtExt as _; +use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; @@ -944,9 +945,14 @@ Normalized { value: projected_ty, obligations: projected_obligations } }; + let mut deduped: SsoHashSet<_> = Default::default(); let mut canonical = SelectionContext::with_query_mode(selcx.infcx(), TraitQueryMode::Canonical); + result.obligations.drain_filter(|projected_obligation| { + if !deduped.insert(projected_obligation.clone()) { + return true; + } // If any global obligations always apply, considering regions, then we don't // need to include them. The `is_global` check rules out inference variables, // so there's no need for the caller of `opt_normalize_projection_type` @@ -1734,7 +1740,7 @@ ty: ret_type, }); - confirm_param_env_candidate(selcx, obligation, predicate, false) + confirm_param_env_candidate(selcx, obligation, predicate, true) } fn confirm_param_env_candidate<'cx, 'tcx>( @@ -1754,8 +1760,18 @@ ); let cache_projection = cache_entry.projection_ty; - let obligation_projection = obligation.predicate; let mut nested_obligations = Vec::new(); + let obligation_projection = obligation.predicate; + let obligation_projection = ensure_sufficient_stack(|| { + normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation_projection, + &mut nested_obligations, + ) + }); let cache_projection = if potentially_unnormalized_candidate { ensure_sufficient_stack(|| { normalize_with_depth_to( @@ -1771,6 +1787,8 @@ cache_projection }; + debug!(?cache_projection, ?obligation_projection); + match infcx.at(cause, param_env).eq(cache_projection, obligation_projection) { Ok(InferOk { value: _, obligations }) => { nested_obligations.extend(obligations);
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index b5398f8..0a85676 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
@@ -77,10 +77,11 @@ let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?; debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); fulfill_cx.register_predicate_obligations(infcx, obligations); - if let Err(e) = fulfill_cx.select_all_or_error(infcx) { + let errors = fulfill_cx.select_all_or_error(infcx); + if !errors.is_empty() { infcx.tcx.sess.diagnostic().delay_span_bug( DUMMY_SP, - &format!("errors selecting obligation during MIR typeck: {:?}", e), + &format!("errors selecting obligation during MIR typeck: {:?}", errors), ); }
diff --git a/compiler/rustc_trait_selection/src/traits/relationships.rs b/compiler/rustc_trait_selection/src/traits/relationships.rs index 7751dd8..e0098cc 100644 --- a/compiler/rustc_trait_selection/src/traits/relationships.rs +++ b/compiler/rustc_trait_selection/src/traits/relationships.rs
@@ -44,6 +44,7 @@ ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: predicate.constness, + polarity: predicate.polarity, }) }) .to_predicate(infcx.tcx),
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 856ea43..0ff3611 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -18,7 +18,7 @@ use crate::traits::coherence::Conflict; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{util, SelectionResult}; -use crate::traits::{ErrorReporting, Overflow, Unimplemented}; +use crate::traits::{Ambiguous, ErrorReporting, Overflow, Unimplemented}; use super::BuiltinImplConditions; use super::IntercrateAmbiguityCause; @@ -121,7 +121,7 @@ return Ok(None); } - let mut candidates = candidate_set.vec; + let candidates = candidate_set.vec; debug!(?stack, ?candidates, "assembled {} candidates", candidates.len()); @@ -134,6 +134,8 @@ // candidate which assumes $0 == int, one that assumes `$0 == // usize`, etc. This spells an ambiguity. + let mut candidates = self.filter_impls(candidates, stack.obligation); + // If there is more than one candidate, first winnow them down // by considering extra conditions (nested obligations and so // forth). We don't winnow if there is exactly one @@ -149,7 +151,7 @@ // Instead, we select the right impl now but report "`Bar` does // not implement `Clone`". if candidates.len() == 1 { - return self.filter_impls(candidates.pop().unwrap(), stack.obligation); + return self.filter_reservation_impls(candidates.pop().unwrap(), stack.obligation); } // Winnow, but record the exact outcome of evaluation, which @@ -195,7 +197,15 @@ // and report ambiguity. if i > 1 { debug!("multiple matches, ambig"); - return Ok(None); + return Err(Ambiguous( + candidates + .into_iter() + .filter_map(|c| match c.candidate { + SelectionCandidate::ImplCandidate(def_id) => Some(def_id), + _ => None, + }) + .collect(), + )); } } } @@ -223,7 +233,7 @@ } // Just one candidate left. - self.filter_impls(candidates.pop().unwrap().candidate, stack.obligation) + self.filter_reservation_impls(candidates.pop().unwrap().candidate, stack.obligation) } #[instrument(skip(self, stack), level = "debug")] @@ -254,68 +264,75 @@ let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - self.assemble_candidates_for_trait_alias(obligation, &mut candidates); - - // Other bounds. Consider both in-scope bounds from fn decl - // and applicable impls. There is a certain set of precedence rules here. - let def_id = obligation.predicate.def_id(); - let lang_items = self.tcx().lang_items(); - - if lang_items.copy_trait() == Some(def_id) { - debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); - - // User-defined copy impls are permitted, but only for - // structs and enums. + // The only way to prove a NotImplemented(T: Foo) predicate is via a negative impl. + // There are no compiler built-in rules for this. + if obligation.polarity() == ty::ImplPolarity::Negative { + self.assemble_candidates_for_trait_alias(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); - - // For other types, we'll use the builtin rules. - let copy_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); - } else if lang_items.discriminant_kind_trait() == Some(def_id) { - // `DiscriminantKind` is automatically implemented for every type. - candidates.vec.push(DiscriminantKindCandidate); - } else if lang_items.pointee_trait() == Some(def_id) { - // `Pointee` is automatically implemented for every type. - candidates.vec.push(PointeeCandidate); - } else if lang_items.sized_trait() == Some(def_id) { - // Sized is never implementable by end-users, it is - // always automatically computed. - let sized_conditions = self.sized_conditions(obligation); - self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); - } else if lang_items.unsize_trait() == Some(def_id) { - self.assemble_candidates_for_unsizing(obligation, &mut candidates); - } else if lang_items.drop_trait() == Some(def_id) - && obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst - { - if self.is_in_const_context { - self.assemble_const_drop_candidates(obligation, &mut candidates)?; - } else { - debug!("passing ~const Drop bound; in non-const context"); - // `~const Drop` when we are not in a const context has no effect. - candidates.vec.push(ConstDropCandidate) - } } else { - if lang_items.clone_trait() == Some(def_id) { - // Same builtin conditions as `Copy`, i.e., every type which has builtin support - // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` - // types have builtin support for `Clone`. - let clone_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates); + self.assemble_candidates_for_trait_alias(obligation, &mut candidates); + + // Other bounds. Consider both in-scope bounds from fn decl + // and applicable impls. There is a certain set of precedence rules here. + let def_id = obligation.predicate.def_id(); + let lang_items = self.tcx().lang_items(); + + if lang_items.copy_trait() == Some(def_id) { + debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); + + // User-defined copy impls are permitted, but only for + // structs and enums. + self.assemble_candidates_from_impls(obligation, &mut candidates); + + // For other types, we'll use the builtin rules. + let copy_conditions = self.copy_clone_conditions(obligation); + self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); + } else if lang_items.discriminant_kind_trait() == Some(def_id) { + // `DiscriminantKind` is automatically implemented for every type. + candidates.vec.push(DiscriminantKindCandidate); + } else if lang_items.pointee_trait() == Some(def_id) { + // `Pointee` is automatically implemented for every type. + candidates.vec.push(PointeeCandidate); + } else if lang_items.sized_trait() == Some(def_id) { + // Sized is never implementable by end-users, it is + // always automatically computed. + let sized_conditions = self.sized_conditions(obligation); + self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); + } else if lang_items.unsize_trait() == Some(def_id) { + self.assemble_candidates_for_unsizing(obligation, &mut candidates); + } else if lang_items.drop_trait() == Some(def_id) + && obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst + { + if self.is_in_const_context { + self.assemble_const_drop_candidates(obligation, stack, &mut candidates)?; + } else { + debug!("passing ~const Drop bound; in non-const context"); + // `~const Drop` when we are not in a const context has no effect. + candidates.vec.push(ConstDropCandidate) + } + } else { + if lang_items.clone_trait() == Some(def_id) { + // Same builtin conditions as `Copy`, i.e., every type which has builtin support + // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` + // types have builtin support for `Clone`. + let clone_conditions = self.copy_clone_conditions(obligation); + self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates); + } + + self.assemble_generator_candidates(obligation, &mut candidates); + self.assemble_closure_candidates(obligation, &mut candidates); + self.assemble_fn_pointer_candidates(obligation, &mut candidates); + self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_from_object_ty(obligation, &mut candidates); } - self.assemble_generator_candidates(obligation, &mut candidates); - self.assemble_closure_candidates(obligation, &mut candidates); - self.assemble_fn_pointer_candidates(obligation, &mut candidates); - self.assemble_candidates_from_impls(obligation, &mut candidates); - self.assemble_candidates_from_object_ty(obligation, &mut candidates); - } - - self.assemble_candidates_from_projected_tys(obligation, &mut candidates); - self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; - // Auto implementations have lower priority, so we only - // consider triggering a default if there is no other impl that can apply. - if candidates.vec.is_empty() { - self.assemble_candidates_from_auto_impls(obligation, &mut candidates); + self.assemble_candidates_from_projected_tys(obligation, &mut candidates); + self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; + // Auto implementations have lower priority, so we only + // consider triggering a default if there is no other impl that can apply. + if candidates.vec.is_empty() { + self.assemble_candidates_from_auto_impls(obligation, &mut candidates); + } } debug!("candidate list size: {}", candidates.vec.len()); Ok(candidates) @@ -376,7 +393,7 @@ for bound in matching_bounds { let wc = self.evaluate_where_clause(stack, bound.value)?; if wc.may_apply() { - candidates.vec.push(ParamCandidate(bound)); + candidates.vec.push(ParamCandidate((bound, stack.obligation.polarity()))); } } @@ -716,9 +733,7 @@ cause.clone(), ); - let data = if let ty::Dynamic(ref data, ..) = normalized_ty.kind() { - data - } else { + let ty::Dynamic(data, ..) = normalized_ty.kind() else { return None; }; @@ -896,9 +911,10 @@ } } - fn assemble_const_drop_candidates( + fn assemble_const_drop_candidates<'a>( &mut self, obligation: &TraitObligation<'tcx>, + obligation_stack: &TraitObligationStack<'a, 'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) -> Result<(), SelectionError<'tcx>> { let mut stack: Vec<(Ty<'tcx>, usize)> = vec![(obligation.self_ty().skip_binder(), 0)]; @@ -907,7 +923,7 @@ let mut noreturn = false; self.check_recursion_depth(depth, obligation)?; - let mut copy_candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; + let mut new_candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; let mut copy_obligation = obligation.with(obligation.predicate.rebind(ty::TraitPredicate { trait_ref: ty::TraitRef { @@ -915,15 +931,32 @@ substs: self.tcx().mk_substs_trait(ty, &[]), }, constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, })); copy_obligation.recursion_depth = depth + 1; - self.assemble_candidates_from_impls(©_obligation, &mut copy_candidates); + self.assemble_candidates_from_impls(©_obligation, &mut new_candidates); let copy_conditions = self.copy_clone_conditions(©_obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut copy_candidates); - if !copy_candidates.vec.is_empty() { + self.assemble_builtin_bound_candidates(copy_conditions, &mut new_candidates); + let copy_stack = self.push_stack(obligation_stack.list(), ©_obligation); + self.assemble_candidates_from_caller_bounds(©_stack, &mut new_candidates)?; + + let const_drop_obligation = + obligation.with(obligation.predicate.rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: self.tcx().require_lang_item(hir::LangItem::Drop, None), + substs: self.tcx().mk_substs_trait(ty, &[]), + }, + constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, + })); + + let const_drop_stack = self.push_stack(obligation_stack.list(), &const_drop_obligation); + self.assemble_candidates_from_caller_bounds(&const_drop_stack, &mut new_candidates)?; + + if !new_candidates.vec.is_empty() { noreturn = true; } - debug!(?copy_candidates.vec, "assemble_const_drop_candidates - copy"); + debug!(?new_candidates.vec, "assemble_const_drop_candidates"); match ty.kind() { ty::Int(_)
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index a36cb13..2f1f797 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -58,8 +58,8 @@ } ParamCandidate(param) => { - let obligations = self.confirm_param_candidate(obligation, param.value); - Ok(ImplSource::Param(obligations, param.constness)) + let obligations = self.confirm_param_candidate(obligation, param.0.value); + Ok(ImplSource::Param(obligations, param.0.constness)) } ImplCandidate(impl_def_id) => { @@ -620,23 +620,37 @@ _ => bug!("closure candidate for non-closure {:?}", obligation), }; + let obligation_predicate = obligation.predicate.to_poly_trait_ref(); + let Normalized { value: obligation_predicate, mut obligations } = + ensure_sufficient_stack(|| { + normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation_predicate, + ) + }); + let trait_ref = self.closure_trait_ref_unnormalized(obligation, substs); - let Normalized { value: trait_ref, mut obligations } = ensure_sufficient_stack(|| { - normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - trait_ref, - ) - }); + let Normalized { value: trait_ref, obligations: trait_ref_obligations } = + ensure_sufficient_stack(|| { + normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + trait_ref, + ) + }); debug!(?closure_def_id, ?trait_ref, ?obligations, "confirm closure candidate obligations"); + obligations.extend(trait_ref_obligations); obligations.extend(self.confirm_poly_trait_refs( obligation.cause.clone(), obligation.param_env, - obligation.predicate.to_poly_trait_ref(), + obligation_predicate, trait_ref, )?); @@ -948,52 +962,24 @@ let tail_field_ty = tcx.type_of(tail_field.did); let mut unsizing_params = GrowableBitSet::new_empty(); - if tcx.features().relaxed_struct_unsize { - for arg in tail_field_ty.walk(tcx) { + for arg in tail_field_ty.walk(tcx) { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.insert(i); + } + } + + // Ensure none of the other fields mention the parameters used + // in unsizing. + for field in prefix_fields { + for arg in tcx.type_of(field.did).walk(tcx) { if let Some(i) = maybe_unsizing_param_idx(arg) { - unsizing_params.insert(i); + unsizing_params.remove(i); } } + } - // Ensure none of the other fields mention the parameters used - // in unsizing. - for field in prefix_fields { - for arg in tcx.type_of(field.did).walk(tcx) { - if let Some(i) = maybe_unsizing_param_idx(arg) { - unsizing_params.remove(i); - } - } - } - - if unsizing_params.is_empty() { - return Err(Unimplemented); - } - } else { - let mut found = false; - for arg in tail_field_ty.walk(tcx) { - if let Some(i) = maybe_unsizing_param_idx(arg) { - unsizing_params.insert(i); - found = true; - } - } - if !found { - return Err(Unimplemented); - } - - // Ensure none of the other fields mention the parameters used - // in unsizing. - // FIXME(eddyb) cache this (including computing `unsizing_params`) - // by putting it in a query; it would only need the `DefId` as it - // looks at declared field types, not anything substituted. - for field in prefix_fields { - for arg in tcx.type_of(field.did).walk(tcx) { - if let Some(i) = maybe_unsizing_param_idx(arg) { - if unsizing_params.contains(i) { - return Err(Unimplemented); - } - } - } - } + if unsizing_params.is_empty() { + return Err(Unimplemented); } // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`.
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 85502a3..2aa2146 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -20,7 +20,7 @@ use super::Selection; use super::SelectionResult; use super::TraitQueryMode; -use super::{ErrorReporting, Overflow, SelectionError, Unimplemented}; +use super::{ErrorReporting, Overflow, SelectionError}; use super::{ObligationCause, PredicateObligation, TraitObligation}; use crate::infer::{InferCtxt, InferOk, TypeFreshener}; @@ -320,10 +320,7 @@ /// Returns `true` if the trait predicate is considerd `const` to this selection context. pub fn is_trait_predicate_const(&self, pred: ty::TraitPredicate<'_>) -> bool { - match pred.constness { - ty::BoundConstness::ConstIfConst if self.is_in_const_context => true, - _ => false, - } + matches!(pred.constness, ty::BoundConstness::ConstIfConst) && self.is_in_const_context } /// Returns `true` if the predicate is considered `const` to @@ -357,18 +354,16 @@ &mut self, obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { - debug_assert!(!obligation.predicate.has_escaping_bound_vars()); - - let pec = &ProvisionalEvaluationCache::default(); - let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); - - let candidate = match self.candidate_from_obligation(&stack) { + let candidate = match self.select_from_obligation(obligation) { Err(SelectionError::Overflow) => { // In standard mode, overflow must have been caught and reported // earlier. assert!(self.query_mode == TraitQueryMode::Canonical); return Err(SelectionError::Overflow); } + Err(SelectionError::Ambiguous(_)) => { + return Ok(None); + } Err(e) => { return Err(e); } @@ -391,6 +386,18 @@ } } + crate fn select_from_obligation( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + debug_assert!(!obligation.predicate.has_escaping_bound_vars()); + + let pec = &ProvisionalEvaluationCache::default(); + let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); + + self.candidate_from_obligation(&stack) + } + /////////////////////////////////////////////////////////////////////////// // EVALUATION // @@ -709,7 +716,11 @@ debug!(?fresh_trait_ref); - if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) { + if let Some(result) = self.check_evaluation_cache( + obligation.param_env, + fresh_trait_ref, + obligation.polarity(), + ) { debug!(?result, "CACHE HIT"); return Ok(result); } @@ -739,12 +750,19 @@ let reached_depth = stack.reached_depth.get(); if reached_depth >= stack.depth { debug!(?result, "CACHE MISS"); - self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, dep_node, result); + self.insert_evaluation_cache( + obligation.param_env, + fresh_trait_ref, + obligation.polarity(), + dep_node, + result, + ); stack.cache().on_completion(stack.dfn, |fresh_trait_ref, provisional_result| { self.insert_evaluation_cache( obligation.param_env, fresh_trait_ref, + obligation.polarity(), dep_node, provisional_result.max(result), ); @@ -855,34 +873,39 @@ // precise still. let unbound_input_types = stack.fresh_trait_ref.value.skip_binder().substs.types().any(|ty| ty.is_fresh()); - // This check was an imperfect workaround for a bug in the old - // intercrate mode; it should be removed when that goes away. - if unbound_input_types && self.intercrate { - debug!("evaluate_stack --> unbound argument, intercrate --> ambiguous",); - // Heuristics: show the diagnostics when there are no candidates in crate. - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - if let Ok(candidate_set) = self.assemble_candidates(stack) { - if !candidate_set.ambiguous && candidate_set.vec.is_empty() { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let cause = - with_no_trimmed_paths(|| IntercrateAmbiguityCause::DownstreamCrate { - trait_desc: trait_ref.print_only_trait_path().to_string(), - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, + + if stack.obligation.polarity() != ty::ImplPolarity::Negative { + // This check was an imperfect workaround for a bug in the old + // intercrate mode; it should be removed when that goes away. + if unbound_input_types && self.intercrate { + debug!("evaluate_stack --> unbound argument, intercrate --> ambiguous",); + // Heuristics: show the diagnostics when there are no candidates in crate. + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + if let Ok(candidate_set) = self.assemble_candidates(stack) { + if !candidate_set.ambiguous && candidate_set.vec.is_empty() { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let cause = with_no_trimmed_paths(|| { + IntercrateAmbiguityCause::DownstreamCrate { + trait_desc: trait_ref.print_only_trait_path().to_string(), + self_desc: if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }, + } }); - debug!(?cause, "evaluate_stack: pushing cause"); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + debug!(?cause, "evaluate_stack: pushing cause"); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } } } + return Ok(EvaluatedToAmbig); } - return Ok(EvaluatedToAmbig); } + if unbound_input_types && stack.iter().skip(1).any(|prev| { stack.obligation.param_env == prev.obligation.param_env @@ -899,6 +922,7 @@ match self.candidate_from_obligation(stack) { Ok(Some(c)) => self.evaluate_candidate(stack, &c), + Err(SelectionError::Ambiguous(_)) => Ok(EvaluatedToAmbig), Ok(None) => Ok(EvaluatedToAmbig), Err(Overflow) => Err(OverflowError::Canonical), Err(ErrorReporting) => Err(OverflowError::ErrorReporting), @@ -977,6 +1001,7 @@ &self, param_env: ty::ParamEnv<'tcx>, trait_ref: ty::ConstnessAnd<ty::PolyTraitRef<'tcx>>, + polarity: ty::ImplPolarity, ) -> Option<EvaluationResult> { // Neither the global nor local cache is aware of intercrate // mode, so don't do any caching. In particular, we might @@ -988,17 +1013,19 @@ let tcx = self.tcx(); if self.can_use_global_caches(param_env) { - if let Some(res) = tcx.evaluation_cache.get(¶m_env.and(trait_ref), tcx) { + if let Some(res) = tcx.evaluation_cache.get(&(param_env.and(trait_ref), polarity), tcx) + { return Some(res); } } - self.infcx.evaluation_cache.get(¶m_env.and(trait_ref), tcx) + self.infcx.evaluation_cache.get(&(param_env.and(trait_ref), polarity), tcx) } fn insert_evaluation_cache( &mut self, param_env: ty::ParamEnv<'tcx>, trait_ref: ty::ConstnessAnd<ty::PolyTraitRef<'tcx>>, + polarity: ty::ImplPolarity, dep_node: DepNodeIndex, result: EvaluationResult, ) { @@ -1023,13 +1050,17 @@ // FIXME: Due to #50507 this overwrites the different values // This should be changed to use HashMapExt::insert_same // when that is fixed - self.tcx().evaluation_cache.insert(param_env.and(trait_ref), dep_node, result); + self.tcx().evaluation_cache.insert( + (param_env.and(trait_ref), polarity), + dep_node, + result, + ); return; } } debug!(?trait_ref, ?result, "insert_evaluation_cache"); - self.infcx.evaluation_cache.insert(param_env.and(trait_ref), dep_node, result); + self.infcx.evaluation_cache.insert((param_env.and(trait_ref), polarity), dep_node, result); } /// For various reasons, it's possible for a subobligation @@ -1094,67 +1125,89 @@ (result, dep_node) } + /// filter_impls filters constant trait obligations and candidates that have a positive impl + /// for a negative goal and a negative impl for a positive goal #[instrument(level = "debug", skip(self))] fn filter_impls( &mut self, + candidates: Vec<SelectionCandidate<'tcx>>, + obligation: &TraitObligation<'tcx>, + ) -> Vec<SelectionCandidate<'tcx>> { + let tcx = self.tcx(); + let mut result = Vec::with_capacity(candidates.len()); + + for candidate in candidates { + // Respect const trait obligations + if self.is_trait_predicate_const(obligation.predicate.skip_binder()) { + match candidate { + // const impl + ImplCandidate(def_id) + if tcx.impl_constness(def_id) == hir::Constness::Const => {} + // const param + ParamCandidate(( + ty::ConstnessAnd { constness: ty::BoundConstness::ConstIfConst, .. }, + _, + )) => {} + // auto trait impl + AutoImplCandidate(..) => {} + // generator, this will raise error in other places + // or ignore error with const_async_blocks feature + GeneratorCandidate => {} + // FnDef where the function is const + FnPointerCandidate { is_const: true } => {} + ConstDropCandidate => {} + _ => { + // reject all other types of candidates + continue; + } + } + } + + if let ImplCandidate(def_id) = candidate { + if ty::ImplPolarity::Reservation == tcx.impl_polarity(def_id) + || obligation.polarity() == tcx.impl_polarity(def_id) + || self.allow_negative_impls + { + result.push(candidate); + } + } else { + result.push(candidate); + } + } + + result + } + + /// filter_reservation_impls filter reservation impl for any goal as ambiguous + #[instrument(level = "debug", skip(self))] + fn filter_reservation_impls( + &mut self, candidate: SelectionCandidate<'tcx>, obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { let tcx = self.tcx(); - // Respect const trait obligations - if self.is_trait_predicate_const(obligation.predicate.skip_binder()) { - match candidate { - // const impl - ImplCandidate(def_id) if tcx.impl_constness(def_id) == hir::Constness::Const => {} - // const param - ParamCandidate(ty::ConstnessAnd { - constness: ty::BoundConstness::ConstIfConst, - .. - }) => {} - // auto trait impl - AutoImplCandidate(..) => {} - // generator, this will raise error in other places - // or ignore error with const_async_blocks feature - GeneratorCandidate => {} - // FnDef where the function is const - FnPointerCandidate { is_const: true } => {} - ConstDropCandidate => {} - _ => { - // reject all other types of candidates - return Err(Unimplemented); - } - } - } - // Treat negative impls as unimplemented, and reservation impls as ambiguity. + // Treat reservation impls as ambiguity. if let ImplCandidate(def_id) = candidate { - match tcx.impl_polarity(def_id) { - ty::ImplPolarity::Negative if !self.allow_negative_impls => { - return Err(Unimplemented); - } - ty::ImplPolarity::Reservation => { - if let Some(intercrate_ambiguity_clauses) = - &mut self.intercrate_ambiguity_causes - { - let attrs = tcx.get_attrs(def_id); - let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl); - let value = attr.and_then(|a| a.value_str()); - if let Some(value) = value { - debug!( - "filter_impls: \ + if let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) { + if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { + let attrs = tcx.get_attrs(def_id); + let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl); + let value = attr.and_then(|a| a.value_str()); + if let Some(value) = value { + debug!( + "filter_reservation_impls: \ reservation impl ambiguity on {:?}", - def_id - ); - intercrate_ambiguity_clauses.push( - IntercrateAmbiguityCause::ReservationImpl { - message: value.to_string(), - }, - ); - } + def_id + ); + intercrate_ambiguity_clauses.push( + IntercrateAmbiguityCause::ReservationImpl { + message: value.to_string(), + }, + ); } - return Ok(None); } - _ => {} - }; + return Ok(None); + } } Ok(Some(candidate)) } @@ -1162,7 +1215,7 @@ fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<Conflict> { debug!("is_knowable(intercrate={:?})", self.intercrate); - if !self.intercrate { + if !self.intercrate || stack.obligation.polarity() == ty::ImplPolarity::Negative { return None; } @@ -1219,14 +1272,14 @@ if self.can_use_global_caches(param_env) { if let Some(res) = tcx .selection_cache - .get(¶m_env.and(trait_ref).with_constness(pred.constness), tcx) + .get(&(param_env.and(trait_ref).with_constness(pred.constness), pred.polarity), tcx) { return Some(res); } } self.infcx .selection_cache - .get(¶m_env.and(trait_ref).with_constness(pred.constness), tcx) + .get(&(param_env.and(trait_ref).with_constness(pred.constness), pred.polarity), tcx) } /// Determines whether can we safely cache the result @@ -1286,7 +1339,7 @@ debug!(?trait_ref, ?candidate, "insert_candidate_cache global"); // This may overwrite the cache with the same value. tcx.selection_cache.insert( - param_env.and(trait_ref).with_constness(pred.constness), + (param_env.and(trait_ref).with_constness(pred.constness), pred.polarity), dep_node, candidate, ); @@ -1297,7 +1350,7 @@ debug!(?trait_ref, ?candidate, "insert_candidate_cache local"); self.infcx.selection_cache.insert( - param_env.and(trait_ref).with_constness(pred.constness), + (param_env.and(trait_ref).with_constness(pred.constness), pred.polarity), dep_node, candidate, ); @@ -1491,8 +1544,9 @@ // Check if a bound would previously have been removed when normalizing // the param_env so that it can be given the lowest priority. See // #50825 for the motivation for this. - let is_global = - |cand: &ty::PolyTraitRef<'_>| cand.is_known_global() && !cand.has_late_bound_regions(); + let is_global = |cand: &ty::PolyTraitRef<'tcx>| { + cand.is_global(self.infcx.tcx) && !cand.has_late_bound_regions() + }; // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, // and `DiscriminantKindCandidate` to anything else. @@ -1523,10 +1577,14 @@ | ConstDropCandidate, ) => false, - (ParamCandidate(other), ParamCandidate(victim)) => { + ( + ParamCandidate((other, other_polarity)), + ParamCandidate((victim, victim_polarity)), + ) => { let same_except_bound_vars = other.value.skip_binder() == victim.value.skip_binder() && other.constness == victim.constness + && other_polarity == victim_polarity && !other.value.skip_binder().has_escaping_bound_vars(); if same_except_bound_vars { // See issue #84398. In short, we can generate multiple ParamCandidates which are @@ -1537,6 +1595,7 @@ other.value.bound_vars().len() <= victim.value.bound_vars().len() } else if other.value == victim.value && victim.constness == ty::BoundConstness::NotConst + && other_polarity == victim_polarity { // Drop otherwise equivalent non-const candidates in favor of const candidates. true @@ -1566,11 +1625,11 @@ | TraitAliasCandidate(..) | ObjectCandidate(_) | ProjectionCandidate(_), - ) => !is_global(&cand.value), + ) => !is_global(&cand.0.value), (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { // Prefer these to a global where-clause bound // (see issue #50825). - is_global(&cand.value) + is_global(&cand.0.value) } ( ImplCandidate(_) @@ -1586,7 +1645,7 @@ ) => { // Prefer these to a global where-clause bound // (see issue #50825). - is_global(&cand.value) && other.evaluation.must_apply_modulo_regions() + is_global(&cand.0.value) && other.evaluation.must_apply_modulo_regions() } (ProjectionCandidate(i), ProjectionCandidate(j)) @@ -1800,7 +1859,8 @@ | ty::Char | ty::RawPtr(..) | ty::Never - | ty::Ref(_, _, hir::Mutability::Not) => { + | ty::Ref(_, _, hir::Mutability::Not) + | ty::Array(..) => { // Implementations provided in libcore None } @@ -1813,11 +1873,6 @@ | ty::Foreign(..) | ty::Ref(_, _, hir::Mutability::Mut) => None, - ty::Array(element_ty, _) => { - // (*) binder moved here - Where(obligation.predicate.rebind(vec![element_ty])) - } - ty::Tuple(tys) => { // (*) binder moved here Where(obligation.predicate.rebind(tys.iter().map(|k| k.expect_ty()).collect()))
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 88aca79..b64c555 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -225,8 +225,18 @@ for oblig in obligations.chain(more_obligations) { fulfill_cx.register_predicate_obligation(&infcx, oblig); } - match fulfill_cx.select_all_or_error(infcx) { - Err(errors) => { + match fulfill_cx.select_all_or_error(infcx).as_slice() { + [] => { + debug!( + "fulfill_implication: an impl for {:?} specializes {:?}", + source_trait_ref, target_trait_ref + ); + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + Ok(infcx.resolve_vars_if_possible(target_substs)) + } + errors => { // no dice! debug!( "fulfill_implication: for impls on {:?} and {:?}, \ @@ -238,17 +248,6 @@ ); Err(()) } - - Ok(()) => { - debug!( - "fulfill_implication: an impl for {:?} specializes {:?}", - source_trait_ref, target_trait_ref - ); - - // Now resolve the *substitution* we built for the target earlier, replacing - // the inference variables inside with whatever we got from fulfillment. - Ok(infcx.resolve_vars_if_possible(target_substs)) - } } }) } @@ -292,6 +291,11 @@ sg } +// This function is only used when +// encountering errors and inlining +// it negatively impacts perf. +#[cold] +#[inline(never)] fn report_overlap_conflict( tcx: TyCtxt<'_>, overlap: OverlapError, @@ -444,8 +448,12 @@ match used_to_be_allowed { None => { sg.has_errored = true; - let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); - decorate(LintDiagnosticBuilder::new(err)); + if overlap.with_impl.is_local() || !tcx.orphan_check_crate(()).contains(&impl_def_id) { + let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); + decorate(LintDiagnosticBuilder::new(err)); + } else { + tcx.sess.delay_span_bug(impl_span, "impl should have failed the orphan check"); + } } Some(kind) => { let lint = match kind { @@ -464,7 +472,7 @@ /// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a /// string. -fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> { +crate fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> { use std::fmt::Write; let trait_ref = tcx.impl_trait_ref(impl_def_id)?;
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index ac8bab0..3d71382 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -17,6 +17,7 @@ Dynamic, Foreign, Opaque, + Closure, Generator, Projection, } @@ -102,7 +103,7 @@ // // 2. We are sometimes doing future-incompatibility lints for // now, so we do not want unconditional errors here. - fulfillment_cx.select_all_or_error(infcx).is_ok() + fulfillment_cx.select_all_or_error(infcx).is_empty() } /// This implements the traversal over the structure of a given type to try to @@ -154,6 +155,9 @@ ty::Projection(..) => { return ControlFlow::Break(NonStructuralMatchTy::Projection); } + ty::Closure(..) => { + return ControlFlow::Break(NonStructuralMatchTy::Closure); + } ty::Generator(..) | ty::GeneratorWitness(..) => { return ControlFlow::Break(NonStructuralMatchTy::Generator); } @@ -197,7 +201,7 @@ // First check all contained types and then tell the caller to continue searching. return ty.super_visit_with(self); } - ty::Closure(..) | ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { + ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { bug!("unexpected type during structural-match checking: {:?}", ty); } ty::Error(_) => {
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index ed49abb..6d2323a 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -9,7 +9,9 @@ use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; -pub use rustc_infer::traits::util::*; +pub use rustc_infer::traits::{self, util::*}; + +use std::iter; /////////////////////////////////////////////////////////////////////////// // `TraitAliasExpander` iterator @@ -229,11 +231,16 @@ ) -> impl Iterator<Item = PredicateObligation<'tcx>> { debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds); - generic_bounds.predicates.into_iter().map(move |predicate| Obligation { - cause: cause.clone(), - recursion_depth, - param_env, - predicate, + iter::zip(generic_bounds.predicates, generic_bounds.spans).map(move |(predicate, span)| { + let cause = match cause.code { + traits::ItemObligation(def_id) if !span.is_dummy() => traits::ObligationCause::new( + cause.span, + cause.body_id, + traits::BindingObligation(def_id, span), + ), + _ => cause.clone(), + }; + Obligation { cause, recursion_depth, param_env, predicate } }) }
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index cb47ba9..2a66684 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -709,7 +709,12 @@ iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev()) .map(|((pred, span), origin_def_id)| { - let cause = self.cause(traits::BindingObligation(origin_def_id, span)); + let code = if span.is_dummy() { + traits::MiscObligation + } else { + traits::BindingObligation(origin_def_id, span) + }; + let cause = self.cause(code); traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred) }) .filter(|pred| !pred.has_escaping_bound_vars())
diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 37e0073..92f2760 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs
@@ -128,9 +128,9 @@ // Ensure that those obligations that we had to solve // get solved *here*. - match fulfill_cx.select_all_or_error(infcx) { - Ok(()) => Ok(implied_bounds), - Err(_) => Err(NoSolution), + match fulfill_cx.select_all_or_error(infcx).as_slice() { + [] => Ok(implied_bounds), + _ => Err(NoSolution), } }
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 87b729f..13ffb2a 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -362,7 +362,7 @@ let is_copy = self_ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env); match self_ty.kind() { _ if is_copy => (), - ty::Array(..) | ty::Closure(..) | ty::Tuple(..) => {} + ty::Closure(..) | ty::Tuple(..) => {} _ => return Ok(None), };
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 3f66e5b..595b623 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -17,7 +17,8 @@ // needs drop. let adt_has_dtor = |adt_def: &ty::AdtDef| adt_def.destructor(tcx).map(|_| DtorType::Significant); - let res = drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor).next().is_some(); + let res = + drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor, false).next().is_some(); debug!("needs_drop_raw({:?}) = {:?}", query, res); res @@ -27,10 +28,15 @@ tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, ) -> bool { - let res = - drop_tys_helper(tcx, query.value, query.param_env, adt_consider_insignificant_dtor(tcx)) - .next() - .is_some(); + let res = drop_tys_helper( + tcx, + query.value, + query.param_env, + adt_consider_insignificant_dtor(tcx), + true, + ) + .next() + .is_some(); debug!("has_significant_drop_raw({:?}) = {:?}", query, res); res } @@ -141,9 +147,9 @@ Ok(tys) => tys, }; for required_ty in tys { - let subst_ty = + let required = tcx.normalize_erasing_regions(self.param_env, required_ty); - queue_type(self, subst_ty); + queue_type(self, required); } } ty::Array(..) | ty::Opaque(..) | ty::Projection(..) | ty::Param(_) => { @@ -186,16 +192,39 @@ ty: Ty<'tcx>, param_env: rustc_middle::ty::ParamEnv<'tcx>, adt_has_dtor: impl Fn(&ty::AdtDef) -> Option<DtorType>, + only_significant: bool, ) -> impl Iterator<Item = NeedsDropResult<Ty<'tcx>>> { + fn with_query_cache<'tcx>( + tcx: TyCtxt<'tcx>, + iter: impl IntoIterator<Item = Ty<'tcx>>, + only_significant: bool, + ) -> NeedsDropResult<Vec<Ty<'tcx>>> { + iter.into_iter().try_fold(Vec::new(), |mut vec, subty| { + match subty.kind() { + ty::Adt(adt_id, subst) => { + for subty in if only_significant { + tcx.adt_significant_drop_tys(adt_id.did)? + } else { + tcx.adt_drop_tys(adt_id.did)? + } { + vec.push(subty.subst(tcx, subst)); + } + } + _ => vec.push(subty), + }; + Ok(vec) + }) + } + let adt_components = move |adt_def: &ty::AdtDef, substs: SubstsRef<'tcx>| { if adt_def.is_manually_drop() { debug!("drop_tys_helper: `{:?}` is manually drop", adt_def); - return Ok(Vec::new().into_iter()); + Ok(Vec::new()) } else if let Some(dtor_info) = adt_has_dtor(adt_def) { match dtor_info { DtorType::Significant => { debug!("drop_tys_helper: `{:?}` implements `Drop`", adt_def); - return Err(AlwaysRequiresDrop); + Err(AlwaysRequiresDrop) } DtorType::Insignificant => { debug!("drop_tys_helper: `{:?}` drop is insignificant", adt_def); @@ -203,22 +232,27 @@ // Since the destructor is insignificant, we just want to make sure all of // the passed in type parameters are also insignificant. // Eg: Vec<T> dtor is insignificant when T=i32 but significant when T=Mutex. - return Ok(substs.types().collect::<Vec<Ty<'_>>>().into_iter()); + with_query_cache(tcx, substs.types(), only_significant) } } } else if adt_def.is_union() { debug!("drop_tys_helper: `{:?}` is a union", adt_def); - return Ok(Vec::new().into_iter()); + Ok(Vec::new()) + } else { + with_query_cache( + tcx, + adt_def.all_fields().map(|field| { + let r = tcx.type_of(field.did).subst(tcx, substs); + debug!( + "drop_tys_helper: Subst into {:?} with {:?} gettng {:?}", + field, substs, r + ); + r + }), + only_significant, + ) } - Ok(adt_def - .all_fields() - .map(|field| { - let r = tcx.type_of(field.did).subst(tcx, substs); - debug!("drop_tys_helper: Subst into {:?} with {:?} gettng {:?}", field, substs, r); - r - }) - .collect::<Vec<_>>() - .into_iter()) + .map(|v| v.into_iter()) }; NeedsDropTypes::new(tcx, param_env, ty, adt_components) @@ -252,20 +286,24 @@ // significant. let adt_has_dtor = |adt_def: &ty::AdtDef| adt_def.destructor(tcx).map(|_| DtorType::Significant); - drop_tys_helper(tcx, tcx.type_of(def_id), tcx.param_env(def_id), adt_has_dtor) + // `tcx.type_of(def_id)` identical to `tcx.make_adt(def, identity_substs)` + drop_tys_helper(tcx, tcx.type_of(def_id), tcx.param_env(def_id), adt_has_dtor, false) .collect::<Result<Vec<_>, _>>() .map(|components| tcx.intern_type_list(&components)) } - +// If `def_id` refers to a generic ADT, the queries above and below act as if they had been handed +// a `tcx.make_ty(def, identity_substs)` and as such it is legal to substitue the generic parameters +// of the ADT into the outputted `ty`s. fn adt_significant_drop_tys( tcx: TyCtxt<'_>, def_id: DefId, ) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> { drop_tys_helper( tcx, - tcx.type_of(def_id), + tcx.type_of(def_id), // identical to `tcx.make_adt(def, identity_substs)` tcx.param_env(def_id), adt_consider_insignificant_dtor(tcx), + true, ) .collect::<Result<Vec<_>, _>>() .map(|components| tcx.intern_type_list(&components))
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 7512403..af3706f 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -1,7 +1,6 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::hir::map as hir_map; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{ self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt, WithConstness, @@ -490,11 +489,11 @@ let node = tcx.hir().get(hir_id); - let fn_like = hir_map::blocks::FnLikeNode::from_node(node).unwrap_or_else(|| { + let fn_kind = node.fn_kind().unwrap_or_else(|| { bug!("asyncness: expected fn-like node but got `{:?}`", def_id); }); - fn_like.asyncness() + fn_kind.asyncness() } /// Don't call this directly: use ``tcx.conservative_is_privately_uninhabited`` instead.
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 91dbbec..f11c93e 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs
@@ -559,6 +559,7 @@ impl<CTX> HashStable<CTX> for InferTy { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { use InferTy::*; + discriminant(self).hash_stable(ctx, hasher); match self { TyVar(v) => v.as_u32().hash_stable(ctx, hasher), IntVar(v) => v.index.hash_stable(ctx, hasher),
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 5befe44..e8bd038 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs
@@ -344,7 +344,7 @@ "reorder the arguments: {}: `<{}>`", param_types_present .into_iter() - .map(|ord| format!("{}s", ord.to_string())) + .map(|ord| format!("{}s", ord)) .collect::<Vec<String>>() .join(", then "), ordered_params @@ -464,16 +464,7 @@ .params .iter() .filter(|param| { - matches!( - param.kind, - ty::GenericParamDefKind::Type { - synthetic: Some( - hir::SyntheticTyParamKind::ImplTrait - | hir::SyntheticTyParamKind::FromAttr - ), - .. - } - ) + matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. }) }) .count() } else { @@ -672,6 +663,17 @@ err.span_label(span, "explicit generic argument not allowed"); } + err.note( + "see issue #83701 <https://github.com/rust-lang/rust/issues/83701> \ + for more information", + ); + if tcx.sess.is_nightly_build() { + err.help( + "add `#![feature(explicit_generic_args_with_impl_trait)]` \ + to the crate attributes to enable", + ); + } + err.emit(); }
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 889b687..da751f2 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -1916,9 +1916,7 @@ debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id); - let self_ty = if let Some(ty) = opt_self_ty { - ty - } else { + let Some(self_ty) = opt_self_ty else { let path_str = tcx.def_path_str(trait_def_id); let def_id = self.item_def_id();
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 6a231e7..a816031 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -6,7 +6,6 @@ use rustc_infer::traits::Obligation; use rustc_middle::ty::{self, ToPredicate, Ty, TyS}; use rustc_span::{MultiSpan, Span}; -use rustc_trait_selection::opaque_types::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, @@ -531,6 +530,7 @@ substs: self.infcx.tcx.mk_substs_trait(outer_ty, &[]), }, constness: t.constness, + polarity: t.polarity, })); let obl = Obligation::new( o.cause.clone(),
diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 06c4209..635ed93 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs
@@ -300,7 +300,7 @@ let end = callee_span.shrink_to_hi(); err.multipart_suggestion( "if you meant to create this closure and immediately call it, surround the \ - closure with parenthesis", + closure with parentheses", vec![(start, "(".to_string()), (end, ")".to_string())], Applicability::MaybeIncorrect, ); @@ -349,9 +349,12 @@ ty::FnPtr(sig) => (sig, None), ref t => { let mut unit_variant = None; + let mut removal_span = call_expr.span; if let ty::Adt(adt_def, ..) = t { if adt_def.is_enum() { if let hir::ExprKind::Call(expr, _) = call_expr.kind { + removal_span = + expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); unit_variant = self.tcx.sess.source_map().span_to_snippet(expr.span).ok(); } @@ -379,14 +382,13 @@ ); if let Some(ref path) = unit_variant { - err.span_suggestion( - call_expr.span, + err.span_suggestion_verbose( + removal_span, &format!( - "`{}` is a unit variant, you need to write it \ - without the parenthesis", + "`{}` is a unit variant, you need to write it without the parentheses", path ), - path.to_string(), + String::new(), Applicability::MachineApplicable, ); }
diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 78849b2..511a2d7 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs
@@ -436,11 +436,8 @@ // Very crude check to see whether the expression must be wrapped // in parentheses for the suggestion to work (issue #89497). // Can/should be extended in the future. - let needs_parens = !has_parens - && match self.expr.kind { - hir::ExprKind::Cast(..) => true, - _ => false, - }; + let needs_parens = + !has_parens && matches!(self.expr.kind, hir::ExprKind::Cast(..)); let mut suggestion = vec![(self.expr.span.shrink_to_lo(), sugg)]; if needs_parens {
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 6631621..bb1d974 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs
@@ -21,7 +21,6 @@ use rustc_span::symbol::sym; use rustc_span::{self, MultiSpan, Span}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::opaque_types::InferCtxtExt as _; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_ty_utils::representability::{self, Representability}; @@ -372,16 +371,26 @@ let param_env = tcx.param_env(item_def_id); for field in fields { let field_ty = field.ty(tcx, substs); - // We are currently checking the type this field came from, so it must be local. - let field_span = tcx.hir().span_if_local(field.did).unwrap(); if field_ty.needs_drop(tcx, param_env) { + let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { + // We are currently checking the type this field came from, so it must be local. + Some(Node::Field(field)) => (field.span, field.ty.span), + _ => unreachable!("mir field has to correspond to hir field"), + }; struct_span_err!( tcx.sess, field_span, E0740, "unions may not contain fields that need dropping" ) - .span_note(field_span, "`std::mem::ManuallyDrop` can be used to wrap the type") + .multipart_suggestion_verbose( + "wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped", + vec![ + (ty_span.shrink_to_lo(), format!("std::mem::ManuallyDrop<")), + (ty_span.shrink_to_hi(), ">".into()), + ], + Applicability::MaybeIncorrect, + ) .emit(); return false; } @@ -664,8 +673,9 @@ // Check that all obligations are satisfied by the implementation's // version. - if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) { - infcx.report_fulfillment_errors(errors, None, false); + let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx); + if !errors.is_empty() { + infcx.report_fulfillment_errors(&errors, None, false); } // Finally, resolve all regions. This catches wily misuses of
diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 410ac24..4a41552 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs
@@ -92,7 +92,7 @@ let parent_substs = InternalSubsts::identity_for_item( self.tcx, - self.tcx.closure_base_def_id(expr_def_id.to_def_id()), + self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), ); let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin { @@ -257,8 +257,7 @@ if is_gen { // Check that we deduce the signature from the `<_ as std::ops::Generator>::Return` // associated item and not yield. - let return_assoc_item = - self.tcx.associated_items(gen_trait).in_definition_order().nth(1).unwrap().def_id; + let return_assoc_item = self.tcx.associated_item_def_ids(gen_trait)[1]; if return_assoc_item != projection.projection_def_id() { debug!("deduce_sig_from_projection: not return assoc item of generator"); return None; @@ -694,8 +693,7 @@ // The `Future` trait has only one associted item, `Output`, // so check that this is what we see. - let output_assoc_item = - self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id; + let output_assoc_item = self.tcx.associated_item_def_ids(future_trait)[0]; if output_assoc_item != predicate.projection_ty.item_def_id { span_bug!( cause_span,
diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index a87318f..77f7ccc 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs
@@ -185,9 +185,10 @@ debug!("coerce: unsize not object safe"); return Err(TypeError::ObjectUnsafeCoercion(did)); } - Err(_) => {} + Err(error) => { + debug!(?error, "coerce: unsize failed"); + } } - debug!("coerce: unsize failed"); // Examine the supertype and consider auto-borrowing. match *b.kind() { @@ -521,9 +522,7 @@ let traits = (self.tcx.lang_items().unsize_trait(), self.tcx.lang_items().coerce_unsized_trait()); - let (unsize_did, coerce_unsized_did) = if let (Some(u), Some(cu)) = traits { - (u, cu) - } else { + let (Some(unsize_did), Some(coerce_unsized_did)) = traits else { debug!("missing Unsize or CoerceUnsized traits"); return Err(TypeError::Mismatch); }; @@ -951,7 +950,7 @@ }; let mut fcx = traits::FulfillmentContext::new_in_snapshot(); fcx.register_predicate_obligations(self, ok.obligations); - fcx.select_where_possible(&self).is_ok() + fcx.select_where_possible(&self).is_empty() }) } @@ -1459,7 +1458,7 @@ cause, expected, found, - coercion_error, + coercion_error.clone(), fcx, parent_id, expression.map(|expr| (expr, blk_id)), @@ -1473,7 +1472,7 @@ cause, expected, found, - coercion_error, + coercion_error.clone(), fcx, id, None, @@ -1484,7 +1483,12 @@ } } _ => { - err = fcx.report_mismatched_types(cause, expected, found, coercion_error); + err = fcx.report_mismatched_types( + cause, + expected, + found, + coercion_error.clone(), + ); } } @@ -1493,7 +1497,14 @@ } if let Some(expr) = expression { - fcx.emit_coerce_suggestions(&mut err, expr, found, expected, None); + fcx.emit_coerce_suggestions( + &mut err, + expr, + found, + expected, + None, + coercion_error, + ); } err.emit_unless(unsized_return);
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 5e12497..fdc5d16 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -92,7 +92,6 @@ impl_m_span, impl_m_hir_id, ObligationCauseCode::CompareImplMethodObligation { - item_name: impl_m.ident.name, impl_item_def_id: impl_m.def_id, trait_item_def_id: trait_m.def_id, }, @@ -211,12 +210,8 @@ let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_hir_id); let param_env = ty::ParamEnv::new(tcx.intern_predicates(&hybrid_preds.predicates), Reveal::UserFacing); - let param_env = traits::normalize_param_env_or_error( - tcx, - impl_m.def_id, - param_env, - normalize_cause.clone(), - ); + let param_env = + traits::normalize_param_env_or_error(tcx, impl_m.def_id, param_env, normalize_cause); tcx.infer_ctxt().enter(|infcx| { let inh = Inherited::new(infcx, impl_m.def_id.expect_local()); @@ -227,12 +222,15 @@ let mut selcx = traits::SelectionContext::new(&infcx); let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs); - for predicate in impl_m_own_bounds.predicates { + for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) { + let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id); let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, normalize_cause.clone(), predicate); + traits::normalize(&mut selcx, param_env, normalize_cause, predicate); inh.register_predicates(obligations); - inh.register_predicate(traits::Obligation::new(cause.clone(), param_env, predicate)); + let mut cause = cause.clone(); + cause.make_mut().span = span; + inh.register_predicate(traits::Obligation::new(cause, param_env, predicate)); } // We now need to check that the signature of the impl method is @@ -276,6 +274,12 @@ let sub_result = infcx.at(&cause, param_env).sup(trait_fty, impl_fty).map( |InferOk { obligations, .. }| { + // FIXME: We'd want to keep more accurate spans than "the method signature" when + // processing the comparison between the trait and impl fn, but we sadly lose them + // and point at the whole signature when a trait bound or specific input or output + // type would be more appropriate. In other places we have a `Vec<Span>` + // corresponding to their `Vec<Predicate>`, but we don't have that here. + // Fixing this would improve the output of test `issue-83765.rs`. inh.register_predicates(obligations); }, ); @@ -386,8 +390,9 @@ // Check that all obligations are satisfied by the implementation's // version. - if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) { - infcx.report_fulfillment_errors(errors, None, false); + let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx); + if !errors.is_empty() { + infcx.report_fulfillment_errors(&errors, None, false); return Err(ErrorReported); } @@ -448,6 +453,7 @@ Ok(()) } +#[instrument(level = "debug", skip(infcx))] fn extract_spans_for_error_reporting<'a, 'tcx>( infcx: &infer::InferCtxt<'a, 'tcx>, terr: &TypeError<'_>, @@ -601,10 +607,7 @@ .params .iter() .filter_map(|p| match p.kind { - GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } => Some(p.span), + GenericParamKind::Type { synthetic: true, .. } => Some(p.span), _ => None, }) .collect(); @@ -621,10 +624,7 @@ .params .iter() .filter_map(|p| match p.kind { - GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } => Some(p.span), + GenericParamKind::Type { synthetic: true, .. } => Some(p.span), _ => None, }) .collect(); @@ -817,7 +817,7 @@ match (impl_synthetic, trait_synthetic) { // The case where the impl method uses `impl Trait` but the trait method uses // explicit generics - (Some(hir::SyntheticTyParamKind::ImplTrait), None) => { + (true, false) => { err.span_label(impl_span, "expected generic parameter, found `impl Trait`"); (|| { // try taking the name from the trait impl @@ -858,7 +858,7 @@ } // The case where the trait method uses `impl Trait`, but the impl method uses // explicit generics. - (None, Some(hir::SyntheticTyParamKind::ImplTrait)) => { + (false, true) => { err.span_label(impl_span, "expected `impl Trait`, found generic parameter"); (|| { let impl_m = impl_m.def_id.as_local()?; @@ -1088,8 +1088,9 @@ // Check that all obligations are satisfied by the implementation's // version. - if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) { - infcx.report_fulfillment_errors(errors, None, false); + let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx); + if !errors.is_empty() { + infcx.report_fulfillment_errors(&errors, None, false); return; } @@ -1159,7 +1160,6 @@ impl_ty_span, impl_ty_hir_id, ObligationCauseCode::CompareImplTypeObligation { - item_name: impl_ty.ident.name, impl_item_def_id: impl_ty.def_id, trait_item_def_id: trait_ty.def_id, }, @@ -1204,8 +1204,9 @@ // Check that all obligations are satisfied by the implementation's // version. - if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) { - infcx.report_fulfillment_errors(errors, None, false); + let errors = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx); + if !errors.is_empty() { + infcx.report_fulfillment_errors(&errors, None, false); return Err(ErrorReported); } @@ -1384,12 +1385,13 @@ let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id); - let mk_cause = |span| { - ObligationCause::new( - impl_ty_span, - impl_ty_hir_id, - ObligationCauseCode::BindingObligation(trait_ty.def_id, span), - ) + let mk_cause = |span: Span| { + let code = if span.is_dummy() { + traits::MiscObligation + } else { + traits::BindingObligation(trait_ty.def_id, span) + }; + ObligationCause::new(impl_ty_span, impl_ty_hir_id, code) }; let obligations = tcx @@ -1421,10 +1423,10 @@ // Check that all obligations are satisfied by the implementation's // version. - if let Err(ref errors) = - inh.fulfillment_cx.borrow_mut().select_all_with_constness_or_error(&infcx, constness) - { - infcx.report_fulfillment_errors(errors, None, false); + let errors = + inh.fulfillment_cx.borrow_mut().select_all_with_constness_or_error(&infcx, constness); + if !errors.is_empty() { + infcx.report_fulfillment_errors(&errors, None, false); return Err(ErrorReported); }
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 5403659..12cd7ad 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -10,6 +10,7 @@ use rustc_hir::{is_range_literal, Node}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::adjustment::AllowTwoPhase; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut}; use rustc_span::symbol::sym; @@ -27,8 +28,10 @@ expr_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + error: TypeError<'tcx>, ) { - self.annotate_expected_due_to_let_ty(err, expr); + self.annotate_expected_due_to_let_ty(err, expr, error); + self.suggest_box_deref(err, expr, expected, expr_ty); self.suggest_compatible_variants(err, expr, expected, expr_ty); self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { @@ -144,9 +147,9 @@ let expr = expr.peel_drop_temps(); let cause = self.misc(expr.span); let expr_ty = self.resolve_vars_with_obligations(checked_ty); - let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); + let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone()); - self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr); + self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr, e); (expected, Some(err)) } @@ -155,15 +158,121 @@ &self, err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>, + error: TypeError<'_>, ) { let parent = self.tcx.hir().get_parent_node(expr.hir_id); - if let Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })) = - self.tcx.hir().find(parent) - { - if init.hir_id == expr.hir_id { + match (self.tcx.hir().find(parent), error) { + (Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })), _) + if init.hir_id == expr.hir_id => + { // Point at `let` assignment type. err.span_label(ty.span, "expected due to this"); } + ( + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Assign(lhs, rhs, _), .. + })), + TypeError::Sorts(ExpectedFound { expected, .. }), + ) if rhs.hir_id == expr.hir_id && !expected.is_closure() => { + // We ignore closures explicitly because we already point at them elsewhere. + // Point at the assigned-to binding. + let mut primary_span = lhs.span; + let mut secondary_span = lhs.span; + let mut post_message = ""; + match lhs.kind { + hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { + res: + hir::def::Res::Def( + hir::def::DefKind::Static | hir::def::DefKind::Const, + def_id, + ), + .. + }, + )) => { + if let Some(hir::Node::Item(hir::Item { + ident, + kind: hir::ItemKind::Static(ty, ..) | hir::ItemKind::Const(ty, ..), + .. + })) = self.tcx.hir().get_if_local(*def_id) + { + primary_span = ty.span; + secondary_span = ident.span; + post_message = " type"; + } + } + hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: hir::def::Res::Local(hir_id), .. }, + )) => { + if let Some(hir::Node::Binding(pat)) = self.tcx.hir().find(*hir_id) { + let parent = self.tcx.hir().get_parent_node(pat.hir_id); + primary_span = pat.span; + secondary_span = pat.span; + match self.tcx.hir().find(parent) { + Some(hir::Node::Local(hir::Local { ty: Some(ty), .. })) => { + primary_span = ty.span; + post_message = " type"; + } + Some(hir::Node::Local(hir::Local { init: Some(init), .. })) => { + primary_span = init.span; + post_message = " value"; + } + Some(hir::Node::Param(hir::Param { ty_span, .. })) => { + primary_span = *ty_span; + post_message = " parameter type"; + } + _ => {} + } + } + } + _ => {} + } + + if primary_span != secondary_span + && self + .tcx + .sess + .source_map() + .is_multiline(secondary_span.shrink_to_hi().until(primary_span)) + { + // We are pointing at the binding's type or initializer value, but it's pattern + // is in a different line, so we point at both. + err.span_label(secondary_span, "expected due to the type of this binding"); + err.span_label(primary_span, &format!("expected due to this{}", post_message)); + } else if post_message == "" { + // We are pointing at either the assignment lhs or the binding def pattern. + err.span_label(primary_span, "expected due to the type of this binding"); + } else { + // We are pointing at the binding's type or initializer value. + err.span_label(primary_span, &format!("expected due to this{}", post_message)); + } + + if !lhs.is_syntactic_place_expr() { + // We already emitted E0070 "invalid left-hand side of assignment", so we + // silence this. + err.delay_as_bug(); + } + } + _ => {} + } + } + + fn suggest_box_deref( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + expr_ty: Ty<'tcx>, + ) { + if expr_ty.is_box() && expr_ty.boxed_ty() == expected { + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "try dereferencing the `Box`", + "*".to_string(), + Applicability::MachineApplicable, + ); } } @@ -181,7 +290,50 @@ return; } - let mut compatible_variants = expected_adt + // If the expression is of type () and it's the return expression of a block, + // we suggest adding a separate return expression instead. + // (To avoid things like suggesting `Ok(while .. { .. })`.) + if expr_ty.is_unit() { + if let Some(hir::Node::Block(&hir::Block { + span: block_span, expr: Some(e), .. + })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) + { + if e.hir_id == expr.hir_id { + if let Some(span) = expr.span.find_ancestor_inside(block_span) { + let return_suggestions = + if self.tcx.is_diagnostic_item(sym::Result, expected_adt.did) { + vec!["Ok(())".to_string()] + } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did) + { + vec!["None".to_string(), "Some(())".to_string()] + } else { + return; + }; + if let Some(indent) = + self.tcx.sess.source_map().indentation_before(span.shrink_to_lo()) + { + // Add a semicolon, except after `}`. + let semicolon = + match self.tcx.sess.source_map().span_to_snippet(span) { + Ok(s) if s.ends_with('}') => "", + _ => ";", + }; + err.span_suggestions( + span.shrink_to_hi(), + "try adding an expression at the end of the block", + return_suggestions + .into_iter() + .map(|r| format!("{}\n{}{}", semicolon, indent, r)), + Applicability::MaybeIncorrect, + ); + } + return; + } + } + } + } + + let compatible_variants: Vec<String> = expected_adt .variants .iter() .filter(|variant| variant.fields.len() == 1) @@ -202,19 +354,33 @@ None } }) - .peekable(); + .collect(); - if compatible_variants.peek().is_some() { - if let Ok(expr_text) = self.tcx.sess.source_map().span_to_snippet(expr.span) { - let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text)); - let msg = "try using a variant of the expected enum"; - err.span_suggestions( - expr.span, - msg, - suggestions, - Applicability::MaybeIncorrect, - ); - } + if let [variant] = &compatible_variants[..] { + // Just a single matching variant. + err.multipart_suggestion( + &format!("try wrapping the expression in `{}`", variant), + vec![ + (expr.span.shrink_to_lo(), format!("{}(", variant)), + (expr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } else if compatible_variants.len() > 1 { + // More than one matching variant. + err.multipart_suggestions( + &format!( + "try wrapping the expression in a variant of `{}`", + self.tcx.def_path_str(expected_adt.did) + ), + compatible_variants.into_iter().map(|variant| { + vec![ + (expr.span.shrink_to_lo(), format!("{}(", variant)), + (expr.span.shrink_to_hi(), ")".to_string()), + ] + }), + Applicability::MaybeIncorrect, + ); } } } @@ -428,7 +594,7 @@ (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind { if let Ok(src) = sm.span_to_snippet(sp) { - if let Some(_) = replace_prefix(&src, "b\"", "\"") { + if replace_prefix(&src, "b\"", "\"").is_some() { let pos = sp.lo() + BytePos(1); return Some(( sp.with_hi(pos), @@ -444,7 +610,7 @@ (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => { if let hir::ExprKind::Lit(_) = expr.kind { if let Ok(src) = sm.span_to_snippet(sp) { - if let Some(_) = replace_prefix(&src, "\"", "b\"") { + if replace_prefix(&src, "\"", "b\"").is_some() { return Some(( sp.shrink_to_lo(), "consider adding a leading `b`", @@ -743,9 +909,7 @@ return false; } - let src = if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) { - src - } else { + let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) else { return false; };
diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 9e2460b..4b4d293 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs
@@ -113,9 +113,10 @@ } } - if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) { + let errors = fulfillment_cx.select_all_or_error(&infcx); + if !errors.is_empty() { // this could be reached when we get lazy normalization - infcx.report_fulfillment_errors(errors, None, false); + infcx.report_fulfillment_errors(&errors, None, false); return Err(ErrorReported); }
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 2d0a406..eb997b0 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -30,15 +30,17 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty; +use rustc_infer::infer::InferOk; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; +use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts}; +use rustc_middle::ty::relate::expected_found_bool; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::Ty; -use rustc_middle::ty::TypeFoldable; -use rustc_middle::ty::{AdtKind, Visibility}; +use rustc_middle::ty::{self, AdtKind, Ty, TypeFoldable}; +use rustc_session::parse::feature_err; use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; @@ -323,7 +325,9 @@ } ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(args) => self.check_expr_array(args, expected, expr), - ExprKind::ConstBlock(ref anon_const) => self.to_const(anon_const).ty, + ExprKind::ConstBlock(ref anon_const) => { + self.check_expr_const_block(anon_const, expected, expr) + } ExprKind::Repeat(element, ref count) => { self.check_expr_repeat(element, count, expected, expr) } @@ -829,7 +833,7 @@ &self, lhs: &'tcx hir::Expr<'tcx>, err_code: &'static str, - expr_span: &Span, + op_span: Span, ) { if lhs.is_syntactic_place_expr() { return; @@ -837,11 +841,58 @@ // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. let mut err = self.tcx.sess.struct_span_err_with_code( - *expr_span, + op_span, "invalid left-hand side of assignment", DiagnosticId::Error(err_code.into()), ); err.span_label(lhs.span, "cannot assign to this expression"); + + let mut parent = self.tcx.hir().get_parent_node(lhs.hir_id); + while let Some(node) = self.tcx.hir().find(parent) { + match node { + hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::Loop( + hir::Block { + expr: + Some(hir::Expr { + kind: + hir::ExprKind::Match(expr, ..) | hir::ExprKind::If(expr, ..), + .. + }), + .. + }, + _, + hir::LoopSource::While, + _, + ), + .. + }) => { + // We have a situation like `while Some(0) = value.get(0) {`, where `while let` + // was more likely intended. + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to use pattern destructuring", + "let ".to_string(), + Applicability::MachineApplicable, + ); + if !self.sess().features_untracked().destructuring_assignment { + // We already emit an E0658 with a suggestion for `while let`, this is + // redundant output. + err.delay_as_bug(); + } + break; + } + hir::Node::Item(_) + | hir::Node::ImplItem(_) + | hir::Node::TraitItem(_) + | hir::Node::Crate(_) => break, + _ => { + parent = self.tcx.hir().get_parent_node(parent); + } + } + } + err.emit(); } @@ -949,7 +1000,7 @@ } else { (Applicability::MaybeIncorrect, false) }; - if !lhs.is_syntactic_place_expr() { + if !lhs.is_syntactic_place_expr() && !matches!(lhs.kind, hir::ExprKind::Lit(_)) { // Do not suggest `if let x = y` as `==` is way more likely to be the intention. let hir = self.tcx.hir(); if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = @@ -961,7 +1012,7 @@ "let ".to_string(), applicability, ); - } + }; } if eq { err.span_suggestion_verbose( @@ -982,7 +1033,7 @@ return self.tcx.ty_error(); } - self.check_lhs_assignable(lhs, "E0070", span); + self.check_lhs_assignable(lhs, "E0070", *span); let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs)); @@ -1166,6 +1217,24 @@ self.tcx.mk_array(element_ty, args.len() as u64) } + fn check_expr_const_block( + &self, + anon_const: &'tcx hir::AnonConst, + expected: Expectation<'tcx>, + _expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + let body = self.tcx.hir().body(anon_const.body); + + // Create a new function context. + let fcx = FnCtxt::new(self, self.param_env, body.value.hir_id); + crate::check::GatherLocalsVisitor::new(&fcx).visit_body(body); + + let ty = fcx.check_expr_with_expectation(&body.value, expected); + fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized); + fcx.write_ty(anon_const.hir_id, ty); + ty + } + fn check_expr_repeat( &self, element: &'tcx hir::Expr<'tcx>, @@ -1262,49 +1331,17 @@ .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); } - let error_happened = self.check_expr_struct_fields( + self.check_expr_struct_fields( adt_ty, expected, expr.hir_id, qpath.span(), variant, fields, - base_expr.is_none(), + base_expr, expr.span, ); - if let Some(base_expr) = base_expr { - // If check_expr_struct_fields hit an error, do not attempt to populate - // the fields with the base_expr. This could cause us to hit errors later - // when certain fields are assumed to exist that in fact do not. - if !error_happened { - self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {}); - match adt_ty.kind() { - ty::Adt(adt, substs) if adt.is_struct() => { - let fru_field_types = adt - .non_enum_variant() - .fields - .iter() - .map(|f| { - self.normalize_associated_types_in( - expr.span, - f.ty(self.tcx, substs), - ) - }) - .collect(); - self.typeck_results - .borrow_mut() - .fru_field_types_mut() - .insert(expr.hir_id, fru_field_types); - } - _ => { - self.tcx - .sess - .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); - } - } - } - } self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized); adt_ty } @@ -1317,9 +1354,9 @@ span: Span, variant: &'tcx ty::VariantDef, ast_fields: &'tcx [hir::ExprField<'tcx>], - check_completeness: bool, + base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, expr_span: Span, - ) -> bool { + ) { let tcx = self.tcx; let adt_ty_hint = self @@ -1394,7 +1431,116 @@ ) .emit(); } - } else if check_completeness && !error_happened && !remaining_fields.is_empty() { + } + + // If check_expr_struct_fields hit an error, do not attempt to populate + // the fields with the base_expr. This could cause us to hit errors later + // when certain fields are assumed to exist that in fact do not. + if error_happened { + return; + } + + if let Some(base_expr) = base_expr { + // FIXME: We are currently creating two branches here in order to maintain + // consistency. But they should be merged as much as possible. + let fru_tys = if self.tcx.features().type_changing_struct_update { + let base_ty = self.check_expr(base_expr); + match adt_ty.kind() { + ty::Adt(adt, substs) if adt.is_struct() => { + match base_ty.kind() { + ty::Adt(base_adt, base_subs) if adt == base_adt => { + variant + .fields + .iter() + .map(|f| { + let fru_ty = self.normalize_associated_types_in( + expr_span, + self.field_ty(base_expr.span, f, base_subs), + ); + let ident = self.tcx.adjust_ident(f.ident, variant.def_id); + if let Some(_) = remaining_fields.remove(&ident) { + let target_ty = + self.field_ty(base_expr.span, f, substs); + let cause = self.misc(base_expr.span); + match self + .at(&cause, self.param_env) + .sup(target_ty, fru_ty) + { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations) + } + // FIXME: Need better diagnostics for `FieldMisMatch` error + Err(_) => self + .report_mismatched_types( + &cause, + target_ty, + fru_ty, + FieldMisMatch( + variant.ident.name, + ident.name, + ), + ) + .emit(), + } + } + fru_ty + }) + .collect() + } + _ => { + return self + .report_mismatched_types( + &self.misc(base_expr.span), + adt_ty, + base_ty, + Sorts(expected_found_bool(true, adt_ty, base_ty)), + ) + .emit(); + } + } + } + _ => { + return self + .tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); + } + } + } else { + self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { + let base_ty = self.check_expr(base_expr); + let same_adt = match (adt_ty.kind(), base_ty.kind()) { + (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true, + _ => false, + }; + if self.tcx.sess.is_nightly_build() && same_adt { + feature_err( + &self.tcx.sess.parse_sess, + sym::type_changing_struct_update, + base_expr.span, + "type changing struct updating is experimental", + ) + .emit(); + } + }); + match adt_ty.kind() { + ty::Adt(adt, substs) if adt.is_struct() => variant + .fields + .iter() + .map(|f| { + self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs)) + }) + .collect(), + _ => { + return self + .tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); + } + } + }; + self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); + } else if kind_name != "union" && !remaining_fields.is_empty() { let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| { !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) }); @@ -1405,8 +1551,6 @@ self.report_missing_fields(adt_ty, span, remaining_fields); } } - - error_happened } fn check_struct_fields_on_error( @@ -1632,7 +1776,7 @@ .filter_map(|field| { // ignore already set fields and private fields from non-local crates if skip.iter().any(|&x| x == field.ident.name) - || (!variant.def_id.is_local() && field.vis != Visibility::Public) + || (!variant.def_id.is_local() && !field.vis.is_public()) { None } else { @@ -1928,7 +2072,7 @@ fn point_at_param_definition(&self, err: &mut DiagnosticBuilder<'_>, param: ty::ParamTy) { let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); let generic_param = generics.type_param(¶m, self.tcx); - if let ty::GenericParamDefKind::Type { synthetic: Some(..), .. } = generic_param.kind { + if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind { return; } let param_def_id = generic_param.def_id; @@ -2058,8 +2202,7 @@ ) -> Option<(&Vec<ty::FieldDef>, SubstsRef<'tcx>)> { debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t); - let mut autoderef = self.autoderef(span, base_t); - while let Some((base_t, _)) = autoderef.next() { + for (base_t, _) in self.autoderef(span, base_t) { match base_t.kind() { ty::Adt(base_def, substs) if !base_def.is_enum() => { let fields = &base_def.non_enum_variant().fields;
diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs index 296e453..e5da33d 100644 --- a/compiler/rustc_typeck/src/check/fallback.rs +++ b/compiler/rustc_typeck/src/check/fallback.rs
@@ -176,7 +176,7 @@ .type_var_origin(ty) .map(|origin| origin.span) .unwrap_or(rustc_span::DUMMY_SP); - let oty = self.inner.borrow().opaque_types_vars.get(ty).map(|v| *v); + let oty = self.inner.borrow().opaque_types_vars.get(ty).copied(); if let Some(opaque_ty) = oty { debug!( "fallback_opaque_type_vars(ty={:?}): falling back to opaque type {:?}",
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index ac4bb65..142a0a8 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -35,7 +35,6 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{self, BytePos, MultiSpan, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::opaque_types::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt, @@ -587,38 +586,6 @@ } } - /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each - /// type/region parameter was instantiated (`substs`), creates and registers suitable - /// trait/region obligations. - /// - /// For example, if there is a function: - /// - /// ``` - /// fn foo<'a,T:'a>(...) - /// ``` - /// - /// and a reference: - /// - /// ``` - /// let f = foo; - /// ``` - /// - /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a` - /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally. - pub fn add_obligations_for_parameters( - &self, - cause: traits::ObligationCause<'tcx>, - predicates: ty::InstantiatedPredicates<'tcx>, - ) { - assert!(!predicates.has_escaping_bound_vars()); - - debug!("add_obligations_for_parameters(predicates={:?})", predicates); - - for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) { - self.register_predicate(obligation); - } - } - // FIXME(arielb1): use this instead of field.ty everywhere // Only for fields! Returns <none> for methods> // Indifferent to privacy flags @@ -643,11 +610,12 @@ #[instrument(skip(self), level = "debug")] pub(in super::super) fn select_all_obligations_or_error(&self) { - if let Err(errors) = self + let errors = self .fulfillment_cx .borrow_mut() - .select_all_with_constness_or_error(&self, self.inh.constness) - { + .select_all_with_constness_or_error(&self, self.inh.constness); + + if !errors.is_empty() { self.report_fulfillment_errors(&errors, self.inh.body_id, false); } } @@ -658,13 +626,13 @@ fallback_has_occurred: bool, mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>), ) { - let result = self + let mut result = self .fulfillment_cx .borrow_mut() .select_with_constness_where_possible(self, self.inh.constness); - if let Err(mut errors) = result { - mutate_fulfillment_errors(&mut errors); - self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred); + if !result.is_empty() { + mutate_fulfillment_errors(&mut result); + self.report_fulfillment_errors(&result, self.inh.body_id, fallback_has_occurred); } } @@ -794,14 +762,17 @@ // we can. We don't care if some things turn // out unconstrained or ambiguous, as we're // just trying to get hints here. - self.save_and_restore_in_snapshot_flag(|_| { + let errors = self.save_and_restore_in_snapshot_flag(|_| { let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); for obligation in ok.obligations { fulfill.register_predicate_obligation(self, obligation); } fulfill.select_where_possible(self) - }) - .map_err(|_| ())?; + }); + + if !errors.is_empty() { + return Err(()); + } } Err(_) => return Err(()), } @@ -949,7 +920,7 @@ let mut err = rustc_errors::struct_span_err!( self.sess(), self_ty.span, - E0783, + E0782, "{}", msg, ); @@ -1519,20 +1490,14 @@ /// Add all the obligations that are required, substituting and normalized appropriately. #[tracing::instrument(level = "debug", skip(self, span, def_id, substs))] - fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) { - let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs); + crate fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) { + let (bounds, _) = self.instantiate_bounds(span, def_id, &substs); - for (i, mut obligation) in traits::predicates_for_generics( + for obligation in traits::predicates_for_generics( traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)), self.param_env, bounds, - ) - .enumerate() - { - // This makes the error point at the bound, but we want to point at the argument - if let Some(span) = spans.get(i) { - obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span); - } + ) { self.register_predicate(obligation); } }
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 5515223..74d7f0a 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -370,6 +370,8 @@ // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty); + final_arg_types.push((i, checked_ty, coerce_ty)); + // Cause selection errors caused by resolving a single argument to point at the // argument and not the call. This is otherwise redundant with the `demand_coerce` // call immediately after, but it lets us customize the span pointed to in the @@ -377,38 +379,20 @@ let _ = self.resolve_vars_with_obligations_and_mutate_fulfillment( coerce_ty, |errors| { - // This is not coming from a macro or a `derive`. - if sp.desugaring_kind().is_none() - && !arg.span.from_expansion() - // Do not change the spans of `async fn`s. - && !matches!( - expr.kind, - hir::ExprKind::Call( - hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::LangItem(_, _)), - .. - }, - _ - ) - ) { - for error in errors { - error.obligation.cause.make_mut().span = arg.span; - let code = error.obligation.cause.code.clone(); - error.obligation.cause.make_mut().code = - ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id: arg.hir_id, - call_hir_id: expr.hir_id, - parent_code: Lrc::new(code), - }; - } - } + self.point_at_type_arg_instead_of_call_if_possible(errors, expr); + self.point_at_arg_instead_of_call_if_possible( + errors, + &final_arg_types, + expr, + sp, + args, + ); }, ); // We're processing function arguments so we definitely want to use // two-phase borrows. self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes); - final_arg_types.push((i, checked_ty, coerce_ty)); // 3. Relate the expected type and the formal one, // if the expected type was used for the coercion. @@ -525,10 +509,7 @@ self.write_user_type_annotation_from_substs(hir_id, did, substs, None); // Check bounds on type arguments used in the path. - let (bounds, _) = self.instantiate_bounds(path_span, did, substs); - let cause = - traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did)); - self.add_obligations_for_parameters(cause, bounds); + self.add_required_obligations(path_span, did, substs); Some((variant, ty)) } else { @@ -756,6 +737,24 @@ &mut |err| { if let Some(expected_ty) = expected.only_has_type(self) { self.consider_hint_about_removing_semicolon(blk, expected_ty, err); + if expected_ty == self.tcx.types.bool { + // If this is caused by a missing `let` in a `while let`, + // silence this redundant error, as we already emit E0070. + let parent = self.tcx.hir().get_parent_node(blk.hir_id); + let parent = self.tcx.hir().get_parent_node(parent); + let parent = self.tcx.hir().get_parent_node(parent); + let parent = self.tcx.hir().get_parent_node(parent); + let parent = self.tcx.hir().get_parent_node(parent); + match self.tcx.hir().find(parent) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _), + .. + })) => { + err.delay_as_bug(); + } + _ => {} + } + } } if let Some(fn_span) = fn_span { err.span_label( @@ -973,45 +972,79 @@ continue; } - if let ty::PredicateKind::Trait(predicate) = - error.obligation.predicate.kind().skip_binder() - { - // Collect the argument position for all arguments that could have caused this - // `FulfillmentError`. - let mut referenced_in = final_arg_types - .iter() - .map(|&(i, checked_ty, _)| (i, checked_ty)) - .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty))) - .flat_map(|(i, ty)| { - let ty = self.resolve_vars_if_possible(ty); - // We walk the argument type because the argument's type could have - // been `Option<T>`, but the `FulfillmentError` references `T`. - if ty.walk(self.tcx).any(|arg| arg == predicate.self_ty().into()) { - Some(i) - } else { - None - } - }) - .collect::<Vec<usize>>(); + // Peel derived obligation, because it's the type that originally + // started this inference chain that matters, not the one we wound + // up with at the end. + fn unpeel_to_top( + mut code: Lrc<ObligationCauseCode<'_>>, + ) -> Lrc<ObligationCauseCode<'_>> { + let mut result_code = code.clone(); + loop { + let parent = match &*code { + ObligationCauseCode::BuiltinDerivedObligation(c) + | ObligationCauseCode::ImplDerivedObligation(c) + | ObligationCauseCode::DerivedObligation(c) => c.parent_code.clone(), + _ => break, + }; + result_code = std::mem::replace(&mut code, parent); + } + result_code + } + let self_: ty::subst::GenericArg<'_> = match &*unpeel_to_top(Lrc::new(error.obligation.cause.code.clone())) { + ObligationCauseCode::BuiltinDerivedObligation(code) | + ObligationCauseCode::ImplDerivedObligation(code) | + ObligationCauseCode::DerivedObligation(code) => { + code.parent_trait_ref.self_ty().skip_binder().into() + } + _ if let ty::PredicateKind::Trait(predicate) = + error.obligation.predicate.kind().skip_binder() => { + predicate.self_ty().into() + } + _ => continue, + }; + let self_ = self.resolve_vars_if_possible(self_); - // Both checked and coerced types could have matched, thus we need to remove - // duplicates. + // Collect the argument position for all arguments that could have caused this + // `FulfillmentError`. + let mut referenced_in = final_arg_types + .iter() + .map(|&(i, checked_ty, _)| (i, checked_ty)) + .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty))) + .flat_map(|(i, ty)| { + let ty = self.resolve_vars_if_possible(ty); + // We walk the argument type because the argument's type could have + // been `Option<T>`, but the `FulfillmentError` references `T`. + if ty.walk(self.tcx).any(|arg| arg == self_) { Some(i) } else { None } + }) + .collect::<Vec<usize>>(); - // We sort primitive type usize here and can use unstable sort - referenced_in.sort_unstable(); - referenced_in.dedup(); + // Both checked and coerced types could have matched, thus we need to remove + // duplicates. - if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) { - // We make sure that only *one* argument matches the obligation failure - // and we assign the obligation's span to its expression's. - error.obligation.cause.make_mut().span = args[ref_in].span; - let code = error.obligation.cause.code.clone(); - error.obligation.cause.make_mut().code = - ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id: args[ref_in].hir_id, - call_hir_id: expr.hir_id, - parent_code: Lrc::new(code), - }; + // We sort primitive type usize here and can use unstable sort + referenced_in.sort_unstable(); + referenced_in.dedup(); + + if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) { + // Do not point at the inside of a macro. + // That would often result in poor error messages. + if args[ref_in].span.from_expansion() { + return; + } + // We make sure that only *one* argument matches the obligation failure + // and we assign the obligation's span to its expression's. + error.obligation.cause.make_mut().span = args[ref_in].span; + let code = error.obligation.cause.code.clone(); + error.obligation.cause.make_mut().code = + ObligationCauseCode::FunctionArgumentObligation { + arg_hir_id: args[ref_in].hir_id, + call_hir_id: expr.hir_id, + parent_code: Lrc::new(code), + }; + } else if error.obligation.cause.make_mut().span == call_sp { + // Make function calls point at the callee, not the whole thing. + if let hir::ExprKind::Call(callee, _) = expr.kind { + error.obligation.cause.make_mut().span = callee.span; } } } @@ -1027,34 +1060,32 @@ call_expr: &'tcx hir::Expr<'tcx>, ) { if let hir::ExprKind::Call(path, _) = &call_expr.kind { - if let hir::ExprKind::Path(qpath) = &path.kind { - if let hir::QPath::Resolved(_, path) = &qpath { - for error in errors { - if let ty::PredicateKind::Trait(predicate) = - error.obligation.predicate.kind().skip_binder() + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &path.kind { + for error in errors { + if let ty::PredicateKind::Trait(predicate) = + error.obligation.predicate.kind().skip_binder() + { + // If any of the type arguments in this path segment caused the + // `FulfillmentError`, point at its span (#61860). + for arg in path + .segments + .iter() + .filter_map(|seg| seg.args.as_ref()) + .flat_map(|a| a.args.iter()) { - // If any of the type arguments in this path segment caused the - // `FulfillmentError`, point at its span (#61860). - for arg in path - .segments - .iter() - .filter_map(|seg| seg.args.as_ref()) - .flat_map(|a| a.args.iter()) - { - if let hir::GenericArg::Type(hir_ty) = &arg { - if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) = - &hir_ty.kind - { - // Avoid ICE with associated types. As this is best - // effort only, it's ok to ignore the case. It - // would trigger in `is_send::<T::AssocType>();` - // from `typeck-default-trait-impl-assoc-type.rs`. - } else { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, hir_ty); - let ty = self.resolve_vars_if_possible(ty); - if ty == predicate.self_ty() { - error.obligation.cause.make_mut().span = hir_ty.span; - } + if let hir::GenericArg::Type(hir_ty) = &arg { + if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) = + &hir_ty.kind + { + // Avoid ICE with associated types. As this is best + // effort only, it's ok to ignore the case. It + // would trigger in `is_send::<T::AssocType>();` + // from `typeck-default-trait-impl-assoc-type.rs`. + } else { + let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, hir_ty); + let ty = self.resolve_vars_if_possible(ty); + if ty == predicate.self_ty() { + error.obligation.cause.make_mut().span = hir_ty.span; } } }
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index dcc635a..6c7d3a0 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -14,6 +14,7 @@ use rustc_middle::ty::{self, Binder, Ty}; use rustc_span::symbol::{kw, sym}; +use rustc_middle::ty::subst::GenericArgKind; use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -232,48 +233,72 @@ let is_struct_pat_shorthand_field = self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span); let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); - if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { - let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods) - .filter_map(|(receiver, method)| { - let method_call = format!(".{}()", method.ident); - if receiver.ends_with(&method_call) { - None // do not suggest code that is already there (#53348) - } else { - let method_call_list = [".to_vec()", ".to_string()"]; - let mut sugg = if receiver.ends_with(".clone()") - && method_call_list.contains(&method_call.as_str()) - { - let max_len = receiver.rfind('.').unwrap(); - vec![( - expr.span, - format!("{}{}", &receiver[..max_len], method_call), - )] + if !methods.is_empty() { + if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { + let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods) + .filter_map(|(receiver, method)| { + let method_call = format!(".{}()", method.ident); + if receiver.ends_with(&method_call) { + None // do not suggest code that is already there (#53348) } else { - if expr.precedence().order() < ExprPrecedence::MethodCall.order() { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!("){}", method_call)), - ] + let method_call_list = [".to_vec()", ".to_string()"]; + let mut sugg = if receiver.ends_with(".clone()") + && method_call_list.contains(&method_call.as_str()) + { + let max_len = receiver.rfind('.').unwrap(); + vec![( + expr.span, + format!("{}{}", &receiver[..max_len], method_call), + )] } else { - vec![(expr.span.shrink_to_hi(), method_call)] + if expr.precedence().order() + < ExprPrecedence::MethodCall.order() + { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!("){}", method_call)), + ] + } else { + vec![(expr.span.shrink_to_hi(), method_call)] + } + }; + if is_struct_pat_shorthand_field { + sugg.insert( + 0, + (expr.span.shrink_to_lo(), format!("{}: ", receiver)), + ); } - }; - if is_struct_pat_shorthand_field { - sugg.insert( - 0, - (expr.span.shrink_to_lo(), format!("{}: ", receiver)), + Some(sugg) + } + }) + .peekable(); + if suggestions.peek().is_some() { + err.multipart_suggestions( + "try using a conversion method", + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + } else if found.to_string().starts_with("Option<") + && expected.to_string() == "Option<&str>" + { + if let ty::Adt(_def, subst) = found.kind() { + if subst.len() != 0 { + if let GenericArgKind::Type(ty) = subst[0].unpack() { + let peeled = ty.peel_refs().to_string(); + if peeled == "String" { + let ref_cnt = ty.to_string().len() - peeled.len(); + let result = format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)); + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + "try converting the passed type into a `&str`", + result, + Applicability::MaybeIncorrect, ); } - Some(sugg) } - }) - .peekable(); - if suggestions.peek().is_some() { - err.multipart_suggestions( - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); + } } } }
diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index b0cb844..6314f2a 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs
@@ -324,9 +324,10 @@ sym::unlikely => (0, vec![tcx.types.bool], tcx.types.bool), sym::discriminant_value => { - let assoc_items = - tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); - let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; + let assoc_items = tcx.associated_item_def_ids( + tcx.require_lang_item(hir::LangItem::DiscriminantKind, None), + ); + let discriminant_def_id = assoc_items[0]; let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }; (
diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index dc54f63f..dabfe92 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs
@@ -120,7 +120,12 @@ // We won't add these if we encountered an illegal sized bound, so that we can use // a custom error in that case. if illegal_sized_bound.is_none() { - self.add_obligations(self.tcx.mk_fn_ptr(method_sig), all_substs, method_predicates); + self.add_obligations( + self.tcx.mk_fn_ptr(method_sig), + all_substs, + method_predicates, + pick.item.def_id, + ); } // Create the final `MethodCallee`. @@ -162,7 +167,7 @@ match &pick.autoref_or_ptr_adjustment { Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { - let region = self.next_region_var(infer::Autoref(self.span, pick.item)); + let region = self.next_region_var(infer::Autoref(self.span)); target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl: *mutbl, ty: target }); let mutbl = match mutbl { hir::Mutability::Not => AutoBorrowMutability::Not, @@ -471,16 +476,23 @@ fty: Ty<'tcx>, all_substs: SubstsRef<'tcx>, method_predicates: ty::InstantiatedPredicates<'tcx>, + def_id: DefId, ) { debug!( - "add_obligations: fty={:?} all_substs={:?} method_predicates={:?}", - fty, all_substs, method_predicates + "add_obligations: fty={:?} all_substs={:?} method_predicates={:?} def_id={:?}", + fty, all_substs, method_predicates, def_id ); - self.add_obligations_for_parameters( - traits::ObligationCause::misc(self.span, self.body_id), + // FIXME: could replace with the following, but we already calculated `method_predicates`, + // so we just call `predicates_for_generics` directly to avoid redoing work. + // `self.add_required_obligations(self.span, def_id, &all_substs);` + for obligation in traits::predicates_for_generics( + traits::ObligationCause::new(self.span, self.body_id, traits::ItemObligation(def_id)), + self.param_env, method_predicates, - ); + ) { + self.register_predicate(obligation); + } // this is a projection from a trait reference, so we have to // make sure that the trait reference inputs are well-formed.
diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index f0f2470..dbc1d4e 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs
@@ -12,6 +12,7 @@ pub use self::MethodError::*; use crate::check::FnCtxt; +use crate::ObligationCause; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; @@ -71,7 +72,8 @@ #[derive(Debug)] pub struct NoMatchData<'tcx> { pub static_candidates: Vec<CandidateSource>, - pub unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>, + pub unsatisfied_predicates: + Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, pub out_of_scope_traits: Vec<DefId>, pub lev_candidate: Option<ty::AssocItem>, pub mode: probe::Mode, @@ -80,7 +82,11 @@ impl<'tcx> NoMatchData<'tcx> { pub fn new( static_candidates: Vec<CandidateSource>, - unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>, + unsatisfied_predicates: Vec<( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )>, out_of_scope_traits: Vec<DefId>, lev_candidate: Option<ty::AssocItem>, mode: probe::Mode,
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 6eeb28e..9fd7e8c 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs
@@ -78,7 +78,8 @@ /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used /// for error reporting - unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>, + unsatisfied_predicates: + Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, is_suggestion: IsSuggestion, @@ -92,7 +93,7 @@ } } -#[derive(Debug)] +#[derive(Debug, Clone)] struct Candidate<'tcx> { // Candidates are (I'm not quite sure, but they are mostly) basically // some metadata on top of a `ty::AssocItem` (without substs). @@ -132,7 +133,7 @@ import_ids: SmallVec<[LocalDefId; 1]>, } -#[derive(Debug)] +#[derive(Debug, Clone)] enum CandidateKind<'tcx> { InherentImplCandidate( SubstsRef<'tcx>, @@ -204,6 +205,7 @@ /// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is /// `*mut T`, convert it to `*const T`. pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment<'tcx>>, + pub self_ty: Ty<'tcx>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -1101,13 +1103,37 @@ } fn pick_core(&mut self) -> Option<PickResult<'tcx>> { - let steps = self.steps.clone(); + let mut unstable_candidates = Vec::new(); + let pick = self.pick_all_method(Some(&mut unstable_candidates)); - // find the first step that works + // In this case unstable picking is done by `pick_method`. + if !self.tcx.sess.opts.debugging_opts.pick_stable_methods_before_any_unstable { + return pick; + } + + match pick { + // Emit a lint if there are unstable candidates alongside the stable ones. + // + // We suppress warning if we're picking the method only because it is a + // suggestion. + Some(Ok(ref p)) if !self.is_suggestion.0 && !unstable_candidates.is_empty() => { + self.emit_unstable_name_collision_hint(p, &unstable_candidates); + pick + } + Some(_) => pick, + None => self.pick_all_method(None), + } + } + + fn pick_all_method( + &mut self, + mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + ) -> Option<PickResult<'tcx>> { + let steps = self.steps.clone(); steps .iter() .filter(|step| { - debug!("pick_core: step={:?}", step); + debug!("pick_all_method: step={:?}", step); // skip types that are from a type error or that would require dereferencing // a raw pointer !step.self_ty.references_error() && !step.from_unsafe_deref @@ -1123,11 +1149,30 @@ .unwrap_or_else(|_| { span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty) }); - self.pick_by_value_method(step, self_ty).or_else(|| { - self.pick_autorefd_method(step, self_ty, hir::Mutability::Not) - .or_else(|| self.pick_autorefd_method(step, self_ty, hir::Mutability::Mut)) - .or_else(|| self.pick_const_ptr_method(step, self_ty)) - }) + self.pick_by_value_method(step, self_ty, unstable_candidates.as_deref_mut()) + .or_else(|| { + self.pick_autorefd_method( + step, + self_ty, + hir::Mutability::Not, + unstable_candidates.as_deref_mut(), + ) + .or_else(|| { + self.pick_autorefd_method( + step, + self_ty, + hir::Mutability::Mut, + unstable_candidates.as_deref_mut(), + ) + }) + .or_else(|| { + self.pick_const_ptr_method( + step, + self_ty, + unstable_candidates.as_deref_mut(), + ) + }) + }) }) .next() } @@ -1142,12 +1187,13 @@ &mut self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> { if step.unsize { return None; } - self.pick_method(self_ty).map(|r| { + self.pick_method(self_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; @@ -1170,6 +1216,7 @@ step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, mutbl: hir::Mutability, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> { let tcx = self.tcx; @@ -1177,7 +1224,7 @@ let region = tcx.lifetimes.re_erased; let autoref_ty = tcx.mk_ref(region, ty::TypeAndMut { ty: self_ty, mutbl }); - self.pick_method(autoref_ty).map(|r| { + self.pick_method(autoref_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { @@ -1196,6 +1243,7 @@ &mut self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> { // Don't convert an unsized reference to ptr if step.unsize { @@ -1209,7 +1257,7 @@ let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }; let const_ptr_ty = self.tcx.mk_ptr(const_self_ty); - self.pick_method(const_ptr_ty).map(|r| { + self.pick_method(const_ptr_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); @@ -1218,8 +1266,8 @@ }) } - fn pick_method(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> { - debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); + fn pick_method_with_unstable(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> { + debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty)); let mut possibly_unsatisfied_predicates = Vec::new(); let mut unstable_candidates = Vec::new(); @@ -1241,7 +1289,7 @@ // // We suppress warning if we're picking the method only because it is a // suggestion. - self.emit_unstable_name_collision_hint(p, &unstable_candidates, self_ty); + self.emit_unstable_name_collision_hint(p, &unstable_candidates); } } return Some(pick); @@ -1251,7 +1299,7 @@ debug!("searching unstable candidates"); let res = self.consider_candidates( self_ty, - unstable_candidates.into_iter().map(|(c, _)| c), + unstable_candidates.iter().map(|(c, _)| c), &mut possibly_unsatisfied_predicates, None, ); @@ -1261,6 +1309,42 @@ res } + fn pick_method( + &mut self, + self_ty: Ty<'tcx>, + mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + ) -> Option<PickResult<'tcx>> { + if !self.tcx.sess.opts.debugging_opts.pick_stable_methods_before_any_unstable { + return self.pick_method_with_unstable(self_ty); + } + + debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); + + let mut possibly_unsatisfied_predicates = Vec::new(); + + for (kind, candidates) in + &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] + { + debug!("searching {} candidates", kind); + let res = self.consider_candidates( + self_ty, + candidates.iter(), + &mut possibly_unsatisfied_predicates, + unstable_candidates.as_deref_mut(), + ); + if let Some(pick) = res { + return Some(pick); + } + } + + // `pick_method` may be called twice for the same self_ty if no stable methods + // match. Only extend once. + if unstable_candidates.is_some() { + self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates); + } + None + } + fn consider_candidates<'b, ProbesIter>( &self, self_ty: Ty<'tcx>, @@ -1268,11 +1352,13 @@ possibly_unsatisfied_predicates: &mut Vec<( ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, )>, - unstable_candidates: Option<&mut Vec<(&'b Candidate<'tcx>, Symbol)>>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> where ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone, + 'tcx: 'b, { let mut applicable_candidates: Vec<_> = probes .clone() @@ -1285,7 +1371,9 @@ debug!("applicable_candidates: {:?}", applicable_candidates); if applicable_candidates.len() > 1 { - if let Some(pick) = self.collapse_candidates_to_trait_pick(&applicable_candidates[..]) { + if let Some(pick) = + self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates[..]) + { return Some(Ok(pick)); } } @@ -1295,7 +1383,7 @@ if let stability::EvalResult::Deny { feature, .. } = self.tcx.eval_stability(p.item.def_id, None, self.span, None) { - uc.push((p, feature)); + uc.push((p.clone(), feature)); return false; } true @@ -1309,7 +1397,7 @@ applicable_candidates.pop().map(|(probe, status)| { if status == ProbeResult::Match { - Ok(probe.to_unadjusted_pick()) + Ok(probe.to_unadjusted_pick(self_ty)) } else { Err(MethodError::BadReturnType) } @@ -1319,8 +1407,7 @@ fn emit_unstable_name_collision_hint( &self, stable_pick: &Pick<'_>, - unstable_candidates: &[(&Candidate<'tcx>, Symbol)], - self_ty: Ty<'tcx>, + unstable_candidates: &[(Candidate<'tcx>, Symbol)], ) { self.tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, @@ -1351,7 +1438,7 @@ "use the fully qualified path to the associated const", format!( "<{} as {}>::{}", - self_ty, + stable_pick.self_ty, self.tcx.def_path_str(def_id), stable_pick.item.ident ), @@ -1412,6 +1499,7 @@ possibly_unsatisfied_predicates: &mut Vec<( ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, )>, ) -> ProbeResult { debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe); @@ -1423,8 +1511,8 @@ .sup(probe.xform_self_ty, self_ty) { Ok(InferOk { obligations, value: () }) => obligations, - Err(_) => { - debug!("--> cannot relate self-types"); + Err(err) => { + debug!("--> cannot relate self-types {:?}", err); return ProbeResult::NoMatch; } }; @@ -1473,7 +1561,11 @@ let o = self.resolve_vars_if_possible(o); if !self.predicate_may_hold(&o) { result = ProbeResult::NoMatch; - possibly_unsatisfied_predicates.push((o.predicate, None)); + possibly_unsatisfied_predicates.push(( + o.predicate, + None, + Some(o.cause), + )); } } } @@ -1519,8 +1611,11 @@ } else { Some(predicate) }; - possibly_unsatisfied_predicates - .push((nested_predicate, p)); + possibly_unsatisfied_predicates.push(( + nested_predicate, + p, + Some(obligation.cause.clone()), + )); } } } @@ -1528,7 +1623,7 @@ // Some nested subobligation of this predicate // failed. let predicate = self.resolve_vars_if_possible(predicate); - possibly_unsatisfied_predicates.push((predicate, None)); + possibly_unsatisfied_predicates.push((predicate, None, None)); } } false @@ -1547,7 +1642,7 @@ let o = self.resolve_vars_if_possible(o); if !self.predicate_may_hold(&o) { result = ProbeResult::NoMatch; - possibly_unsatisfied_predicates.push((o.predicate, None)); + possibly_unsatisfied_predicates.push((o.predicate, None, Some(o.cause))); } } @@ -1591,6 +1686,7 @@ /// use, so it's ok to just commit to "using the method from the trait Foo". fn collapse_candidates_to_trait_pick( &self, + self_ty: Ty<'tcx>, probes: &[(&Candidate<'tcx>, ProbeResult)], ) -> Option<Pick<'tcx>> { // Do all probes correspond to the same trait? @@ -1610,6 +1706,7 @@ import_ids: probes[0].0.import_ids.clone(), autoderefs: 0, autoref_or_ptr_adjustment: None, + self_ty, }) } @@ -1828,7 +1925,7 @@ } impl<'tcx> Candidate<'tcx> { - fn to_unadjusted_pick(&self) -> Pick<'tcx> { + fn to_unadjusted_pick(&self, self_ty: Ty<'tcx>) -> Pick<'tcx> { Pick { item: self.item, kind: match self.kind { @@ -1852,6 +1949,7 @@ import_ids: self.import_ids.clone(), autoderefs: 0, autoref_or_ptr_adjustment: None, + self_ty, } } }
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 183ebc5..ca174ed 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -15,9 +15,11 @@ use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_span::lev_distance; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{source_map, FileName, MultiSpan, Span}; +use rustc_span::{source_map, FileName, MultiSpan, Span, Symbol}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::{FulfillmentError, Obligation}; +use rustc_trait_selection::traits::{ + FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, +}; use std::cmp::Ordering; use std::iter; @@ -317,6 +319,10 @@ .span_to_snippet(lit.span) .unwrap_or_else(|_| "<numeric literal>".to_owned()); + // If this is a floating point literal that ends with '.', + // get rid of it to stop this from becoming a member access. + let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); + err.span_suggestion( lit.span, &format!( @@ -324,7 +330,7 @@ like `{}`", concrete_type ), - format!("{}_{}", snippet, concrete_type), + format!("{snippet}_{concrete_type}"), Applicability::MaybeIncorrect, ); } @@ -478,6 +484,26 @@ let mut label_span_not_found = || { if unsatisfied_predicates.is_empty() { err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); + let is_string_or_ref_str = match actual.kind() { + ty::Ref(_, ty, _) => { + ty.is_str() + || matches!( + ty.kind(), + ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did) + ) + } + ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did), + _ => false, + }; + if is_string_or_ref_str && item_name.name == sym::iter { + err.span_suggestion_verbose( + item_name.span, + "because of the in-memory representation of `&str`, to obtain \ + an `Iterator` over each of its codepoint use method `chars`", + String::from("chars"), + Applicability::MachineApplicable, + ); + } if let ty::Adt(adt, _) = rcvr_ty.kind() { let mut inherent_impls_candidate = self .tcx @@ -678,27 +704,39 @@ if let (ty::Param(_), ty::PredicateKind::Trait(p)) = (self_ty.kind(), parent_pred.kind().skip_binder()) { - if let ty::Adt(def, _) = p.trait_ref.self_ty().kind() { - let node = def.did.as_local().map(|def_id| { + let node = match p.trait_ref.self_ty().kind() { + ty::Param(_) => { + // Account for `fn` items like in `issue-35677.rs` to + // suggest restricting its type params. + let did = self.tcx.hir().body_owner_def_id(hir::BodyId { + hir_id: self.body_id, + }); + Some( + self.tcx + .hir() + .get(self.tcx.hir().local_def_id_to_hir_id(did)), + ) + } + ty::Adt(def, _) => def.did.as_local().map(|def_id| { self.tcx .hir() .get(self.tcx.hir().local_def_id_to_hir_id(def_id)) - }); - if let Some(hir::Node::Item(hir::Item { kind, .. })) = node { - if let Some(g) = kind.generics() { - let key = match g.where_clause.predicates { - [.., pred] => (pred.span().shrink_to_hi(), false), - [] => ( - g.where_clause - .span_for_predicates_or_empty_place(), - true, - ), - }; - type_params - .entry(key) - .or_insert_with(FxHashSet::default) - .insert(obligation.to_owned()); - } + }), + _ => None, + }; + if let Some(hir::Node::Item(hir::Item { kind, .. })) = node { + if let Some(g) = kind.generics() { + let key = match g.where_clause.predicates { + [.., pred] => (pred.span().shrink_to_hi(), false), + [] => ( + g.where_clause.span_for_predicates_or_empty_place(), + true, + ), + }; + type_params + .entry(key) + .or_insert_with(FxHashSet::default) + .insert(obligation.to_owned()); } } } @@ -767,22 +805,109 @@ _ => None, } }; + + // 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) 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, p, parent)) + } + _ => None, + }) + { + let parent_trait_ref = data.parent_trait_ref; + let parent_def_id = parent_trait_ref.def_id(); + let path = parent_trait_ref.print_only_trait_path(); + let tr_self_ty = parent_trait_ref.skip_binder().self_ty(); + let mut candidates = vec![]; + self.tcx.for_each_relevant_impl( + parent_def_id, + parent_trait_ref.self_ty().skip_binder(), + |impl_def_id| match self.tcx.hir().get_if_local(impl_def_id) { + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { .. }), + .. + })) => { + candidates.push(impl_def_id); + } + _ => {} + }, + ); + if let [def_id] = &candidates[..] { + match self.tcx.hir().get_if_local(*def_id) { + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + .. + })) => { + 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 = Vec::with_capacity(2); + if let Some(trait_ref) = of_trait { + spans.push(trait_ref.path.span); + } + spans.push(self_ty.span); + let entry = spanned_predicates.entry(spans.into()); + entry + .or_insert_with(|| (path, tr_self_ty, Vec::new())) + .2 + .push(p); + } + _ => {} + } + } + } + for (span, (path, self_ty, preds)) in spanned_predicates { + err.span_note( + span, + &format!( + "the following trait bounds were not satisfied because of the \ + requirements of the implementation of `{}` for `{}`:\n{}", + path, + self_ty, + preds + .into_iter() + // .map(|pred| format!("{:?}", pred)) + .filter_map(|pred| format_pred(*pred)) + .map(|(p, _)| format!("`{}`", p)) + .collect::<Vec<_>>() + .join("\n"), + ), + ); + } + + // The requirements that didn't have an `impl` span to show. let mut bound_list = unsatisfied_predicates .iter() - .filter_map(|(pred, parent_pred)| { - format_pred(*pred).map(|(p, self_ty)| match parent_pred { - None => format!("`{}`", &p), - Some(parent_pred) => match format_pred(*parent_pred) { + .filter(|(pred, _, _parent_pred)| !skip_list.contains(&pred)) + .filter_map(|(pred, parent_pred, _cause)| { + format_pred(*pred).map(|(p, self_ty)| { + collect_type_param_suggestions(self_ty, pred, &p); + match parent_pred { None => format!("`{}`", &p), - Some((parent_p, _)) => { - collect_type_param_suggestions(self_ty, parent_pred, &p); - format!("`{}`\nwhich is required by `{}`", p, parent_p) - } - }, + Some(parent_pred) => match format_pred(*parent_pred) { + None => format!("`{}`", &p), + Some((parent_p, _)) => { + collect_type_param_suggestions( + self_ty, + parent_pred, + &p, + ); + format!("`{}`\nwhich is required by `{}`", p, parent_p) + } + }, + } }) }) .enumerate() .collect::<Vec<(usize, String)>>(); + for ((span, empty_where), obligations) in type_params.into_iter() { restrict_type_params = true; // #74886: Sort here so that the output is always the same. @@ -812,7 +937,7 @@ for (span, msg) in bound_spans.into_iter() { err.span_label(span, &msg); } - if !bound_list.is_empty() { + if !bound_list.is_empty() || !skip_list.is_empty() { let bound_list = bound_list .into_iter() .map(|(_, path)| path) @@ -822,9 +947,11 @@ err.set_primary_message(&format!( "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied" )); - err.note(&format!( - "the following trait bounds were not satisfied:\n{bound_list}" - )); + if !bound_list.is_empty() { + err.note(&format!( + "the following trait bounds were not satisfied:\n{bound_list}" + )); + } self.suggest_derive(&mut err, &unsatisfied_predicates); unsatisfied_bounds = true; @@ -1038,18 +1165,25 @@ err.span_note(spans, &msg); } - let preds: Vec<_> = errors.iter().map(|e| (e.obligation.predicate, None)).collect(); + let preds: Vec<_> = errors + .iter() + .map(|e| (e.obligation.predicate, None, Some(e.obligation.cause.clone()))) + .collect(); self.suggest_derive(err, &preds); } fn suggest_derive( &self, err: &mut DiagnosticBuilder<'_>, - unsatisfied_predicates: &Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>, + unsatisfied_predicates: &Vec<( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )>, ) { let mut derives = Vec::<(String, Span, String)>::new(); let mut traits = Vec::<Span>::new(); - for (pred, _) in unsatisfied_predicates { + for (pred, _, _) in unsatisfied_predicates { let trait_pred = match pred.kind().skip_binder() { ty::PredicateKind::Trait(trait_pred) => trait_pred, _ => continue, @@ -1203,6 +1337,13 @@ let mut candidates = valid_out_of_scope_traits; candidates.sort(); candidates.dedup(); + + // `TryFrom` and `FromIterator` have no methods + let edition_fix = candidates + .iter() + .find(|did| self.tcx.is_diagnostic_item(sym::TryInto, **did)) + .copied(); + err.help("items from traits can only be used if the trait is in scope"); let msg = format!( "the following {traits_are} implemented but not in scope; \ @@ -1212,6 +1353,13 @@ ); self.suggest_use_candidates(err, msg, candidates); + if let Some(did) = edition_fix { + err.note(&format!( + "'{}' is included in the prelude starting in Edition 2021", + with_crate_prefix(|| self.tcx.def_path_str(did)) + )); + } + true } else { false @@ -1226,7 +1374,11 @@ item_name: Ident, source: SelfSource<'tcx>, valid_out_of_scope_traits: Vec<DefId>, - unsatisfied_predicates: &[(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)], + unsatisfied_predicates: &[( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )], unsatisfied_bounds: bool, ) { let mut alt_rcvr_sugg = false; @@ -1237,6 +1389,7 @@ self.tcx.lang_items().deref_trait(), self.tcx.lang_items().deref_mut_trait(), self.tcx.lang_items().drop_trait(), + self.tcx.get_diagnostic_item(sym::AsRef), ]; // Try alternative arbitrary self types that could fulfill this call. // FIXME: probe for all types that *could* be arbitrary self-types, not @@ -1286,7 +1439,11 @@ // We don't want to suggest a container type when the missing // method is `.clone()` or `.deref()` otherwise we'd suggest // `Arc::new(foo).clone()`, which is far from what the user wants. - let skip = skippable.contains(&did); + // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not + // implement the `AsRef` trait. + let skip = skippable.contains(&did) + || (("Pin::new" == *pre) + && (Symbol::intern("as_ref") == item_name.name)); // Make sure the method is defined for the *actual* receiver: we don't // want to treat `Box<Self>` as a receiver if it only works because of // an autoderef to `&self` @@ -1337,7 +1494,7 @@ // this isn't perfect (that is, there are cases when // implementing a trait would be legal but is rejected // here). - unsatisfied_predicates.iter().all(|(p, _)| { + unsatisfied_predicates.iter().all(|(p, _, _)| { match p.kind().skip_binder() { // Hide traits if they are present in predicates as they can be fixed without // having to implement them. @@ -1391,7 +1548,7 @@ } } // We only want to suggest public or local traits (#45781). - item.vis == ty::Visibility::Public || info.def_id.is_local() + item.vis.is_public() || info.def_id.is_local() }) .is_some() }) @@ -1451,7 +1608,7 @@ Node::GenericParam(param) => { let mut impl_trait = false; let has_bounds = - if let hir::GenericParamKind::Type { synthetic: Some(_), .. } = + if let hir::GenericParamKind::Type { synthetic: true, .. } = ¶m.kind { // We've found `fn foo(x: impl Trait)` instead of
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index a037bb6..7bfd3f0 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -297,9 +297,9 @@ fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { // Closures' typeck results come from their outermost function, // as they are part of the same "inference environment". - let outer_def_id = tcx.closure_base_def_id(def_id); - if outer_def_id != def_id { - return tcx.has_typeck_results(outer_def_id); + let typeck_root_def_id = tcx.typeck_root_def_id(def_id); + if typeck_root_def_id != def_id { + return tcx.has_typeck_results(typeck_root_def_id); } if let Some(def_id) = def_id.as_local() { @@ -348,9 +348,9 @@ ) -> &'tcx ty::TypeckResults<'tcx> { // Closures' typeck results come from their outermost function, // as they are part of the same "inference environment". - let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local(); - if outer_def_id != def_id { - return tcx.typeck(outer_def_id); + let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); + if typeck_root_def_id != def_id { + return tcx.typeck(typeck_root_def_id); } let id = tcx.hir().local_def_id_to_hir_id(def_id); @@ -535,8 +535,8 @@ } fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: Span) { - // Only restricted on wasm32 target for now - if !tcx.sess.opts.target_triple.triple().starts_with("wasm32") { + // Only restricted on wasm target for now + if !tcx.sess.target.is_like_wasm { return; }
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 79e004a..f83209f 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs
@@ -42,7 +42,7 @@ return_ty }; - self.check_lhs_assignable(lhs, "E0067", &op.span); + self.check_lhs_assignable(lhs, "E0067", op.span); ty } @@ -399,12 +399,9 @@ } }; if let Ref(_, rty, _) = lhs_ty.kind() { - if { - self.infcx.type_is_copy_modulo_regions(self.param_env, rty, lhs_expr.span) - && self - .lookup_op_method(rty, &[rhs_ty], Op::Binary(op, is_assign)) - .is_ok() - } { + if self.infcx.type_is_copy_modulo_regions(self.param_env, rty, lhs_expr.span) + && self.lookup_op_method(rty, &[rhs_ty], Op::Binary(op, is_assign)).is_ok() + { if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) { let msg = &format!( "`{}{}` can be used on `{}`, you can dereference `{}`", @@ -492,7 +489,7 @@ other_ty: Ty<'tcx>, op: hir::BinOp, is_assign: IsAssign, - ) -> bool /* did we suggest to call a function because of missing parenthesis? */ { + ) -> bool /* did we suggest to call a function because of missing parentheses? */ { err.span_label(span, ty.to_string()); if let FnDef(def_id, _) = *ty.kind() { let source_map = self.tcx.sess.source_map(); @@ -502,9 +499,7 @@ // FIXME: Instead of exiting early when encountering bound vars in // the function signature, consider keeping the binder here and // propagating it downwards. - let fn_sig = if let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() { - fn_sig - } else { + let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else { return false; }; @@ -831,10 +826,7 @@ self.obligation_for_method(span, trait_did, lhs_ty, Some(other_tys)); let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); fulfill.register_predicate_obligation(self, obligation); - Err(match fulfill.select_where_possible(&self.infcx) { - Err(errors) => errors, - _ => vec![], - }) + Err(fulfill.select_where_possible(&self.infcx)) } } }
diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 6352191..cbf33cf 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs
@@ -292,7 +292,9 @@ // String and byte-string literals result in types `&str` and `&[u8]` respectively. // All other literals result in non-reference types. // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`. - PatKind::Lit(lt) => match self.check_expr(lt).kind() { + // + // Call `resolve_vars_if_possible` here for inline const blocks. + PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_expr(lt)).kind() { ty::Ref(..) => AdjustMode::Pass, _ => AdjustMode::Peel, }, @@ -718,10 +720,7 @@ ti: TopInfo<'tcx>, ) -> Ty<'tcx> { // Resolve the path and check the definition for errors. - let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, pat.hir_id) - { - variant_ty - } else { + let Some((variant, pat_ty)) = self.check_struct_path(qpath, pat.hir_id) else { let err = self.tcx.ty_error(); for field in fields { let ti = TopInfo { parent_pat: Some(pat), ..ti };
diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs index 849bf1e..5d9e6eb 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs
@@ -411,9 +411,7 @@ debug!("convert_place_op_to_mutable: method={:?}", method); self.write_method_call(expr.hir_id, method); - let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind() { - r - } else { + let ty::Ref(region, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind() else { span_bug!(expr.span, "input to mutable place op is not a mut ref?"); };
diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 7c8b752..d2d8b14 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs
@@ -88,7 +88,6 @@ use rustc_middle::ty::adjustment; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; -use rustc_trait_selection::opaque_types::InferCtxtExt as _; use std::ops::Deref; // a variation on try that just returns unit @@ -104,7 +103,7 @@ }; } -trait OutlivesEnvironmentExt<'tcx> { +pub(crate) trait OutlivesEnvironmentExt<'tcx> { fn add_implied_bounds( &mut self, infcx: &InferCtxt<'a, 'tcx>, @@ -340,8 +339,29 @@ self.link_fn_params(body.params); self.visit_body(body); self.visit_region_obligations(body_id.hir_id); + } - self.constrain_opaque_types(self.outlives_environment.free_region_map()); + fn visit_inline_const(&mut self, id: hir::HirId, body: &'tcx hir::Body<'tcx>) { + debug!("visit_inline_const(id={:?})", id); + + // Save state of current function. We will restore afterwards. + let old_body_id = self.body_id; + let old_body_owner = self.body_owner; + let env_snapshot = self.outlives_environment.push_snapshot_pre_typeck_child(); + + let body_id = body.id(); + self.body_id = body_id.hir_id; + self.body_owner = self.tcx.hir().body_owner_def_id(body_id); + + self.outlives_environment.save_implied_bounds(body_id.hir_id); + + self.visit_body(body); + self.visit_region_obligations(body_id.hir_id); + + // Restore state from previous function. + self.outlives_environment.pop_snapshot_post_typeck_child(env_snapshot); + self.body_id = old_body_id; + self.body_owner = old_body_owner; } fn visit_region_obligations(&mut self, hir_id: hir::HirId) { @@ -409,13 +429,13 @@ // `visit_fn_body`. We will restore afterwards. let old_body_id = self.body_id; let old_body_owner = self.body_owner; - let env_snapshot = self.outlives_environment.push_snapshot_pre_closure(); + let env_snapshot = self.outlives_environment.push_snapshot_pre_typeck_child(); let body = self.tcx.hir().body(body_id); self.visit_fn_body(hir_id, body, span); // Restore state from previous function. - self.outlives_environment.pop_snapshot_post_closure(env_snapshot); + self.outlives_environment.pop_snapshot_post_typeck_child(env_snapshot); self.body_id = old_body_id; self.body_owner = old_body_owner; } @@ -463,6 +483,11 @@ intravisit::walk_expr(self, expr); } + hir::ExprKind::ConstBlock(anon_const) => { + let body = self.tcx.hir().body(anon_const.body); + self.visit_inline_const(anon_const.hir_id, body); + } + _ => intravisit::walk_expr(self, expr), } }
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 67c9670..5f5d308 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -148,10 +148,17 @@ } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Closure(cc, _, body_id, _, _) = expr.kind { - let body = self.fcx.tcx.hir().body(body_id); - self.visit_body(body); - self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, cc); + match expr.kind { + hir::ExprKind::Closure(cc, _, body_id, _, _) => { + let body = self.fcx.tcx.hir().body(body_id); + self.visit_body(body); + self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, cc); + } + hir::ExprKind::ConstBlock(anon_const) => { + let body = self.fcx.tcx.hir().body(anon_const.body); + self.visit_body(body); + } + _ => {} } intravisit::walk_expr(self, expr); @@ -930,8 +937,8 @@ self.tcx.get_diagnostic_item(sym::unwind_safe_trait), self.tcx.get_diagnostic_item(sym::ref_unwind_safe_trait), ]; - let auto_traits = - vec!["`Clone`", "`Sync`", "`Send`", "`Unpin`", "`UnwindSafe`", "`RefUnwindSafe`"]; + const AUTO_TRAITS: [&str; 6] = + ["`Clone`", "`Sync`", "`Send`", "`Unpin`", "`UnwindSafe`", "`RefUnwindSafe`"]; let root_var_min_capture_list = min_captures.and_then(|m| m.get(&var_hir_id))?; @@ -1004,7 +1011,7 @@ // by the root variable but not by the capture for (idx, _) in obligations_should_hold.iter().enumerate() { if !obligations_holds_for_capture[idx] && obligations_should_hold[idx] { - capture_problems.insert(auto_traits[idx]); + capture_problems.insert(AUTO_TRAITS[idx]); } } @@ -1052,11 +1059,7 @@ return None; } - let root_var_min_capture_list = if let Some(root_var_min_capture_list) = - min_captures.and_then(|m| m.get(&var_hir_id)) - { - root_var_min_capture_list - } else { + let Some(root_var_min_capture_list) = min_captures.and_then(|m| m.get(&var_hir_id)) else { // The upvar is mentioned within the closure but no path starting from it is // used. This occurs when you have (e.g.) // @@ -1150,9 +1153,7 @@ closure_clause: hir::CaptureBy, min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, ) -> (Vec<NeededMigration>, MigrationWarningReason) { - let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { - upvars - } else { + let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) else { return (Vec::new(), MigrationWarningReason::default()); }; @@ -1756,9 +1757,7 @@ diag_expr_id: hir::HirId, ) { let tcx = self.fcx.tcx; - let upvar_id = if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { - upvar_id - } else { + let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return; };
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index d737fe9..e58faf9 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -1,3 +1,4 @@ +use crate::check::regionck::OutlivesEnvironmentExt; use crate::check::{FnCtxt, Inherited}; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; @@ -11,16 +12,21 @@ use rustc_hir::itemlikevisit::ParItemLikeVisitor; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::outlives::obligations::TypeOutlives; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{self, RegionckMode, SubregionOrigin}; use rustc_middle::hir::map as hir_map; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ - self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, + self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitor, + WithConstness, }; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::Span; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc}; use std::convert::TryInto; @@ -253,6 +259,362 @@ .emit(); } } + + check_gat_where_clauses(tcx, trait_item, encl_trait_def_id); +} + +/// Require that the user writes where clauses on GATs for the implicit +/// outlives bounds involving trait parameters in trait functions and +/// lifetimes passed as GAT substs. See `self-outlives-lint` test. +/// +/// This trait will be our running example. We are currently WF checking the `Item` item... +/// +/// ```rust +/// trait LendingIterator { +/// type Item<'me>; // <-- WF checking this trait item +/// +/// fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>; +/// } +/// ``` +fn check_gat_where_clauses( + tcx: TyCtxt<'_>, + trait_item: &hir::TraitItem<'_>, + encl_trait_def_id: DefId, +) { + let item = tcx.associated_item(trait_item.def_id); + // If the current trait item isn't a type, it isn't a GAT + if !matches!(item.kind, ty::AssocKind::Type) { + return; + } + let generics: &ty::Generics = tcx.generics_of(trait_item.def_id); + // If the current associated type doesn't have any (own) params, it's not a GAT + // FIXME(jackh726): we can also warn in the more general case + if generics.params.len() == 0 { + return; + } + let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id); + let mut clauses: Option<FxHashSet<ty::Predicate<'_>>> = None; + // For every function in this trait... + // In our example, this would be the `next` method + for item in + associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn)) + { + // The clauses we that we would require from this function + let mut function_clauses = FxHashSet::default(); + + let id = hir::HirId::make_owner(item.def_id.expect_local()); + let param_env = tcx.param_env(item.def_id.expect_local()); + + let sig = tcx.fn_sig(item.def_id); + // Get the signature using placeholders. In our example, this would + // convert the late-bound 'a into a free region. + let sig = tcx.liberate_late_bound_regions(item.def_id, sig); + // Collect the arguments that are given to this GAT in the return type + // of the function signature. In our example, the GAT in the return + // type is `<Self as LendingIterator>::Item<'a>`, so 'a and Self are arguments. + let (regions, types) = + GATSubstCollector::visit(tcx, trait_item.def_id.to_def_id(), sig.output()); + + // If both regions and types are empty, then this GAT isn't in the + // return type, and we shouldn't try to do clause analysis + // (particularly, doing so would end up with an empty set of clauses, + // since the current method would require none, and we take the + // intersection of requirements of all methods) + if types.is_empty() && regions.is_empty() { + continue; + } + + // The types we can assume to be well-formed. In our example, this + // would be &'a mut Self, from the first argument. + let mut wf_tys = FxHashSet::default(); + wf_tys.extend(sig.inputs()); + + // For each region argument (e.g., 'a in our example), check for a + // relationship to the type arguments (e.g., Self). If there is an + // outlives relationship (`Self: 'a`), then we want to ensure that is + // reflected in a where clause on the GAT itself. + for (region, region_idx) in ®ions { + for (ty, ty_idx) in &types { + // In our example, requires that Self: 'a + if ty_known_to_outlive(tcx, id, param_env, &wf_tys, *ty, *region) { + debug!(?ty_idx, ?region_idx); + debug!("required clause: {} must outlive {}", ty, region); + // Translate into the generic parameters of the GAT. In + // our example, the type was Self, which will also be + // Self in the GAT. + let ty_param = generics.param_at(*ty_idx, tcx); + let ty_param = tcx.mk_ty(ty::Param(ty::ParamTy { + index: ty_param.index, + name: ty_param.name, + })); + // Same for the region. In our example, 'a corresponds + // to the 'me parameter. + let region_param = generics.param_at(*region_idx, tcx); + let region_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_param.def_id, + index: region_param.index, + name: region_param.name, + })); + // The predicate we expect to see. (In our example, + // `Self: 'me`.) + let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( + ty_param, + region_param, + )); + let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); + function_clauses.insert(clause); + } + } + } + + // For each region argument (e.g., 'a in our example), also check for a + // relationship to the other region arguments. If there is an + // outlives relationship, then we want to ensure that is + // reflected in a where clause on the GAT itself. + for (region_a, region_a_idx) in ®ions { + for (region_b, region_b_idx) in ®ions { + if region_a == region_b { + continue; + } + + if region_known_to_outlive(tcx, id, param_env, &wf_tys, *region_a, *region_b) { + debug!(?region_a_idx, ?region_b_idx); + debug!("required clause: {} must outlive {}", region_a, region_b); + // Translate into the generic parameters of the GAT. + let region_a_param = generics.param_at(*region_a_idx, tcx); + let region_a_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_a_param.def_id, + index: region_a_param.index, + name: region_a_param.name, + })); + // Same for the region. + let region_b_param = generics.param_at(*region_b_idx, tcx); + let region_b_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_b_param.def_id, + index: region_b_param.index, + name: region_b_param.name, + })); + // The predicate we expect to see. + let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( + region_a_param, + region_b_param, + )); + let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); + function_clauses.insert(clause); + } + } + } + + // Imagine we have: + // ``` + // trait Foo { + // type Bar<'me>; + // fn gimme(&self) -> Self::Bar<'_>; + // fn gimme_default(&self) -> Self::Bar<'static>; + // } + // ``` + // We only want to require clauses on `Bar` that we can prove from *all* functions (in this + // case, `'me` can be `static` from `gimme_default`) + match clauses.as_mut() { + Some(clauses) => { + clauses.drain_filter(|p| !function_clauses.contains(p)); + } + None => { + clauses = Some(function_clauses); + } + } + } + + // If there are any missing clauses, emit an error + let mut clauses = clauses.unwrap_or_default(); + debug!(?clauses); + if !clauses.is_empty() { + let written_predicates: ty::GenericPredicates<'_> = + tcx.explicit_predicates_of(trait_item.def_id); + let mut clauses: Vec<_> = clauses + .drain_filter(|clause| !written_predicates.predicates.iter().any(|p| &p.0 == clause)) + .map(|clause| format!("{}", clause)) + .collect(); + // We sort so that order is predictable + clauses.sort(); + if !clauses.is_empty() { + let mut err = tcx.sess.struct_span_err( + trait_item.span, + &format!("Missing required bounds on {}", trait_item.ident), + ); + + let suggestion = format!( + "{} {}", + if !trait_item.generics.where_clause.predicates.is_empty() { + "," + } else { + " where" + }, + clauses.join(", "), + ); + err.span_suggestion( + trait_item.generics.where_clause.tail_span_for_suggestion(), + "add the required where clauses", + suggestion, + Applicability::MachineApplicable, + ); + + err.emit() + } + } +} + +// FIXME(jackh726): refactor some of the shared logic between the two functions below + +/// Given a known `param_env` and a set of well formed types, can we prove that +/// `ty` outlives `region`. +fn ty_known_to_outlive<'tcx>( + tcx: TyCtxt<'tcx>, + id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxHashSet<Ty<'tcx>>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, +) -> bool { + // Unfortunately, we have to use a new `InferCtxt` each call, because + // region constraints get added and solved there and we need to test each + // call individually. + tcx.infer_ctxt().enter(|infcx| { + let mut outlives_environment = OutlivesEnvironment::new(param_env); + outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); + outlives_environment.save_implied_bounds(id); + let region_bound_pairs = outlives_environment.region_bound_pairs_map().get(&id).unwrap(); + + let cause = ObligationCause::new(DUMMY_SP, id, ObligationCauseCode::MiscObligation); + + let sup_type = ty; + let sub_region = region; + + let origin = SubregionOrigin::from_obligation_cause(&cause, || { + infer::RelateParamBound(cause.span, sup_type, None) + }); + + let outlives = &mut TypeOutlives::new( + &infcx, + tcx, + ®ion_bound_pairs, + Some(infcx.tcx.lifetimes.re_root_empty), + param_env, + ); + outlives.type_must_outlive(origin, sup_type, sub_region); + + let errors = infcx.resolve_regions( + id.expect_owner().to_def_id(), + &outlives_environment, + RegionckMode::default(), + ); + + debug!(?errors, "errors"); + + // If we were able to prove that the type outlives the region without + // an error, it must be because of the implied or explicit bounds... + errors.is_empty() + }) +} + +fn region_known_to_outlive<'tcx>( + tcx: TyCtxt<'tcx>, + id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxHashSet<Ty<'tcx>>, + region_a: ty::Region<'tcx>, + region_b: ty::Region<'tcx>, +) -> bool { + // Unfortunately, we have to use a new `InferCtxt` each call, because + // region constraints get added and solved there and we need to test each + // call individually. + tcx.infer_ctxt().enter(|infcx| { + let mut outlives_environment = OutlivesEnvironment::new(param_env); + outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); + outlives_environment.save_implied_bounds(id); + + let cause = ObligationCause::new(DUMMY_SP, id, ObligationCauseCode::MiscObligation); + + let origin = SubregionOrigin::from_obligation_cause(&cause, || { + infer::RelateRegionParamBound(cause.span) + }); + + use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate; + (&infcx).push_sub_region_constraint(origin, region_a, region_b); + + let errors = infcx.resolve_regions( + id.expect_owner().to_def_id(), + &outlives_environment, + RegionckMode::default(), + ); + + debug!(?errors, "errors"); + + // If we were able to prove that the type outlives the region without + // an error, it must be because of the implied or explicit bounds... + errors.is_empty() + }) +} + +/// TypeVisitor that looks for uses of GATs like +/// `<P0 as Trait<P1..Pn>>::GAT<Pn..Pm>` and adds the arguments `P0..Pm` into +/// the two vectors, `regions` and `types` (depending on their kind). For each +/// parameter `Pi` also track the index `i`. +struct GATSubstCollector<'tcx> { + tcx: TyCtxt<'tcx>, + gat: DefId, + // Which region appears and which parameter index its subsituted for + regions: FxHashSet<(ty::Region<'tcx>, usize)>, + // Which params appears and which parameter index its subsituted for + types: FxHashSet<(Ty<'tcx>, usize)>, +} + +impl<'tcx> GATSubstCollector<'tcx> { + fn visit<T: TypeFoldable<'tcx>>( + tcx: TyCtxt<'tcx>, + gat: DefId, + t: T, + ) -> (FxHashSet<(ty::Region<'tcx>, usize)>, FxHashSet<(Ty<'tcx>, usize)>) { + let mut visitor = GATSubstCollector { + tcx, + gat, + regions: FxHashSet::default(), + types: FxHashSet::default(), + }; + t.visit_with(&mut visitor); + (visitor.regions, visitor.types) + } +} + +impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { + type BreakTy = !; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + match t.kind() { + ty::Projection(p) if p.item_def_id == self.gat => { + for (idx, subst) in p.substs.iter().enumerate() { + match subst.unpack() { + GenericArgKind::Lifetime(lt) => { + self.regions.insert((lt, idx)); + } + GenericArgKind::Type(t) => { + self.types.insert((t, idx)); + } + _ => {} + } + } + } + _ => {} + } + t.super_visit_with(self) + } + + fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { + Some(self.tcx) + } } fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { @@ -1274,19 +1636,38 @@ /// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that /// aren't true. -fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, span: Span, id: hir::HirId) { +fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, mut span: Span, id: hir::HirId) { let empty_env = ty::ParamEnv::empty(); let def_id = fcx.tcx.hir().local_def_id(id); - let predicates = fcx.tcx.predicates_of(def_id).predicates.iter().map(|(p, _)| *p); + let predicates_with_span = + fcx.tcx.predicates_of(def_id).predicates.iter().map(|(p, span)| (*p, *span)); // Check elaborated bounds. - let implied_obligations = traits::elaborate_predicates(fcx.tcx, predicates); + let implied_obligations = traits::elaborate_predicates_with_span(fcx.tcx, predicates_with_span); for obligation in implied_obligations { let pred = obligation.predicate; // Match the existing behavior. if pred.is_global(fcx.tcx) && !pred.has_late_bound_regions() { let pred = fcx.normalize_associated_types_in(span, pred); + let hir_node = fcx.tcx.hir().find(id); + + // only use the span of the predicate clause (#90869) + + if let Some(hir::Generics { where_clause, .. }) = + hir_node.and_then(|node| node.generics()) + { + let obligation_span = obligation.cause.span(fcx.tcx); + + span = where_clause + .predicates + .iter() + // There seems to be no better way to find out which predicate we are in + .find(|pred| pred.span().contains(obligation_span)) + .map(|pred| pred.span()) + .unwrap_or(obligation_span); + } + let obligation = traits::Obligation::new( traits::ObligationCause::new(span, id, traits::TrivialBound), empty_env,
diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index d951df9..fdc8b6b 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs
@@ -282,6 +282,12 @@ hir::ExprKind::Field(..) => { self.visit_field_id(e.hir_id); } + hir::ExprKind::ConstBlock(anon_const) => { + self.visit_node_id(e.span, anon_const.hir_id); + + let body = self.tcx().hir().body(anon_const.body); + self.visit_body(body); + } _ => {} }
diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index 8cae61e..372e835 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs
@@ -180,14 +180,14 @@ let coerced_fields = fields .iter() - .filter_map(|field| { + .filter(|field| { let ty_a = field.ty(tcx, substs_a); let ty_b = field.ty(tcx, substs_b); if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) { if layout.is_zst() && layout.align.abi.bytes() == 1 { // ignore ZST fields with alignment of 1 byte - return None; + return false; } } @@ -204,11 +204,11 @@ )) .emit(); - return None; + return false; } } - Some(field) + return true; }) .collect::<Vec<_>>(); @@ -263,7 +263,8 @@ } // Check that all transitive obligations are satisfied. - if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) { + let errors = fulfill_cx.select_all_or_error(&infcx); + if !errors.is_empty() { infcx.report_fulfillment_errors(&errors, None, false); } @@ -522,7 +523,8 @@ fulfill_cx.register_predicate_obligation(&infcx, predicate); // Check that all transitive obligations are satisfied. - if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) { + let errors = fulfill_cx.select_all_or_error(&infcx); + if !errors.is_empty() { infcx.report_fulfillment_errors(&errors, None, false); }
diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs index 079604f..377ebf1 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_typeck/src/coherence/mod.rs
@@ -168,6 +168,7 @@ use self::builtin::coerce_unsized_info; use self::inherent_impls::{crate_inherent_impls, inherent_impls}; use self::inherent_impls_overlap::crate_inherent_impls_overlap_check; + use self::orphan::orphan_check_crate; *providers = Providers { coherent_trait, @@ -175,6 +176,7 @@ inherent_impls, crate_inherent_impls_overlap_check, coerce_unsized_info, + orphan_check_crate, ..*providers }; } @@ -195,13 +197,13 @@ } pub fn check_coherence(tcx: TyCtxt<'_>) { + tcx.sess.time("unsafety_checking", || unsafety::check(tcx)); + tcx.ensure().orphan_check_crate(()); + for &trait_def_id in tcx.all_local_trait_impls(()).keys() { tcx.ensure().coherent_trait(trait_def_id); } - tcx.sess.time("unsafety_checking", || unsafety::check(tcx)); - tcx.sess.time("orphan_checking", || orphan::check(tcx)); - // these queries are executed for side-effects (error reporting): tcx.ensure().crate_inherent_impls(()); tcx.ensure().crate_inherent_impls_overlap_check(());
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index 0326d1f..b450d3f 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -2,250 +2,266 @@ //! crate or pertains to a type defined in this crate. use rustc_errors::struct_span_err; +use rustc_errors::ErrorReported; use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::def_id::LocalDefId; +use rustc_span::Span; use rustc_trait_selection::traits; -pub fn check(tcx: TyCtxt<'_>) { - let mut orphan = OrphanChecker { tcx }; - tcx.hir().visit_all_item_likes(&mut orphan); -} - -struct OrphanChecker<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { - /// Checks exactly one impl for orphan rules and other such - /// restrictions. In this fn, it can happen that multiple errors - /// apply to a specific impl, so just return after reporting one - /// to prevent inundating the user with a bunch of similar error - /// reports. - fn visit_item(&mut self, item: &hir::Item<'_>) { - // "Trait" impl - if let hir::ItemKind::Impl(hir::Impl { - generics, of_trait: Some(ref tr), self_ty, .. - }) = &item.kind - { - debug!( - "coherence2::orphan check: trait impl {}", - self.tcx.hir().node_to_string(item.hir_id()) - ); - let trait_ref = self.tcx.impl_trait_ref(item.def_id).unwrap(); - let trait_def_id = trait_ref.def_id; - let sm = self.tcx.sess.source_map(); - let sp = sm.guess_head_span(item.span); - match traits::orphan_check(self.tcx, item.def_id.to_def_id()) { +pub(super) fn orphan_check_crate(tcx: TyCtxt<'_>, (): ()) -> &[LocalDefId] { + let mut errors = Vec::new(); + for (_trait, impls_of_trait) in tcx.all_local_trait_impls(()) { + for &impl_of_trait in impls_of_trait { + match orphan_check_impl(tcx, impl_of_trait) { Ok(()) => {} - Err(traits::OrphanCheckErr::NonLocalInputType(tys)) => { - let mut err = struct_span_err!( - self.tcx.sess, - sp, - E0117, - "only traits defined in the current crate can be implemented for \ - arbitrary types" - ); - err.span_label(sp, "impl doesn't use only types from inside the current crate"); - for (ty, is_target_ty) in &tys { - let mut ty = *ty; - self.tcx.infer_ctxt().enter(|infcx| { - // Remove the lifetimes unnecessary for this error. - ty = infcx.freshen(ty); - }); - ty = match ty.kind() { - // Remove the type arguments from the output, as they are not relevant. - // You can think of this as the reverse of `resolve_vars_if_possible`. - // That way if we had `Vec<MyType>`, we will properly attribute the - // problem to `Vec<T>` and avoid confusing the user if they were to see - // `MyType` in the error. - ty::Adt(def, _) => self.tcx.mk_adt(def, ty::List::empty()), - _ => ty, - }; - let this = "this".to_string(); - let (ty, postfix) = match &ty.kind() { - ty::Slice(_) => (this, " because slices are always foreign"), - ty::Array(..) => (this, " because arrays are always foreign"), - ty::Tuple(..) => (this, " because tuples are always foreign"), - _ => (format!("`{}`", ty), ""), - }; - let msg = format!("{} is not defined in the current crate{}", ty, postfix); - if *is_target_ty { - // Point at `D<A>` in `impl<A, B> for C<B> in D<A>` - err.span_label(self_ty.span, &msg); - } else { - // Point at `C<B>` in `impl<A, B> for C<B> in D<A>` - err.span_label(tr.path.span, &msg); - } - } - err.note("define and implement a trait or new type instead"); - err.emit(); - return; - } - Err(traits::OrphanCheckErr::UncoveredTy(param_ty, local_type)) => { - let mut sp = sp; - for param in generics.params { - if param.name.ident().to_string() == param_ty.to_string() { - sp = param.span; - } - } - - match local_type { - Some(local_type) => { - struct_span_err!( - self.tcx.sess, - sp, - E0210, - "type parameter `{}` must be covered by another type \ - when it appears before the first local type (`{}`)", - param_ty, - local_type - ) - .span_label( - sp, - format!( - "type parameter `{}` must be covered by another type \ - when it appears before the first local type (`{}`)", - param_ty, local_type - ), - ) - .note( - "implementing a foreign trait is only possible if at \ - least one of the types for which it is implemented is local, \ - and no uncovered type parameters appear before that first \ - local type", - ) - .note( - "in this case, 'before' refers to the following order: \ - `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \ - where `T0` is the first and `Tn` is the last", - ) - .emit(); - } - None => { - struct_span_err!( - self.tcx.sess, - sp, - E0210, - "type parameter `{}` must be used as the type parameter for some \ - local type (e.g., `MyStruct<{}>`)", - param_ty, - param_ty - ).span_label(sp, format!( - "type parameter `{}` must be used as the type parameter for some \ - local type", - param_ty, - )).note("implementing a foreign trait is only possible if at \ - least one of the types for which it is implemented is local" - ).note("only traits defined in the current crate can be \ - implemented for a type parameter" - ).emit(); - } - }; - return; - } + Err(ErrorReported) => errors.push(impl_of_trait), } + } + } + tcx.arena.alloc_slice(&errors) +} - // In addition to the above rules, we restrict impls of auto traits - // so that they can only be implemented on nominal types, such as structs, - // enums or foreign types. To see why this restriction exists, consider the - // following example (#22978). Imagine that crate A defines an auto trait - // `Foo` and a fn that operates on pairs of types: - // - // ``` - // // Crate A - // auto trait Foo { } - // fn two_foos<A:Foo,B:Foo>(..) { - // one_foo::<(A,B)>(..) - // } - // fn one_foo<T:Foo>(..) { .. } - // ``` - // - // This type-checks fine; in particular the fn - // `two_foos` is able to conclude that `(A,B):Foo` - // because `A:Foo` and `B:Foo`. - // - // Now imagine that crate B comes along and does the following: - // - // ``` - // struct A { } - // struct B { } - // impl Foo for A { } - // impl Foo for B { } - // impl !Send for (A, B) { } - // ``` - // - // This final impl is legal according to the orphan - // rules, but it invalidates the reasoning from - // `two_foos` above. - debug!( - "trait_ref={:?} trait_def_id={:?} trait_is_auto={}", - trait_ref, - trait_def_id, - self.tcx.trait_is_auto(trait_def_id) - ); - if self.tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() { - let self_ty = trait_ref.self_ty(); - let opt_self_def_id = match *self_ty.kind() { - ty::Adt(self_def, _) => Some(self_def.did), - ty::Foreign(did) => Some(did), - _ => None, - }; +#[instrument(skip(tcx), level = "debug")] +fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorReported> { + let trait_ref = tcx.impl_trait_ref(def_id).unwrap(); + let trait_def_id = trait_ref.def_id; - let msg = match opt_self_def_id { - // We only want to permit nominal types, but not *all* nominal types. - // They must be local to the current crate, so that people - // can't do `unsafe impl Send for Rc<SomethingLocal>` or - // `impl !Send for Box<SomethingLocalAndSend>`. - Some(self_def_id) => { - if self_def_id.is_local() { - None - } else { - Some(( - format!( - "cross-crate traits with a default impl, like `{}`, \ - can only be implemented for a struct/enum type \ - defined in the current crate", - self.tcx.def_path_str(trait_def_id) - ), - "can't implement cross-crate trait for type in another crate", - )) - } - } - _ => Some(( + let item = tcx.hir().item(hir::ItemId { def_id }); + let impl_ = match item.kind { + hir::ItemKind::Impl(ref impl_) => impl_, + _ => bug!("{:?} is not an impl: {:?}", def_id, item), + }; + let sp = tcx.sess.source_map().guess_head_span(item.span); + let tr = impl_.of_trait.as_ref().unwrap(); + match traits::orphan_check(tcx, item.def_id.to_def_id()) { + Ok(()) => {} + Err(err) => emit_orphan_check_error( + tcx, + sp, + tr.path.span, + impl_.self_ty.span, + &impl_.generics, + err, + )?, + } + + // In addition to the above rules, we restrict impls of auto traits + // so that they can only be implemented on nominal types, such as structs, + // enums or foreign types. To see why this restriction exists, consider the + // following example (#22978). Imagine that crate A defines an auto trait + // `Foo` and a fn that operates on pairs of types: + // + // ``` + // // Crate A + // auto trait Foo { } + // fn two_foos<A:Foo,B:Foo>(..) { + // one_foo::<(A,B)>(..) + // } + // fn one_foo<T:Foo>(..) { .. } + // ``` + // + // This type-checks fine; in particular the fn + // `two_foos` is able to conclude that `(A,B):Foo` + // because `A:Foo` and `B:Foo`. + // + // Now imagine that crate B comes along and does the following: + // + // ``` + // struct A { } + // struct B { } + // impl Foo for A { } + // impl Foo for B { } + // impl !Send for (A, B) { } + // ``` + // + // This final impl is legal according to the orphan + // rules, but it invalidates the reasoning from + // `two_foos` above. + debug!( + "trait_ref={:?} trait_def_id={:?} trait_is_auto={}", + trait_ref, + trait_def_id, + tcx.trait_is_auto(trait_def_id) + ); + + if tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() { + let self_ty = trait_ref.self_ty(); + let opt_self_def_id = match *self_ty.kind() { + ty::Adt(self_def, _) => Some(self_def.did), + ty::Foreign(did) => Some(did), + _ => None, + }; + + let msg = match opt_self_def_id { + // We only want to permit nominal types, but not *all* nominal types. + // They must be local to the current crate, so that people + // can't do `unsafe impl Send for Rc<SomethingLocal>` or + // `impl !Send for Box<SomethingLocalAndSend>`. + Some(self_def_id) => { + if self_def_id.is_local() { + None + } else { + Some(( format!( - "cross-crate traits with a default impl, like `{}`, can \ - only be implemented for a struct/enum type, not `{}`", - self.tcx.def_path_str(trait_def_id), - self_ty + "cross-crate traits with a default impl, like `{}`, \ + can only be implemented for a struct/enum type \ + defined in the current crate", + tcx.def_path_str(trait_def_id) ), - "can't implement cross-crate trait with a default impl for \ - non-struct/enum type", - )), - }; + "can't implement cross-crate trait for type in another crate", + )) + } + } + _ => Some(( + format!( + "cross-crate traits with a default impl, like `{}`, can \ + only be implemented for a struct/enum type, not `{}`", + tcx.def_path_str(trait_def_id), + self_ty + ), + "can't implement cross-crate trait with a default impl for \ + non-struct/enum type", + )), + }; - if let Some((msg, label)) = msg { - struct_span_err!(self.tcx.sess, sp, E0321, "{}", msg) - .span_label(sp, label) - .emit(); - return; + if let Some((msg, label)) = msg { + struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit(); + return Err(ErrorReported); + } + } + + if let ty::Opaque(def_id, _) = *trait_ref.self_ty().kind() { + tcx.sess + .struct_span_err(sp, "cannot implement trait on type alias impl trait") + .span_note(tcx.def_span(def_id), "type alias impl trait defined here") + .emit(); + return Err(ErrorReported); + } + + Ok(()) +} + +fn emit_orphan_check_error( + tcx: TyCtxt<'tcx>, + sp: Span, + trait_span: Span, + self_ty_span: Span, + generics: &hir::Generics<'tcx>, + err: traits::OrphanCheckErr<'tcx>, +) -> Result<!, ErrorReported> { + match err { + traits::OrphanCheckErr::NonLocalInputType(tys) => { + let mut err = struct_span_err!( + tcx.sess, + sp, + E0117, + "only traits defined in the current crate can be implemented for \ + arbitrary types" + ); + err.span_label(sp, "impl doesn't use only types from inside the current crate"); + for (ty, is_target_ty) in &tys { + let mut ty = *ty; + tcx.infer_ctxt().enter(|infcx| { + // Remove the lifetimes unnecessary for this error. + ty = infcx.freshen(ty); + }); + ty = match ty.kind() { + // Remove the type arguments from the output, as they are not relevant. + // You can think of this as the reverse of `resolve_vars_if_possible`. + // That way if we had `Vec<MyType>`, we will properly attribute the + // problem to `Vec<T>` and avoid confusing the user if they were to see + // `MyType` in the error. + ty::Adt(def, _) => tcx.mk_adt(def, ty::List::empty()), + _ => ty, + }; + let this = "this".to_string(); + let (ty, postfix) = match &ty.kind() { + ty::Slice(_) => (this, " because slices are always foreign"), + ty::Array(..) => (this, " because arrays are always foreign"), + ty::Tuple(..) => (this, " because tuples are always foreign"), + _ => (format!("`{}`", ty), ""), + }; + let msg = format!("{} is not defined in the current crate{}", ty, postfix); + if *is_target_ty { + // Point at `D<A>` in `impl<A, B> for C<B> in D<A>` + err.span_label(self_ty_span, &msg); + } else { + // Point at `C<B>` in `impl<A, B> for C<B> in D<A>` + err.span_label(trait_span, &msg); + } + } + err.note("define and implement a trait or new type instead"); + err.emit() + } + traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => { + let mut sp = sp; + for param in generics.params { + if param.name.ident().to_string() == param_ty.to_string() { + sp = param.span; } } - if let ty::Opaque(def_id, _) = *trait_ref.self_ty().kind() { - self.tcx - .sess - .struct_span_err(sp, "cannot implement trait on type alias impl trait") - .span_note(self.tcx.def_span(def_id), "type alias impl trait defined here") - .emit(); + match local_type { + Some(local_type) => struct_span_err!( + tcx.sess, + sp, + E0210, + "type parameter `{}` must be covered by another type \ + when it appears before the first local type (`{}`)", + param_ty, + local_type + ) + .span_label( + sp, + format!( + "type parameter `{}` must be covered by another type \ + when it appears before the first local type (`{}`)", + param_ty, local_type + ), + ) + .note( + "implementing a foreign trait is only possible if at \ + least one of the types for which it is implemented is local, \ + and no uncovered type parameters appear before that first \ + local type", + ) + .note( + "in this case, 'before' refers to the following order: \ + `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \ + where `T0` is the first and `Tn` is the last", + ) + .emit(), + None => struct_span_err!( + tcx.sess, + sp, + E0210, + "type parameter `{}` must be used as the type parameter for some \ + local type (e.g., `MyStruct<{}>`)", + param_ty, + param_ty + ) + .span_label( + sp, + format!( + "type parameter `{}` must be used as the type parameter for some \ + local type", + param_ty, + ), + ) + .note( + "implementing a foreign trait is only possible if at \ + least one of the types for which it is implemented is local", + ) + .note( + "only traits defined in the current crate can be \ + implemented for a type parameter", + ) + .emit(), } } } - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} - - fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {} + Err(ErrorReported) }
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index df7f2ae..b9db8a6 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs
@@ -28,7 +28,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::weak_lang_items; @@ -668,6 +668,7 @@ }) .flat_map(|b| predicates_from_bound(self, ty, b)); + let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id(); let from_where_clauses = ast_generics .where_clause .predicates @@ -677,7 +678,7 @@ _ => None, }) .flat_map(|bp| { - let bt = if is_param(self.tcx, bp.bounded_ty, param_id) { + let bt = if bp.is_param_bound(param_def_id) { Some(ty) } else if !only_self_bounds.0 { Some(self.to_ty(bp.bounded_ty)) @@ -714,23 +715,6 @@ } } -/// Tests whether this is the AST for a reference to the type -/// parameter with ID `param_id`. We use this so as to avoid running -/// `ast_ty_to_ty`, because we want to avoid triggering an all-out -/// conversion of the type to avoid inducing unnecessary cycles. -fn is_param(tcx: TyCtxt<'_>, ast_ty: &hir::Ty<'_>, param_id: hir::HirId) -> bool { - if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ast_ty.kind { - match path.res { - Res::SelfTy(Some(def_id), None) | Res::Def(DefKind::TyParam, def_id) => { - def_id == tcx.hir().local_def_id(param_id).to_def_id() - } - _ => false, - } - } else { - false - } -} - fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { let it = tcx.hir().item(item_id); debug!("convert: item {} with id {}", it.ident, it.hir_id()); @@ -1494,13 +1478,15 @@ { Some(parent_def_id.to_def_id()) } - + Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => { + Some(tcx.typeck_root_def_id(def_id)) + } _ => None, } } } Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { - Some(tcx.closure_base_def_id(def_id)) + Some(tcx.typeck_root_def_id(def_id)) } Node::Item(item) => match item.kind { ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => { @@ -1557,7 +1543,7 @@ kind: ty::GenericParamDefKind::Type { has_default: false, object_lifetime_default: rl::Set1::Empty, - synthetic: None, + synthetic: false, }, }); @@ -1687,11 +1673,29 @@ kind: ty::GenericParamDefKind::Type { has_default: false, object_lifetime_default: rl::Set1::Empty, - synthetic: None, + synthetic: false, }, })); } + // provide junk type parameter defs for const blocks. + if let Node::AnonConst(_) = node { + let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node { + params.push(ty::GenericParamDef { + index: type_start, + name: Symbol::intern("<const_ty>"), + def_id, + pure_wrt_drop: false, + kind: ty::GenericParamDefKind::Type { + has_default: false, + object_lifetime_default: rl::Set1::Empty, + synthetic: false, + }, + }); + } + } + let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect(); ty::Generics { @@ -1986,16 +1990,12 @@ // prove that the trait applies to the types that were // used, and adding the predicate into this list ensures // that this is done. - let mut span = tcx.def_span(def_id); - if tcx.sess.source_map().is_local_span(span) { - // `guess_head_span` reads the actual source file from - // disk to try to determine the 'head' snippet of the span. - // Don't do this for a span that comes from a file outside - // of our crate, since this would make our query output - // (and overall crate metadata) dependent on the - // *current* state of an external file. - span = tcx.sess.source_map().guess_head_span(span); - } + // + // We use a DUMMY_SP here as a way to signal trait bounds that come + // from the trait itself that *shouldn't* be shown as the source of + // an obligation and instead be skipped. Otherwise we'd use + // `tcx.def_span(def_id);` + let span = rustc_span::DUMMY_SP; result.predicates = tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( ty::TraitRef::identity(tcx, def_id).without_const().to_predicate(tcx), @@ -2861,14 +2861,6 @@ } else if attr.has_name(sym::link_name) { codegen_fn_attrs.link_name = attr.value_str(); } else if attr.has_name(sym::link_ordinal) { - if link_ordinal_span.is_some() { - tcx.sess - .struct_span_err( - attr.span, - "multiple `link_ordinal` attributes on a single definition", - ) - .emit(); - } link_ordinal_span = Some(attr.span); if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { codegen_fn_attrs.link_ordinal = ordinal; @@ -2879,6 +2871,8 @@ for item in list.iter() { if item.has_name(sym::address) { codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS; + } else if item.has_name(sym::cfi) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI; } else if item.has_name(sym::memory) { codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; } else if item.has_name(sym::thread) {
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index cee3679..04a68250 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -292,7 +292,8 @@ // Getting this wrong can lead to ICE and unsoundness, so we assert it here. for arg in substs.iter() { let allowed_flags = ty::TypeFlags::MAY_NEED_DEFAULT_CONST_SUBSTS - | ty::TypeFlags::STILL_FURTHER_SPECIALIZABLE; + | ty::TypeFlags::STILL_FURTHER_SPECIALIZABLE + | ty::TypeFlags::HAS_ERROR; assert!(!arg.has_type_flags(!allowed_flags)); } substs @@ -493,7 +494,8 @@ Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. }) if anon_const.hir_id == hir_id => { - tcx.typeck(def_id).node_type(anon_const.hir_id) + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + substs.as_inline_const().ty() } Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) @@ -771,7 +773,7 @@ } else { err.span_note( tcx.hir().body(body_id).value.span, - &format!("however, the inferred type `{}` cannot be named", ty.to_string()), + &format!("however, the inferred type `{}` cannot be named", ty), ); } } @@ -795,7 +797,7 @@ } else { diag.span_note( tcx.hir().body(body_id).value.span, - &format!("however, the inferred type `{}` cannot be named", ty.to_string()), + &format!("however, the inferred type `{}` cannot be named", ty), ); } }
diff --git a/compiler/rustc_typeck/src/hir_wf_check.rs b/compiler/rustc_typeck/src/hir_wf_check.rs index 39bcf89..a49eda6 100644 --- a/compiler/rustc_typeck/src/hir_wf_check.rs +++ b/compiler/rustc_typeck/src/hir_wf_check.rs
@@ -88,7 +88,8 @@ ), ); - if let Err(errors) = fulfill.select_all_or_error(&infcx) { + let errors = fulfill.select_all_or_error(&infcx); + if !errors.is_empty() { tracing::debug!("Wf-check got errors for {:?}: {:?}", ty, errors); for error in errors { if error.obligation.predicate == self.predicate {
diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index f4bb576..4fb422c 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
@@ -382,6 +382,7 @@ ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: ty::BoundConstness::NotConst, + polarity: _, }) => { if !matches!( trait_predicate_kind(tcx, predicate), @@ -413,6 +414,7 @@ ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: ty::BoundConstness::NotConst, + polarity: _, }) => Some(tcx.trait_def(trait_ref.def_id).specialization_kind), ty::PredicateKind::Trait(_) | ty::PredicateKind::RegionOutlives(_)
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 971776c..0881cf0 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs
@@ -58,17 +58,19 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] -#![feature(format_args_capture)] +#![cfg_attr(bootstrap, feature(format_args_capture))] #![feature(if_let_guard)] #![feature(in_band_lifetimes)] #![feature(is_sorted)] #![feature(iter_zip)] +#![feature(let_else)] #![feature(min_specialization)] #![feature(nll)] #![feature(try_blocks)] #![feature(never_type)] #![feature(slice_partition_dedup)] #![feature(control_flow_enum)] +#![feature(hash_drain_filter)] #![recursion_limit = "256"] #[macro_use] @@ -155,10 +157,10 @@ } } - match fulfill_cx.select_all_or_error(infcx) { - Ok(()) => true, - Err(errors) => { - infcx.report_fulfillment_errors(&errors, None, false); + match fulfill_cx.select_all_or_error(infcx).as_slice() { + [] => true, + errors => { + infcx.report_fulfillment_errors(errors, None, false); false } } @@ -350,8 +352,9 @@ term_id, cause, ); - if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) { - infcx.report_fulfillment_errors(&err, None, false); + let errors = fulfillment_cx.select_all_or_error(&infcx); + if !errors.is_empty() { + infcx.report_fulfillment_errors(&errors, None, false); error = true; } });
diff --git a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs index 4ab5fe2..91727d5 100644 --- a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs +++ b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs
@@ -83,7 +83,8 @@ // variables. Process these constraints. let mut fulfill_cx = FulfillmentContext::new(); fulfill_cx.register_predicate_obligations(self, result.obligations); - if fulfill_cx.select_all_or_error(self).is_err() { + let errors = fulfill_cx.select_all_or_error(self); + if !errors.is_empty() { self.tcx.sess.delay_span_bug( span, "implied_outlives_bounds failed to solve obligations from instantiation",
diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs index 8d3862f..a1c2945 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
@@ -300,7 +300,7 @@ hir::TyKind::Path(hir::QPath::Resolved( None, hir::Path { res: hir::def::Res::Def(_, id), .. }, - )) if *id == def_id => true, + )) => *id == def_id, _ => false, }) })
diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs index 1c8ac10..33c27ce 100644 --- a/compiler/rustc_typeck/src/variance/constraints.rs +++ b/compiler/rustc_typeck/src/variance/constraints.rs
@@ -223,8 +223,8 @@ self.add_constraints_from_region(current, lt, variance_i) } GenericArgKind::Type(ty) => self.add_constraints_from_ty(current, ty, variance_i), - GenericArgKind::Const(_) => { - // Consts impose no constraints. + GenericArgKind::Const(val) => { + self.add_constraints_from_const(current, val, variance_i) } } } @@ -263,7 +263,8 @@ self.add_constraints_from_mt(current, &ty::TypeAndMut { ty, mutbl }, variance); } - ty::Array(typ, _) => { + ty::Array(typ, len) => { + self.add_constraints_from_const(current, len, variance); self.add_constraints_from_ty(current, typ, variance); } @@ -385,13 +386,32 @@ self.add_constraints_from_region(current, lt, variance_i) } GenericArgKind::Type(ty) => self.add_constraints_from_ty(current, ty, variance_i), - GenericArgKind::Const(_) => { - // Consts impose no constraints. + GenericArgKind::Const(val) => { + self.add_constraints_from_const(current, val, variance) } } } } + /// Adds constraints appropriate for a const expression `val` + /// in a context with ambient variance `variance` + fn add_constraints_from_const( + &mut self, + current: &CurrentItem, + val: &ty::Const<'tcx>, + variance: VarianceTermPtr<'a>, + ) { + debug!("add_constraints_from_const(val={:?}, variance={:?})", val, variance); + + match &val.val { + ty::ConstKind::Unevaluated(uv) => { + let substs = uv.substs(self.tcx()); + self.add_constraints_from_invariant_substs(current, substs, variance); + } + _ => {} + } + } + /// Adds constraints appropriate for a function with signature /// `sig` appearing in a context with ambient variance `variance` fn add_constraints_from_sig(