diff options
Diffstat (limited to 'compiler')
494 files changed, 26963 insertions, 11585 deletions
diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 4340d50f845..6e6c0c71a1f 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -4,10 +4,6 @@ name = "rustc-main" version = "0.0.0" edition = '2018' -[[bin]] -name = "rustc_binary" -path = "src/main.rs" - [dependencies] rustc_driver = { path = "../rustc_driver" } @@ -23,3 +19,4 @@ features = ['unprefixed_malloc_on_supported_platforms'] [features] jemalloc = ['jemalloc-sys'] llvm = ['rustc_driver/llvm'] +max_level_info = ['rustc_driver/max_level_info'] diff --git a/compiler/rustc_apfloat/src/lib.rs b/compiler/rustc_apfloat/src/lib.rs index ba3adc4a135..4a845fcb691 100644 --- a/compiler/rustc_apfloat/src/lib.rs +++ b/compiler/rustc_apfloat/src/lib.rs @@ -30,7 +30,7 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![no_std] #![forbid(unsafe_code)] #![feature(nll)] diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 5e6a0340d12..b4bf31b1aba 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -8,31 +8,26 @@ //! This crate implements several kinds of arena. #![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", + html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(no_crate_inject, attr(deny(warnings))) )] -#![feature(core_intrinsics)] #![feature(dropck_eyepatch)] -#![feature(raw_vec_internals)] +#![feature(new_uninit)] +#![feature(maybe_uninit_slice)] #![cfg_attr(test, feature(test))] #![allow(deprecated)] -extern crate alloc; - use rustc_data_structures::cold_path; use smallvec::SmallVec; use std::alloc::Layout; use std::cell::{Cell, RefCell}; use std::cmp; -use std::intrinsics; use std::marker::{PhantomData, Send}; -use std::mem; +use std::mem::{self, MaybeUninit}; use std::ptr; use std::slice; -use alloc::raw_vec::RawVec; - /// An arena that can hold objects of only one type. pub struct TypedArena<T> { /// A pointer to the next object to be allocated. @@ -52,7 +47,7 @@ pub struct TypedArena<T> { struct TypedArenaChunk<T> { /// The raw storage for the arena chunk. - storage: RawVec<T>, + storage: Box<[MaybeUninit<T>]>, /// The number of valid entries in the chunk. entries: usize, } @@ -60,7 +55,7 @@ struct TypedArenaChunk<T> { impl<T> TypedArenaChunk<T> { #[inline] unsafe fn new(capacity: usize) -> TypedArenaChunk<T> { - TypedArenaChunk { storage: RawVec::with_capacity(capacity), entries: 0 } + TypedArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 } } /// Destroys this arena chunk. @@ -69,30 +64,25 @@ impl<T> TypedArenaChunk<T> { // The branch on needs_drop() is an -O1 performance optimization. // Without the branch, dropping TypedArena<u8> takes linear time. if mem::needs_drop::<T>() { - let mut start = self.start(); - // Destroy all allocated objects. - for _ in 0..len { - ptr::drop_in_place(start); - start = start.offset(1); - } + ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut self.storage[..len])); } } // Returns a pointer to the first allocated object. #[inline] - fn start(&self) -> *mut T { - self.storage.ptr() + fn start(&mut self) -> *mut T { + MaybeUninit::slice_as_mut_ptr(&mut self.storage) } // Returns a pointer to the end of the allocated space. #[inline] - fn end(&self) -> *mut T { + fn end(&mut self) -> *mut T { unsafe { if mem::size_of::<T>() == 0 { // A pointer as large as possible for zero-sized elements. !0 as *mut T } else { - self.start().add(self.storage.capacity()) + self.start().add(self.storage.len()) } } } @@ -130,7 +120,7 @@ impl<T> TypedArena<T> { unsafe { if mem::size_of::<T>() == 0 { - self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T); + self.ptr.set((self.ptr.get() as *mut u8).wrapping_offset(1) as *mut T); let ptr = mem::align_of::<T>() as *mut T; // Don't drop the object. This `write` is equivalent to `forget`. ptr::write(ptr, object); @@ -226,10 +216,10 @@ impl<T> TypedArena<T> { let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize; last_chunk.entries = used_bytes / mem::size_of::<T>(); - // If the previous chunk's capacity is less than HUGE_PAGE + // If the previous chunk's len is less than HUGE_PAGE // bytes, then this chunk will be least double the previous // chunk's size. - new_cap = last_chunk.storage.capacity(); + new_cap = last_chunk.storage.len(); if new_cap < HUGE_PAGE / elem_size { new_cap = new_cap.checked_mul(2).unwrap(); } @@ -239,7 +229,7 @@ impl<T> TypedArena<T> { // Also ensure that this chunk can fit `additional`. new_cap = cmp::max(additional, new_cap); - let chunk = TypedArenaChunk::<T>::new(new_cap); + let mut chunk = TypedArenaChunk::<T>::new(new_cap); self.ptr.set(chunk.start()); self.end.set(chunk.end()); chunks.push(chunk); @@ -301,7 +291,7 @@ unsafe impl<#[may_dangle] T> Drop for TypedArena<T> { chunk.destroy(chunk.entries); } } - // RawVec handles deallocation of `last_chunk` and `self.chunks`. + // Box handles deallocation of `last_chunk` and `self.chunks`. } } } @@ -309,11 +299,13 @@ unsafe impl<#[may_dangle] T> Drop for TypedArena<T> { unsafe impl<T: Send> Send for TypedArena<T> {} pub struct DroplessArena { - /// A pointer to the next object to be allocated. - ptr: Cell<*mut u8>, + /// A pointer to the start of the free space. + start: Cell<*mut u8>, - /// A pointer to the end of the allocated area. When this pointer is - /// reached, a new chunk is allocated. + /// A pointer to the end of free space. + /// + /// The allocation proceeds from the end of the chunk towards the start. + /// When this pointer crosses the start pointer, a new chunk is allocated. end: Cell<*mut u8>, /// A vector of arena chunks. @@ -326,7 +318,7 @@ impl Default for DroplessArena { #[inline] fn default() -> DroplessArena { DroplessArena { - ptr: Cell::new(ptr::null_mut()), + start: Cell::new(ptr::null_mut()), end: Cell::new(ptr::null_mut()), chunks: Default::default(), } @@ -344,10 +336,10 @@ impl DroplessArena { // There is no need to update `last_chunk.entries` because that // field isn't used by `DroplessArena`. - // If the previous chunk's capacity is less than HUGE_PAGE + // If the previous chunk's len is less than HUGE_PAGE // bytes, then this chunk will be least double the previous // chunk's size. - new_cap = last_chunk.storage.capacity(); + new_cap = last_chunk.storage.len(); if new_cap < HUGE_PAGE { new_cap = new_cap.checked_mul(2).unwrap(); } @@ -357,8 +349,8 @@ impl DroplessArena { // Also ensure that this chunk can fit `additional`. new_cap = cmp::max(additional, new_cap); - let chunk = TypedArenaChunk::<u8>::new(new_cap); - self.ptr.set(chunk.start()); + let mut chunk = TypedArenaChunk::<u8>::new(new_cap); + self.start.set(chunk.start()); self.end.set(chunk.end()); chunks.push(chunk); } @@ -369,24 +361,17 @@ impl DroplessArena { /// request. #[inline] fn alloc_raw_without_grow(&self, layout: Layout) -> Option<*mut u8> { - let ptr = self.ptr.get() as usize; + let start = self.start.get() as usize; let end = self.end.get() as usize; + let align = layout.align(); let bytes = layout.size(); - // The allocation request fits into the current chunk iff: - // - // let aligned = align_to(ptr, align); - // ptr <= aligned && aligned + bytes <= end - // - // Except that we work with fixed width integers and need to be careful - // about potential overflow in the calcuation. If the overflow does - // happen, then we definitely don't have enough free and need to grow - // the arena. - let aligned = ptr.checked_add(align - 1)? & !(align - 1); - let new_ptr = aligned.checked_add(bytes)?; - if new_ptr <= end { - self.ptr.set(new_ptr as *mut u8); - Some(aligned as *mut u8) + + let new_end = end.checked_sub(bytes)? & !(align - 1); + if start <= new_end { + let new_end = new_end as *mut u8; + self.end.set(new_end); + Some(new_end) } else { None } diff --git a/compiler/rustc_arena/src/tests.rs b/compiler/rustc_arena/src/tests.rs index 8e63bdf5458..e8a1f2db1a1 100644 --- a/compiler/rustc_arena/src/tests.rs +++ b/compiler/rustc_arena/src/tests.rs @@ -121,6 +121,17 @@ pub fn bench_typed_arena_clear(b: &mut Bencher) { }) } +#[bench] +pub fn bench_typed_arena_clear_100(b: &mut Bencher) { + let mut arena = TypedArena::default(); + b.iter(|| { + for _ in 0..100 { + arena.alloc(Point { x: 1, y: 2, z: 3 }); + } + arena.clear(); + }) +} + // Drop tests struct DropCounter<'a> { diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 127a53cad2b..95abf552915 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -96,6 +96,7 @@ pub struct Path { /// The segments in the path: the things separated by `::`. /// Global paths begin with `kw::PathRoot`. pub segments: Vec<PathSegment>, + pub tokens: Option<TokenStream>, } impl PartialEq<Symbol> for Path { @@ -117,7 +118,7 @@ impl Path { // Convert a span and an identifier to the corresponding // one-segment path. pub fn from_ident(ident: Ident) -> Path { - Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span } + Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None } } pub fn is_global(&self) -> bool { @@ -540,6 +541,7 @@ pub struct Block { /// Distinguishes between `unsafe { ... }` and `{ ... }`. pub rules: BlockCheckMode, pub span: Span, + pub tokens: Option<TokenStream>, } /// A match pattern. @@ -586,7 +588,7 @@ impl Pat { _ => return None, }; - Some(P(Ty { kind, id: self.id, span: self.span })) + Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) } /// Walk top-down and call `it` in each place where a pattern occurs @@ -916,15 +918,20 @@ pub struct Stmt { pub id: NodeId, pub kind: StmtKind, pub span: Span, + pub tokens: Option<TokenStream>, } impl Stmt { pub fn add_trailing_semicolon(mut self) -> Self { self.kind = match self.kind { StmtKind::Expr(expr) => StmtKind::Semi(expr), - StmtKind::MacCall(mac) => StmtKind::MacCall( - mac.map(|(mac, _style, attrs)| (mac, MacStmtStyle::Semicolon, attrs)), - ), + StmtKind::MacCall(mac) => { + StmtKind::MacCall(mac.map(|MacCallStmt { mac, style: _, attrs }| MacCallStmt { + mac, + style: MacStmtStyle::Semicolon, + attrs, + })) + } kind => kind, }; self @@ -958,7 +965,14 @@ pub enum StmtKind { /// Just a trailing semi-colon. Empty, /// Macro. - MacCall(P<(MacCall, MacStmtStyle, AttrVec)>), + MacCall(P<MacCallStmt>), +} + +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct MacCallStmt { + pub mac: MacCall, + pub style: MacStmtStyle, + pub attrs: AttrVec, } #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)] @@ -1057,7 +1071,7 @@ pub struct Expr { // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr, 104); +rustc_data_structures::static_assert_size!(Expr, 112); impl Expr { /// Returns `true` if this expression would be valid somewhere that expects a value; @@ -1157,7 +1171,7 @@ impl Expr { _ => return None, }; - Some(P(Ty { kind, id: self.id, span: self.span })) + Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) } pub fn precedence(&self) -> ExprPrecedence { @@ -1855,6 +1869,7 @@ pub struct Ty { pub id: NodeId, pub kind: TyKind, pub span: Span, + pub tokens: Option<TokenStream>, } #[derive(Clone, Encodable, Decodable, Debug)] @@ -1916,7 +1931,7 @@ pub enum TyKind { impl TyKind { pub fn is_implicit_self(&self) -> bool { - if let TyKind::ImplicitSelf = *self { true } else { false } + matches!(self, TyKind::ImplicitSelf) } pub fn is_unit(&self) -> bool { @@ -2133,7 +2148,7 @@ impl Param { /// Builds a `Param` object from `ExplicitSelf`. pub fn from_self(attrs: AttrVec, eself: ExplicitSelf, eself_ident: Ident) -> Param { let span = eself.span.to(eself_ident.span); - let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span }); + let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span, tokens: None }); let param = |mutbl, ty| Param { attrs, pat: P(Pat { @@ -2156,6 +2171,7 @@ impl Param { id: DUMMY_NODE_ID, kind: TyKind::Rptr(lt, MutTy { ty: infer_ty, mutbl }), span, + tokens: None, }), ), } @@ -2211,7 +2227,7 @@ pub enum Async { impl Async { pub fn is_async(self) -> bool { - if let Async::Yes { .. } = self { true } else { false } + matches!(self, Async::Yes { .. }) } /// In this case this is an `async` return, the `NodeId` for the generated `impl Trait` item. @@ -2278,12 +2294,15 @@ impl FnRetTy { /// Module declaration. /// /// E.g., `mod foo;` or `mod foo { .. }`. -#[derive(Clone, Encodable, Decodable, Debug, Default)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct Mod { /// A span from the first token past `{` to the last token until `}`. /// For `mod foo;`, the inner span ranges from the first token /// to the last token in the external file. pub inner: Span, + /// `unsafe` keyword accepted syntactically for macro DSLs, but not + /// semantically by Rust. + pub unsafety: Unsafe, pub items: Vec<P<Item>>, /// `true` for `mod foo { .. }`; `false` for `mod foo;`. pub inline: bool, @@ -2291,9 +2310,12 @@ pub struct Mod { /// Foreign module declaration. /// -/// E.g., `extern { .. }` or `extern C { .. }`. +/// E.g., `extern { .. }` or `extern "C" { .. }`. #[derive(Clone, Encodable, Decodable, Debug)] pub struct ForeignMod { + /// `unsafe` keyword accepted syntactically for macro DSLs, but not + /// semantically by Rust. + pub unsafety: Unsafe, pub abi: Option<StrLit>, pub items: Vec<P<ForeignItem>>, } @@ -2399,6 +2421,7 @@ impl<D: Decoder> rustc_serialize::Decodable<D> for AttrId { pub struct AttrItem { pub path: Path, pub args: MacArgs, + pub tokens: Option<TokenStream>, } /// A list of attributes. @@ -2468,7 +2491,12 @@ pub enum CrateSugar { JustCrate, } -pub type Visibility = Spanned<VisibilityKind>; +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct Visibility { + pub kind: VisibilityKind, + pub span: Span, + pub tokens: Option<TokenStream>, +} #[derive(Clone, Encodable, Decodable, Debug)] pub enum VisibilityKind { @@ -2480,7 +2508,7 @@ pub enum VisibilityKind { impl VisibilityKind { pub fn is_pub(&self) -> bool { - if let VisibilityKind::Public = *self { true } else { false } + matches!(self, VisibilityKind::Public) } } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index edcbce3e2cf..2782869fb88 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -8,7 +8,7 @@ use crate::ast::{Path, PathSegment}; use crate::mut_visit::visit_clobber; use crate::ptr::P; use crate::token::{self, CommentKind, Token}; -use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint}; +use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; use rustc_index::bit_set::GrowableBitSet; use rustc_span::source_map::{BytePos, Spanned}; @@ -16,7 +16,6 @@ use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; use std::iter; -use std::ops::DerefMut; pub struct MarkedAttrs(GrowableBitSet<AttrId>); @@ -331,7 +330,7 @@ crate fn mk_attr_id() -> AttrId { } pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute { - mk_attr_from_item(style, AttrItem { path, args }, span) + mk_attr_from_item(style, AttrItem { path, args, tokens: None }, span) } pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute { @@ -362,7 +361,7 @@ pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool { } impl MetaItem { - fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> { + fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> { let mut idents = vec![]; let mut last_pos = BytePos(0 as u32); for (i, segment) in self.path.segments.iter().enumerate() { @@ -375,7 +374,7 @@ impl MetaItem { idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into()); last_pos = segment.ident.span.hi(); } - idents.extend(self.kind.token_trees_and_joints(self.span)); + idents.extend(self.kind.token_trees_and_spacings(self.span)); idents } @@ -416,7 +415,7 @@ impl MetaItem { } } let span = span.with_hi(segments.last().unwrap().ident.span.hi()); - Path { span, segments } + Path { span, segments, tokens: None } } Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt { token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span), @@ -448,7 +447,7 @@ impl MetaItemKind { if i > 0 { tts.push(TokenTree::token(token::Comma, span).into()); } - tts.extend(item.token_trees_and_joints()) + tts.extend(item.token_trees_and_spacings()) } MacArgs::Delimited( DelimSpan::from_single(span), @@ -459,7 +458,7 @@ impl MetaItemKind { } } - fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> { + fn token_trees_and_spacings(&self, span: Span) -> Vec<TreeAndSpacing> { match *self { MetaItemKind::Word => vec![], MetaItemKind::NameValue(ref lit) => { @@ -471,7 +470,7 @@ impl MetaItemKind { if i > 0 { tokens.push(TokenTree::token(token::Comma, span).into()); } - tokens.extend(item.token_trees_and_joints()) + tokens.extend(item.token_trees_and_spacings()) } vec![ TokenTree::Delimited( @@ -554,9 +553,9 @@ impl NestedMetaItem { } } - fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> { + fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> { match *self { - NestedMetaItem::MetaItem(ref item) => item.token_trees_and_joints(), + NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(), NestedMetaItem::Literal(ref lit) => vec![lit.token_tree().into()], } } @@ -634,10 +633,7 @@ impl HasAttrs for StmtKind { StmtKind::Local(ref local) => local.attrs(), StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(), StmtKind::Empty | StmtKind::Item(..) => &[], - StmtKind::MacCall(ref mac) => { - let (_, _, ref attrs) = **mac; - attrs.attrs() - } + StmtKind::MacCall(ref mac) => mac.attrs.attrs(), } } @@ -647,8 +643,7 @@ impl HasAttrs for StmtKind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f), StmtKind::Empty | StmtKind::Item(..) => {} StmtKind::MacCall(mac) => { - let (_mac, _style, attrs) = mac.deref_mut(); - attrs.visit_attrs(f); + mac.attrs.visit_attrs(f); } } } diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index b556c1a446b..6e47ff7d740 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -4,18 +4,18 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] -#![feature(bool_to_option)] +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", + test(attr(deny(warnings))) +)] #![feature(box_syntax)] #![feature(const_fn)] // For the `transmute` in `P::new` -#![feature(const_panic)] #![feature(const_fn_transmute)] +#![feature(const_panic)] #![feature(crate_visibility_modifier)] #![feature(label_break_value)] #![feature(nll)] #![feature(or_patterns)] -#![feature(try_trait)] -#![feature(unicode_internals)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 965571aaa54..425ef83b57a 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -14,7 +14,7 @@ use crate::tokenstream::*; use rustc_data_structures::map_in_place::MapInPlace; use rustc_data_structures::sync::Lrc; -use rustc_span::source_map::{respan, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::Ident; use rustc_span::Span; @@ -451,7 +451,7 @@ pub fn noop_visit_ty_constraint<T: MutVisitor>( } pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) { - let Ty { id, kind, span } = ty.deref_mut(); + let Ty { id, kind, span, tokens: _ } = ty.deref_mut(); vis.visit_id(id); match kind { TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never | TyKind::CVarArgs => {} @@ -490,7 +490,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) { } pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis: &mut T) { - let ForeignMod { abi: _, items } = foreign_mod; + let ForeignMod { unsafety: _, abi: _, items } = foreign_mod; items.flat_map_in_place(|item| vis.flat_map_foreign_item(item)); } @@ -513,7 +513,7 @@ pub fn noop_visit_ident<T: MutVisitor>(Ident { name: _, span }: &mut Ident, vis: vis.visit_span(span); } -pub fn noop_visit_path<T: MutVisitor>(Path { segments, span }: &mut Path, vis: &mut T) { +pub fn noop_visit_path<T: MutVisitor>(Path { segments, span, tokens: _ }: &mut Path, vis: &mut T) { vis.visit_span(span); for PathSegment { ident, id, args } in segments { vis.visit_ident(ident); @@ -579,7 +579,7 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) { pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) { let Attribute { kind, id: _, style: _, span } = attr; match kind { - AttrKind::Normal(AttrItem { path, args }) => { + AttrKind::Normal(AttrItem { path, args, tokens: _ }) => { vis.visit_path(path); visit_mac_args(args, vis); } @@ -709,7 +709,7 @@ pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis: token::NtLifetime(ident) => vis.visit_ident(ident), token::NtLiteral(expr) => vis.visit_expr(expr), token::NtMeta(item) => { - let AttrItem { path, args } = item.deref_mut(); + let AttrItem { path, args, tokens: _ } = item.deref_mut(); vis.visit_path(path); visit_mac_args(args, vis); } @@ -871,7 +871,7 @@ pub fn noop_visit_mt<T: MutVisitor>(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mu } pub fn noop_visit_block<T: MutVisitor>(block: &mut P<Block>, vis: &mut T) { - let Block { id, stmts, rules: _, span } = block.deref_mut(); + let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut(); vis.visit_id(id); stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt)); vis.visit_span(span); @@ -970,18 +970,21 @@ pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) { vis.visit_asyncness(asyncness); } -pub fn noop_visit_mod<T: MutVisitor>(Mod { inner, items, inline: _ }: &mut Mod, vis: &mut T) { +pub fn noop_visit_mod<T: MutVisitor>(module: &mut Mod, vis: &mut T) { + let Mod { inner, unsafety: _, items, inline: _ } = module; vis.visit_span(inner); items.flat_map_in_place(|item| vis.flat_map_item(item)); } pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) { visit_clobber(krate, |Crate { module, attrs, span, proc_macros }| { + let item_vis = + Visibility { kind: VisibilityKind::Public, span: span.shrink_to_lo(), tokens: None }; let item = P(Item { ident: Ident::invalid(), attrs, id: DUMMY_NODE_ID, - vis: respan(span.shrink_to_lo(), VisibilityKind::Public), + vis: item_vis, span, kind: ItemKind::Mod(module), tokens: None, @@ -990,7 +993,7 @@ pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) { let len = items.len(); if len == 0 { - let module = Mod { inner: span, items: vec![], inline: true }; + let module = Mod { inner: span, unsafety: Unsafe::No, items: vec![], inline: true }; Crate { module, attrs: vec![], span, proc_macros } } else if len == 1 { let Item { attrs, span, kind, .. } = items.into_iter().next().unwrap().into_inner(); @@ -1283,12 +1286,15 @@ pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Optio } pub fn noop_flat_map_stmt<T: MutVisitor>( - Stmt { kind, mut span, mut id }: Stmt, + Stmt { kind, mut span, mut id, tokens }: Stmt, vis: &mut T, ) -> SmallVec<[Stmt; 1]> { vis.visit_id(&mut id); vis.visit_span(&mut span); - noop_flat_map_stmt_kind(kind, vis).into_iter().map(|kind| Stmt { id, kind, span }).collect() + noop_flat_map_stmt_kind(kind, vis) + .into_iter() + .map(|kind| Stmt { id, kind, span, tokens: tokens.clone() }) + .collect() } pub fn noop_flat_map_stmt_kind<T: MutVisitor>( @@ -1305,7 +1311,7 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>( StmtKind::Semi(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Semi).collect(), StmtKind::Empty => smallvec![StmtKind::Empty], StmtKind::MacCall(mut mac) => { - let (mac_, _semi, attrs) = mac.deref_mut(); + let MacCallStmt { mac: mac_, style: _, attrs } = mac.deref_mut(); vis.visit_mac(mac_); visit_thin_attrs(attrs, vis); smallvec![StmtKind::MacCall(mac)] @@ -1313,13 +1319,13 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>( } } -pub fn noop_visit_vis<T: MutVisitor>(Spanned { node, span }: &mut Visibility, vis: &mut T) { - match node { +pub fn noop_visit_vis<T: MutVisitor>(visibility: &mut Visibility, vis: &mut T) { + match &mut visibility.kind { VisibilityKind::Public | VisibilityKind::Crate(_) | VisibilityKind::Inherited => {} VisibilityKind::Restricted { path, id } => { vis.visit_path(path); vis.visit_id(id); } } - vis.visit_span(span); + vis.visit_span(&mut visibility.span); } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 4a8bf6b4f19..d5b3e87adc3 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -173,6 +173,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool { kw::Move, kw::Return, kw::True, + kw::Try, kw::Unsafe, kw::While, kw::Yield, @@ -251,17 +252,6 @@ pub enum TokenKind { /// similarly to symbols in string literal tokens. DocComment(CommentKind, ast::AttrStyle, Symbol), - // Junk. These carry no data because we don't really care about the data - // they *would* carry, and don't really want to allocate a new ident for - // them. Instead, users could extract that from the associated span. - /// Whitespace. - Whitespace, - /// A comment. - Comment, - Shebang(Symbol), - /// A completely invalid token which should be skipped. - Unknown(Symbol), - Eof, } @@ -331,7 +321,7 @@ impl Token { /// Some token that will be thrown away later. pub fn dummy() -> Self { - Token::new(TokenKind::Whitespace, DUMMY_SP) + Token::new(TokenKind::Question, DUMMY_SP) } /// Recovers a `Token` from an `Ident`. This creates a raw identifier if necessary. @@ -360,7 +350,7 @@ impl Token { pub fn is_op(&self) -> bool { match self.kind { OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..) - | Lifetime(..) | Interpolated(..) | Whitespace | Comment | Shebang(..) | Eof => false, + | Lifetime(..) | Interpolated(..) | Eof => false, _ => true, } } @@ -676,8 +666,7 @@ impl Token { Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) - | Lifetime(..) | Interpolated(..) | DocComment(..) | Whitespace | Comment - | Shebang(..) | Unknown(..) | Eof => return None, + | Lifetime(..) | Interpolated(..) | DocComment(..) | Eof => return None, }; Some(Token::new(kind, self.span.to(joint.span))) @@ -711,7 +700,7 @@ pub enum Nonterminal { // `Nonterminal` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Nonterminal, 40); +rustc_data_structures::static_assert_size!(Nonterminal, 48); #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable)] pub enum NonterminalKind { @@ -821,9 +810,19 @@ impl Nonterminal { if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind { let filename = source_map.span_to_filename(orig_span); if let FileName::Real(RealFileName::Named(path)) = filename { - if (path.ends_with("time-macros-impl/src/lib.rs") - && macro_name == sym::impl_macros) - || (path.ends_with("js-sys/src/lib.rs") && macro_name == sym::arrays) + let matches_prefix = |prefix| { + // Check for a path that ends with 'prefix*/src/lib.rs' + let mut iter = path.components().rev(); + iter.next().and_then(|p| p.as_os_str().to_str()) == Some("lib.rs") + && iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src") + && iter + .next() + .and_then(|p| p.as_os_str().to_str()) + .map_or(false, |p| p.starts_with(prefix)) + }; + + if (macro_name == sym::impl_macros && matches_prefix("time-macros-impl")) + || (macro_name == sym::arrays && matches_prefix("js-sys")) { let snippet = source_map.span_to_snippet(orig_span); if snippet.as_deref() == Ok("$name") { diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 151acddae84..f201f0b5c66 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -83,7 +83,7 @@ impl TokenTree { } pub fn joint(self) -> TokenStream { - TokenStream::new(vec![(self, Joint)]) + TokenStream::new(vec![(self, Spacing::Joint)]) } pub fn token(kind: TokenKind, span: Span) -> TokenTree { @@ -125,22 +125,20 @@ where /// instead of a representation of the abstract syntax tree. /// Today's `TokenTree`s can still contain AST via `token::Interpolated` for back-compat. #[derive(Clone, Debug, Default, Encodable, Decodable)] -pub struct TokenStream(pub Lrc<Vec<TreeAndJoint>>); +pub struct TokenStream(pub Lrc<Vec<TreeAndSpacing>>); -pub type TreeAndJoint = (TokenTree, IsJoint); +pub type TreeAndSpacing = (TokenTree, Spacing); // `TokenStream` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] rustc_data_structures::static_assert_size!(TokenStream, 8); #[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable)] -pub enum IsJoint { +pub enum Spacing { + Alone, Joint, - NonJoint, } -use IsJoint::*; - impl TokenStream { /// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream` /// separating the two arguments with a comma for diagnostic suggestions. @@ -153,7 +151,7 @@ impl TokenStream { let sp = match (&ts, &next) { (_, (TokenTree::Token(Token { kind: token::Comma, .. }), _)) => continue, ( - (TokenTree::Token(token_left), NonJoint), + (TokenTree::Token(token_left), Spacing::Alone), (TokenTree::Token(token_right), _), ) if ((token_left.is_ident() && !token_left.is_reserved_ident()) || token_left.is_lit()) @@ -162,11 +160,11 @@ impl TokenStream { { token_left.span } - ((TokenTree::Delimited(sp, ..), NonJoint), _) => sp.entire(), + ((TokenTree::Delimited(sp, ..), Spacing::Alone), _) => sp.entire(), _ => continue, }; let sp = sp.shrink_to_hi(); - let comma = (TokenTree::token(token::Comma, sp), NonJoint); + let comma = (TokenTree::token(token::Comma, sp), Spacing::Alone); suggestion = Some((pos, comma, sp)); } } @@ -184,19 +182,19 @@ impl TokenStream { impl From<TokenTree> for TokenStream { fn from(tree: TokenTree) -> TokenStream { - TokenStream::new(vec![(tree, NonJoint)]) + TokenStream::new(vec![(tree, Spacing::Alone)]) } } -impl From<TokenTree> for TreeAndJoint { - fn from(tree: TokenTree) -> TreeAndJoint { - (tree, NonJoint) +impl From<TokenTree> for TreeAndSpacing { + fn from(tree: TokenTree) -> TreeAndSpacing { + (tree, Spacing::Alone) } } impl iter::FromIterator<TokenTree> for TokenStream { fn from_iter<I: IntoIterator<Item = TokenTree>>(iter: I) -> Self { - TokenStream::new(iter.into_iter().map(Into::into).collect::<Vec<TreeAndJoint>>()) + TokenStream::new(iter.into_iter().map(Into::into).collect::<Vec<TreeAndSpacing>>()) } } @@ -209,7 +207,7 @@ impl PartialEq<TokenStream> for TokenStream { } impl TokenStream { - pub fn new(streams: Vec<TreeAndJoint>) -> TokenStream { + pub fn new(streams: Vec<TreeAndSpacing>) -> TokenStream { TokenStream(Lrc::new(streams)) } @@ -320,11 +318,11 @@ impl TokenStreamBuilder { // If `self` is not empty and the last tree within the last stream is a // token tree marked with `Joint`... if let Some(TokenStream(ref mut last_stream_lrc)) = self.0.last_mut() { - if let Some((TokenTree::Token(last_token), Joint)) = last_stream_lrc.last() { + if let Some((TokenTree::Token(last_token), Spacing::Joint)) = last_stream_lrc.last() { // ...and `stream` is not empty and the first tree within it is // a token tree... let TokenStream(ref mut stream_lrc) = stream; - if let Some((TokenTree::Token(token), is_joint)) = stream_lrc.first() { + if let Some((TokenTree::Token(token), spacing)) = stream_lrc.first() { // ...and the two tokens can be glued together... if let Some(glued_tok) = last_token.glue(&token) { // ...then do so, by overwriting the last token @@ -337,8 +335,7 @@ impl TokenStreamBuilder { // Overwrite the last token tree with the merged // token. let last_vec_mut = Lrc::make_mut(last_stream_lrc); - *last_vec_mut.last_mut().unwrap() = - (TokenTree::Token(glued_tok), *is_joint); + *last_vec_mut.last_mut().unwrap() = (TokenTree::Token(glued_tok), *spacing); // Remove the first token tree from `stream`. (This // is almost always the only tree in `stream`.) @@ -375,7 +372,7 @@ impl Iterator for Cursor { type Item = TokenTree; fn next(&mut self) -> Option<TokenTree> { - self.next_with_joint().map(|(tree, _)| tree) + self.next_with_spacing().map(|(tree, _)| tree) } } @@ -384,7 +381,7 @@ impl Cursor { Cursor { stream, index: 0 } } - pub fn next_with_joint(&mut self) -> Option<TreeAndJoint> { + pub fn next_with_spacing(&mut self) -> Option<TreeAndSpacing> { if self.index < self.stream.len() { self.index += 1; Some(self.stream.0[self.index - 1].clone()) diff --git a/compiler/rustc_ast/src/util/lev_distance.rs b/compiler/rustc_ast/src/util/lev_distance.rs index d4e0e3ba051..754b1f13381 100644 --- a/compiler/rustc_ast/src/util/lev_distance.rs +++ b/compiler/rustc_ast/src/util/lev_distance.rs @@ -103,6 +103,7 @@ fn find_match_by_sorted_words<'a>(iter_names: Vec<&'a Symbol>, lookup: &str) -> fn sort_by_words(name: &str) -> String { let mut split_words: Vec<&str> = name.split('_').collect(); - split_words.sort(); + // We are sorting primitive &strs and can use unstable sort here + split_words.sort_unstable(); split_words.join("_") } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index b65a88cb90e..86fd87f6c42 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -692,7 +692,7 @@ pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) { StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => visitor.visit_expr(expr), StmtKind::Empty => {} StmtKind::MacCall(ref mac) => { - let (ref mac, _, ref attrs) = **mac; + let MacCallStmt { ref mac, style: _, ref attrs } = **mac; visitor.visit_mac(mac); for attr in attrs.iter() { visitor.visit_attribute(attr); @@ -879,7 +879,7 @@ pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) { } pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) { - if let VisibilityKind::Restricted { ref path, id } = vis.node { + if let VisibilityKind::Restricted { ref path, id } = vis.kind { visitor.visit_path(path, id); } } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index df452825bba..c97f80cf09b 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1121,7 +1121,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // features. We check that at least one type is available for // the current target. let reg_class = reg.reg_class(); - let mut required_features = vec![]; + let mut required_features: Vec<&str> = vec![]; for &(_, feature) in reg_class.supported_types(asm_arch) { if let Some(feature) = feature { if self.sess.target_features.contains(&Symbol::intern(feature)) { @@ -1135,7 +1135,8 @@ impl<'hir> LoweringContext<'_, 'hir> { break; } } - required_features.sort(); + // We are sorting primitive strs here and can use unstable sort here + required_features.sort_unstable(); required_features.dedup(); match &required_features[..] { [] => {} diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f3309afec7d..617cacee0e7 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -27,7 +27,7 @@ pub(super) struct ItemLowerer<'a, 'lowering, 'hir> { impl ItemLowerer<'_, '_, '_> { fn with_trait_impl_ref(&mut self, impl_ref: &Option<TraitRef>, f: impl FnOnce(&mut Self)) { let old = self.lctx.is_in_trait_impl; - self.lctx.is_in_trait_impl = if let &None = impl_ref { false } else { true }; + self.lctx.is_in_trait_impl = impl_ref.is_some(); f(self); self.lctx.is_in_trait_impl = old; } @@ -251,7 +251,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::ExternCrate(orig_name) => hir::ItemKind::ExternCrate(orig_name), ItemKind::Use(ref use_tree) => { // Start with an empty prefix. - let prefix = Path { segments: vec![], span: use_tree.span }; + let prefix = Path { segments: vec![], span: use_tree.span, tokens: None }; self.lower_use_tree(use_tree, &prefix, id, vis, ident, attrs) } @@ -488,7 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> { *ident = tree.ident(); // First, apply the prefix to the path. - let mut path = Path { segments, span: path.span }; + let mut path = Path { segments, span: path.span, tokens: None }; // Correctly resolve `self` imports. if path.segments.len() > 1 @@ -540,8 +540,11 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ItemKind::Use(path, hir::UseKind::Single) } UseTreeKind::Glob => { - let path = - self.lower_path(id, &Path { segments, span: path.span }, ParamMode::Explicit); + let path = self.lower_path( + id, + &Path { segments, span: path.span, tokens: None }, + ParamMode::Explicit, + ); hir::ItemKind::Use(path, hir::UseKind::Glob) } UseTreeKind::Nested(ref trees) => { @@ -569,7 +572,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // for that we return the `{}` import (called the // `ListStem`). - let prefix = Path { segments, span: prefix.span.to(path.span) }; + let prefix = Path { segments, span: prefix.span.to(path.span), tokens: None }; // Add all the nested `PathListItem`s to the HIR. for &(ref use_tree, id) in trees { @@ -927,7 +930,7 @@ impl<'hir> LoweringContext<'_, 'hir> { v: &Visibility, explicit_owner: Option<NodeId>, ) -> hir::Visibility<'hir> { - let node = match v.node { + let node = match v.kind { VisibilityKind::Public => hir::VisibilityKind::Public, VisibilityKind::Crate(sugar) => hir::VisibilityKind::Crate(sugar), VisibilityKind::Restricted { ref path, id } => { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 586355fe613..a28d022c661 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -967,6 +967,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { AttrKind::Normal(ref item) => AttrKind::Normal(AttrItem { path: item.path.clone(), args: self.lower_mac_args(&item.args), + tokens: None, }), AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data), }; @@ -1106,6 +1107,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { id: node_id, kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()), span: constraint.span, + tokens: None, }, itctx, ); diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index 7cf3e752c92..9ed6bdc3d6a 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -5,7 +5,7 @@ version = "0.0.0" edition = "2018" [dependencies] -itertools = "0.8" +itertools = "0.9" tracing = "0.1" rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a01dd8c939c..232ee35c4f7 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -198,13 +198,13 @@ impl<'a> AstValidator<'a> { } fn invalid_visibility(&self, vis: &Visibility, note: Option<&str>) { - if let VisibilityKind::Inherited = vis.node { + if let VisibilityKind::Inherited = vis.kind { return; } let mut err = struct_span_err!(self.session, vis.span, E0449, "unnecessary visibility qualifier"); - if vis.node.is_pub() { + if vis.kind.is_pub() { err.span_label(vis.span, "`pub` not permitted here because it's implied"); } if let Some(note) = note { @@ -868,10 +868,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .emit(); } - if !bounds - .iter() - .any(|b| if let GenericBound::Trait(..) = *b { true } else { false }) - { + if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) { self.err_handler().span_err(ty.span, "at least one trait must be specified"); } @@ -990,12 +987,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.error_item_without_body(item.span, "function", msg, " { <body> }"); } } - ItemKind::ForeignMod(_) => { + ItemKind::ForeignMod(ForeignMod { unsafety, .. }) => { let old_item = mem::replace(&mut self.extern_mod, Some(item)); self.invalid_visibility( &item.vis, Some("place qualifiers on individual foreign items instead"), ); + if let Unsafe::Yes(span) = unsafety { + self.err_handler().span_err(span, "extern block cannot be declared unsafe"); + } visit::walk_item(self, item); self.extern_mod = old_item; return; // Avoid visiting again. @@ -1029,7 +1029,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_attribute, &item.attrs); return; } - ItemKind::Mod(Mod { inline, .. }) => { + ItemKind::Mod(Mod { inline, unsafety, .. }) => { + if let Unsafe::Yes(span) = unsafety { + self.err_handler().span_err(span, "module cannot be declared unsafe"); + } // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584). if !inline && !self.session.contains_name(&item.attrs, sym::path) { self.check_mod_file_item_asciionly(item.ident); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 0ee8ef55e61..00d3db73766 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -260,7 +260,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { cfg => doc_cfg masked => doc_masked spotlight => doc_spotlight - alias => doc_alias keyword => doc_keyword ); } @@ -594,7 +593,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } fn visit_vis(&mut self, vis: &'a ast::Visibility) { - if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node { + if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.kind { gate_feature_post!( &self, crate_visibility_modifier, @@ -608,6 +607,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { pub fn check_crate(krate: &ast::Crate, sess: &Session) { maybe_stage_features(sess, krate); + check_incompatible_features(sess); let mut visitor = PostExpansionVisitor { sess, features: &sess.features_untracked() }; let spans = sess.parse_sess.gated_spans.spans.borrow(); @@ -677,3 +677,36 @@ fn maybe_stage_features(sess: &Session, krate: &ast::Crate) { } } } + +fn check_incompatible_features(sess: &Session) { + let features = sess.features_untracked(); + + let declared_features = features + .declared_lang_features + .iter() + .copied() + .map(|(name, span, _)| (name, span)) + .chain(features.declared_lib_features.iter().copied()); + + for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES + .iter() + .filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2)) + { + if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) { + if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2) + { + let spans = vec![f1_span, f2_span]; + sess.struct_span_err( + spans.clone(), + &format!( + "features `{}` and `{}` are incompatible, using them at the same time \ + is not allowed", + f1_name, f2_name + ), + ) + .help("remove one of these features") + .emit(); + } + } + } +} diff --git a/compiler/rustc_ast_pretty/src/pprust.rs b/compiler/rustc_ast_pretty/src/pprust.rs index cb48deb5886..d16b541c699 100644 --- a/compiler/rustc_ast_pretty/src/pprust.rs +++ b/compiler/rustc_ast_pretty/src/pprust.rs @@ -289,10 +289,6 @@ fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>) doc_comment_to_string(comment_kind, attr_style, data) } token::Eof => "<eof>".to_string(), - token::Whitespace => " ".to_string(), - token::Comment => "/* */".to_string(), - token::Shebang(s) => format!("/* shebang: {}*/", s), - token::Unknown(s) => s.to_string(), token::Interpolated(ref nt) => nonterminal_to_string(nt), } @@ -1143,7 +1139,11 @@ impl<'a> State<'a> { self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs); } ast::ItemKind::Mod(ref _mod) => { - self.head(visibility_qualified(&item.vis, "mod")); + self.head(to_string(|s| { + s.print_visibility(&item.vis); + s.print_unsafety(_mod.unsafety); + s.word("mod"); + })); self.print_ident(item.ident); if _mod.inline || self.is_expanded { @@ -1158,7 +1158,10 @@ impl<'a> State<'a> { } } ast::ItemKind::ForeignMod(ref nmod) => { - self.head("extern"); + self.head(to_string(|s| { + s.print_unsafety(nmod.unsafety); + s.word("extern"); + })); if let Some(abi) = nmod.abi { self.print_literal(&abi.as_lit()); self.nbsp(); @@ -1356,7 +1359,7 @@ impl<'a> State<'a> { } crate fn print_visibility(&mut self, vis: &ast::Visibility) { - match vis.node { + match vis.kind { ast::VisibilityKind::Public => self.word_nbsp("pub"), ast::VisibilityKind::Crate(sugar) => match sugar { ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"), @@ -1507,11 +1510,10 @@ impl<'a> State<'a> { self.s.word(";"); } ast::StmtKind::MacCall(ref mac) => { - let (ref mac, style, ref attrs) = **mac; self.space_if_not_bol(); - self.print_outer_attributes(attrs); - self.print_mac(mac); - if style == ast::MacStmtStyle::Semicolon { + self.print_outer_attributes(&mac.attrs); + self.print_mac(&mac.mac); + if mac.style == ast::MacStmtStyle::Semicolon { self.s.word(";"); } } diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs index fdbf3feb900..b1a73a0bf02 100644 --- a/compiler/rustc_ast_pretty/src/pprust/tests.rs +++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs @@ -1,7 +1,6 @@ use super::*; use rustc_ast as ast; -use rustc_span::source_map::respan; use rustc_span::symbol::Ident; use rustc_span::with_default_session_globals; @@ -45,7 +44,11 @@ fn test_variant_to_string() { let var = ast::Variant { ident, - vis: respan(rustc_span::DUMMY_SP, ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: rustc_span::DUMMY_SP, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, attrs: Vec::new(), id: ast::DUMMY_NODE_ID, data: ast::VariantData::Unit(ast::DUMMY_NODE_ID), diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index b8929fe0889..1808eb270ba 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -160,10 +160,10 @@ pub enum StabilityLevel { impl StabilityLevel { pub fn is_unstable(&self) -> bool { - if let StabilityLevel::Unstable { .. } = *self { true } else { false } + matches!(self, StabilityLevel::Unstable { .. }) } pub fn is_stable(&self) -> bool { - if let StabilityLevel::Stable { .. } = *self { true } else { false } + matches!(self, StabilityLevel::Stable { .. }) } } @@ -301,7 +301,7 @@ where .emit(); }; match issue.parse() { - Ok(num) if num == 0 => { + Ok(0) => { emit_diag( "`issue` must not be \"0\", \ use \"none\" instead", diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs index 34e2accc615..5ed8b69d92a 100644 --- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs +++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs @@ -15,7 +15,7 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) - ); let start_span = parser.token.span; - let AttrItem { path, args } = match parser.parse_attr_item() { + let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item() { Ok(ai) => ai, Err(mut err) => { err.emit(); diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs index 8223cdda072..c4d1c6eee31 100644 --- a/compiler/rustc_builtin_macros/src/concat_idents.rs +++ b/compiler/rustc_builtin_macros/src/concat_idents.rs @@ -61,6 +61,7 @@ pub fn expand_concat_idents<'cx>( id: ast::DUMMY_NODE_ID, kind: ast::TyKind::Path(None, ast::Path::from_ident(self.ident)), span: self.ident.span, + tokens: None, })) } } diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 120e859f2b1..d84b3956475 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -133,5 +133,5 @@ fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> as span: sp, attrs: ast::AttrVec::new(), }); - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp } + ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp, tokens: None } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 849e8b136e1..f4924997d1a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -187,7 +187,6 @@ use rustc_ast::{GenericArg, GenericParamKind, VariantData}; use rustc_attr as attr; use rustc_data_structures::map_in_place::MapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::source_map::respan; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -532,7 +531,11 @@ impl<'a> TraitDef<'a> { id: ast::DUMMY_NODE_ID, span: self.span, ident, - vis: respan(self.span.shrink_to_lo(), ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: self.span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, attrs: Vec::new(), kind: ast::AssocItemKind::TyAlias( ast::Defaultness::Final, @@ -933,7 +936,11 @@ impl<'a> MethodDef<'a> { id: ast::DUMMY_NODE_ID, attrs: self.attributes.clone(), span: trait_.span, - vis: respan(trait_lo_sp, ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: trait_lo_sp, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, ident: method_ident, kind: ast::AssocItemKind::Fn(def, sig, fn_generics, Some(body_block)), tokens: None, @@ -1522,7 +1529,7 @@ impl<'a> TraitDef<'a> { } } - let is_tuple = if let ast::VariantData::Tuple(..) = struct_def { true } else { false }; + let is_tuple = matches!(struct_def, ast::VariantData::Tuple(..)); match (just_spans.is_empty(), named_idents.is_empty()) { (false, false) => cx.span_bug( self.span, diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 7e3fd131d44..9c8e0fc2f01 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -75,6 +75,7 @@ fn call_intrinsic( id: ast::DUMMY_NODE_ID, rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated), span, + tokens: None, })) } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 373277f525d..5d6f791f137 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -135,21 +135,52 @@ fn parse_args<'a>( return Err(ecx.struct_span_err(sp, "requires at least a format string argument")); } - let fmtstr = p.parse_expr()?; + let first_token = &p.token; + let fmtstr = match first_token.kind { + token::TokenKind::Literal(token::Lit { + kind: token::LitKind::Str | token::LitKind::StrRaw(_), + .. + }) => { + // If the first token is a string literal, then a format expression + // is constructed from it. + // + // This allows us to properly handle cases when the first comma + // after the format string is mistakenly replaced with any operator, + // which cause the expression parser to eat too much tokens. + p.parse_literal_maybe_minus()? + } + _ => { + // Otherwise, we fall back to the expression parser. + p.parse_expr()? + } + }; + let mut first = true; let mut named = false; while p.token != token::Eof { if !p.eat(&token::Comma) { if first { - // After `format!(""` we always expect *only* a comma... - let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`"); - err.span_label(p.token.span, "expected `,`"); - p.maybe_annotate_with_ascription(&mut err, false); - return Err(err); - } else { - // ...after that delegate to `expect` to also include the other expected tokens. - let _ = p.expect(&token::Comma)?; + p.clear_expected_tokens(); + } + + // `Parser::expect` tries to recover using the + // `Parser::unexpected_try_recover` function. This function is able + // to recover if the expected token is a closing delimiter. + // + // As `,` is not a closing delimiter, it will always return an `Err` + // variant. + let mut err = p.expect(&token::Comma).unwrap_err(); + + match token::TokenKind::Comma.similar_tokens() { + Some(tks) if tks.contains(&p.token.kind) => { + // If a similar token is found, then it may be a typo. We + // consider it as a comma, and continue parsing. + err.emit(); + p.bump(); + } + // Otherwise stop the parsing and return the error. + _ => return Err(err), } } first = false; diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs index 85cf4c42e94..ff81b5eca13 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign.rs @@ -166,14 +166,14 @@ pub mod printf { let cap = self.span.len() + if has_options { 2 } else { 0 }; let mut s = String::with_capacity(cap); - s.push_str("{"); + s.push('{'); if let Some(arg) = self.parameter { write!(s, "{}", arg.checked_sub(1)?).ok()?; } if has_options { - s.push_str(":"); + s.push(':'); let align = if let Some(fill) = fill { s.push_str(fill); @@ -191,11 +191,11 @@ pub mod printf { } if alt { - s.push_str("#"); + s.push('#'); } if zero_fill { - s.push_str("0"); + s.push('0'); } if let Some(width) = width { @@ -203,7 +203,7 @@ pub mod printf { } if let Some(precision) = precision { - s.push_str("."); + s.push('.'); precision.translate(&mut s).ok()?; } @@ -212,7 +212,7 @@ pub mod printf { } } - s.push_str("}"); + s.push('}'); Some(s) } } @@ -518,8 +518,7 @@ pub mod printf { .and_then(|end| end.at_next_cp()) .map(|end| (next.slice_between(end).unwrap(), end)); let end = match end { - Some(("32", end)) => end, - Some(("64", end)) => end, + Some(("32" | "64", end)) => end, _ => next, }; state = Type; diff --git a/compiler/rustc_builtin_macros/src/global_asm.rs b/compiler/rustc_builtin_macros/src/global_asm.rs index 2465f33622e..3689e33be6f 100644 --- a/compiler/rustc_builtin_macros/src/global_asm.rs +++ b/compiler/rustc_builtin_macros/src/global_asm.rs @@ -14,7 +14,6 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_errors::DiagnosticBuilder; use rustc_expand::base::{self, *}; -use rustc_span::source_map::respan; use rustc_span::symbol::Ident; use rustc_span::Span; use smallvec::smallvec; @@ -30,7 +29,11 @@ pub fn expand_global_asm<'cx>( attrs: Vec::new(), id: ast::DUMMY_NODE_ID, kind: ast::ItemKind::GlobalAsm(P(global_asm)), - vis: respan(sp.shrink_to_lo(), ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: sp.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, span: cx.with_def_site_ctxt(sp), tokens: None, })]), diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 87be6d1743a..97cadb913ca 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -1,7 +1,7 @@ //! This crate contains implementations of built-in macros and other code generating facilities //! injecting code into the crate before it is lowered to HIR. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 0c6769906f3..c6ab3faf568 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -98,7 +98,7 @@ pub fn inject( impl<'a> CollectProcMacros<'a> { fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { - if self.is_proc_macro_crate && self.in_root && vis.node.is_pub() { + if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() { self.handler.span_err( sp, "`proc-macro` crate types currently cannot export any items other \ @@ -184,7 +184,7 @@ impl<'a> CollectProcMacros<'a> { Vec::new() }; - if self.in_root && item.vis.node.is_pub() { + if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Derive(ProcMacroDerive { id: item.id, span: item.span, @@ -204,7 +204,7 @@ impl<'a> CollectProcMacros<'a> { } fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) { - if self.in_root && item.vis.node.is_pub() { + if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Def(ProcMacroDef { id: item.id, span: item.span, @@ -223,7 +223,7 @@ impl<'a> CollectProcMacros<'a> { } fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) { - if self.in_root && item.vis.node.is_pub() { + if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Def(ProcMacroDef { id: item.id, span: item.span, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 8e56e80bba2..1de0b32f519 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -7,7 +7,6 @@ use rustc_ast::attr; use rustc_ast_pretty::pprust; use rustc_expand::base::*; use rustc_session::Session; -use rustc_span::source_map::respan; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; @@ -35,7 +34,11 @@ pub fn expand_test_case( let sp = ecx.with_def_site_ctxt(attr_sp); let mut item = anno_item.expect_item(); item = item.map(|mut item| { - item.vis = respan(item.vis.span, ast::VisibilityKind::Public); + item.vis = ast::Visibility { + span: item.vis.span, + kind: ast::VisibilityKind::Public, + tokens: None, + }; item.ident.span = item.ident.span.with_ctxt(sp.ctxt()); item.attrs.push(ecx.attribute(ecx.meta_word(sp, sym::rustc_test_marker))); item @@ -292,7 +295,7 @@ pub fn expand_test_or_bench( ), ); test_const = test_const.map(|mut tc| { - tc.vis.node = ast::VisibilityKind::Public; + tc.vis.kind = ast::VisibilityKind::Public; tc }); diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 0ea60665d67..0a60ca8faaa 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -10,7 +10,6 @@ use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; use rustc_session::Session; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; -use rustc_span::source_map::respan; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::PanicStrategy; @@ -333,7 +332,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> { attrs: vec![main_attr], id: ast::DUMMY_NODE_ID, kind: main, - vis: respan(sp, ast::VisibilityKind::Public), + vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None }, span: sp, tokens: None, }); diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 38f552558c8..04792b334d5 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -25,7 +25,7 @@ rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } -rustc_llvm = { path = "../../src/librustc_llvm" } +rustc_llvm = { path = "../rustc_llvm" } rustc_session = { path = "../rustc_session" } rustc_serialize = { path = "../rustc_serialize" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 7c710a1cb3d..4b2d5907a02 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -346,14 +346,14 @@ fn fat_lto( Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: serialized_bitcode }) } -struct Linker<'a>(&'a mut llvm::Linker<'a>); +crate struct Linker<'a>(&'a mut llvm::Linker<'a>); impl Linker<'a> { - fn new(llmod: &'a llvm::Module) -> Self { + crate fn new(llmod: &'a llvm::Module) -> Self { unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) } } - fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> { + crate fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> { unsafe { if llvm::LLVMRustLinkerAdd( self.0, diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 6f386c1287c..937821e9d4f 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -617,6 +617,31 @@ unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static } } +pub(crate) fn link( + cgcx: &CodegenContext<LlvmCodegenBackend>, + diag_handler: &Handler, + mut modules: Vec<ModuleCodegen<ModuleLlvm>>, +) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> { + use super::lto::{Linker, ModuleBuffer}; + // Sort the modules by name to ensure to ensure deterministic behavior. + modules.sort_by(|a, b| a.name.cmp(&b.name)); + let (first, elements) = + modules.split_first().expect("Bug! modules must contain at least one module."); + + let mut linker = Linker::new(first.module_llvm.llmod()); + for module in elements { + let _timer = + cgcx.prof.generic_activity_with_arg("LLVM_link_module", format!("{:?}", module.name)); + let buffer = ModuleBuffer::new(module.module_llvm.llmod()); + linker.add(&buffer.data()).map_err(|()| { + let msg = format!("failed to serialize module {:?}", module.name); + llvm_err(&diag_handler, &msg) + })?; + } + drop(linker); + Ok(modules.remove(0)) +} + pub(crate) unsafe fn codegen( cgcx: &CodegenContext<LlvmCodegenBackend>, diag_handler: &Handler, diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 6a1b373ef07..f35708b1d09 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -108,7 +108,7 @@ pub fn compile_codegen_unit( // We assume that the cost to run LLVM on a CGU is proportional to // the time we needed for codegenning it. - let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64; + let cost = time_to_codegen.as_nanos() as u64; fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen<ModuleLlvm> { let cgu = tcx.codegen_unit(cgu_name); diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 4ece08f6293..23a3be1a2f2 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -6,7 +6,6 @@ use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; use libc::{c_char, c_uint}; -use rustc_codegen_ssa::base::to_immediate; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; @@ -307,10 +306,10 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { use rustc_ast::UintTy::*; use rustc_middle::ty::{Int, Uint}; - let new_kind = match ty.kind { + let new_kind = match ty.kind() { Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.ptr_width)), Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.ptr_width)), - ref t @ (Uint(_) | Int(_)) => t.clone(), + t @ (Uint(_) | Int(_)) => t.clone(), _ => panic!("tried to get overflow intrinsic for op applied to non-int type"), }; @@ -367,6 +366,20 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { (self.extract_value(res, 0), self.extract_value(res, 1)) } + fn from_immediate(&mut self, val: Self::Value) -> Self::Value { + if self.cx().val_ty(val) == self.cx().type_i1() { + self.zext(val, self.cx().type_i8()) + } else { + val + } + } + fn to_immediate_scalar(&mut self, val: Self::Value, scalar: &abi::Scalar) -> Self::Value { + if scalar.is_bool() { + return self.trunc(val, self.cx().type_i1()); + } + val + } + fn alloca(&mut self, ty: &'ll Type, align: Align) -> &'ll Value { let mut bx = Builder::with_cx(self.cx); bx.position_at_start(unsafe { llvm::LLVMGetFirstBasicBlock(self.llfn()) }); @@ -471,7 +484,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } load }); - OperandValue::Immediate(to_immediate(self, llval, place.layout)) + OperandValue::Immediate(self.to_immediate(llval, place.layout)) } else if let abi::Abi::ScalarPair(ref a, ref b) = place.layout.abi { let b_offset = a.value.size(self).align_to(b.value.align(self).abi); @@ -479,7 +492,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let llptr = self.struct_gep(place.llval, i as u64); let load = self.load(llptr, align); scalar_load_metadata(self, load, scalar); - if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load } + self.to_immediate_scalar(load, scalar) }; OperandValue::Pair( diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 86a5ec59254..6d3582d3027 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -7,12 +7,13 @@ use crate::type_of::LayoutLlvmExt; use crate::value::Value; use libc::c_uint; use rustc_codegen_ssa::traits::*; +use rustc_data_structures::const_cstr; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::Node; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ - read_target_uint, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer, + read_target_uint, Allocation, ErrorHandled, GlobalAlloc, Pointer, }; use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::{self, Instance, Ty}; @@ -22,8 +23,6 @@ use rustc_span::Span; use rustc_target::abi::{AddressSpace, Align, HasDataLayout, LayoutOf, Primitive, Scalar, Size}; use tracing::debug; -use std::ffi::CStr; - pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll Value { let mut llvals = Vec::with_capacity(alloc.relocations().len() + 1); let dl = cx.data_layout(); @@ -85,10 +84,7 @@ pub fn codegen_static_initializer( cx: &CodegenCx<'ll, 'tcx>, def_id: DefId, ) -> Result<(&'ll Value, &'tcx Allocation), ErrorHandled> { - let alloc = match cx.tcx.const_eval_poly(def_id)? { - ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => alloc, - val => bug!("static const eval returned {:#?}", val), - }; + let alloc = cx.tcx.eval_static_initializer(def_id)?; Ok((const_alloc_to_llvm(cx, alloc), alloc)) } @@ -125,7 +121,7 @@ fn check_and_apply_linkage( // extern "C" fn() from being non-null, so we can't just declare a // static and call it a day. Some linkages (like weak) will make it such // that the static actually has a null value. - let llty2 = if let ty::RawPtr(ref mt) = ty.kind { + let llty2 = if let ty::RawPtr(ref mt) = ty.kind() { cx.layout_of(mt.ty).llvm_type(cx) } else { cx.sess().span_fatal( @@ -457,9 +453,9 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { .all(|&byte| byte == 0); let sect_name = if all_bytes_are_zero { - CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_bss\0") + const_cstr!("__DATA,__thread_bss") } else { - CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_data\0") + const_cstr!("__DATA,__thread_data") }; llvm::LLVMSetSection(g, sect_name.as_ptr()); } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 1c51a9df5d8..1696f35563d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -433,6 +433,17 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { llvm::LLVMSetSection(g, section.as_ptr()); } } + + fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function> { + if self.get_declared_value("main").is_none() { + Some(self.declare_cfn("main", fn_type)) + } else { + // If the symbol already exists, it is an error: for example, the user wrote + // #[no_mangle] extern "C" fn main(..) {..} + // instead of #[start] + None + } + } } impl CodegenCx<'b, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 9d92d53775c..868eb74cf09 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -343,7 +343,7 @@ fn fixed_vec_metadata( let (size, align) = cx.size_and_align_of(array_or_slice_type); - let upper_bound = match array_or_slice_type.kind { + let upper_bound = match array_or_slice_type.kind() { ty::Array(_, len) => len.eval_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong, _ => -1, }; @@ -432,7 +432,7 @@ fn subroutine_type_metadata( let signature_metadata: Vec<_> = iter::once( // return type - match signature.output().kind { + match signature.output().kind() { ty::Tuple(ref tys) if tys.is_empty() => None, _ => Some(type_metadata(cx, signature.output(), span)), }, @@ -472,7 +472,7 @@ fn trait_pointer_metadata( // type is assigned the correct name, size, namespace, and source location. // However, it does not describe the trait's methods. - let containing_scope = match trait_type.kind { + let containing_scope = match trait_type.kind() { ty::Dynamic(ref data, ..) => { data.principal_def_id().map(|did| get_namespace_for_item(cx, did)) } @@ -572,7 +572,7 @@ pub fn type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, usage_site_span: Sp debug!("type_metadata: {:?}", t); - let ptr_metadata = |ty: Ty<'tcx>| match ty.kind { + let ptr_metadata = |ty: Ty<'tcx>| match *ty.kind() { ty::Slice(typ) => Ok(vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span)), ty::Str => Ok(vec_slice_metadata(cx, t, cx.tcx.types.u8, unique_type_id, usage_site_span)), ty::Dynamic(..) => Ok(MetadataCreationResult::new( @@ -592,7 +592,7 @@ pub fn type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, usage_site_span: Sp } }; - let MetadataCreationResult { metadata, already_stored_in_typemap } = match t.kind { + let MetadataCreationResult { metadata, already_stored_in_typemap } = match *t.kind() { ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { MetadataCreationResult::new(basic_type_metadata(cx, t), false) } @@ -876,7 +876,7 @@ fn basic_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { // .natvis visualizers (and perhaps other existing native debuggers?) let msvc_like_names = cx.tcx.sess.target.target.options.is_like_msvc; - let (name, encoding) = match t.kind { + let (name, encoding) = match t.kind() { ty::Never => ("!", DW_ATE_unsigned), ty::Tuple(ref elements) if elements.is_empty() => ("()", DW_ATE_unsigned), ty::Bool => ("bool", DW_ATE_boolean), @@ -904,7 +904,7 @@ fn basic_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { return ty_metadata; } - let typedef_name = match t.kind { + let typedef_name = match t.kind() { ty::Int(int_ty) => int_ty.name_str(), ty::Uint(uint_ty) => uint_ty.name_str(), ty::Float(float_ty) => float_ty.name_str(), @@ -1239,7 +1239,7 @@ fn prepare_struct_metadata( ) -> RecursiveTypeDescription<'ll, 'tcx> { let struct_name = compute_debuginfo_type_name(cx.tcx, struct_type, false); - let (struct_def_id, variant) = match struct_type.kind { + let (struct_def_id, variant) = match struct_type.kind() { ty::Adt(def, _) => (def.did, def.non_enum_variant()), _ => bug!("prepare_struct_metadata on a non-ADT"), }; @@ -1373,7 +1373,7 @@ fn prepare_union_metadata( ) -> RecursiveTypeDescription<'ll, 'tcx> { let union_name = compute_debuginfo_type_name(cx.tcx, union_type, false); - let (union_def_id, variant) = match union_type.kind { + let (union_def_id, variant) = match union_type.kind() { ty::Adt(def, _) => (def.did, def.non_enum_variant()), _ => bug!("prepare_union_metadata on a non-ADT"), }; @@ -1457,14 +1457,14 @@ struct EnumMemberDescriptionFactory<'ll, 'tcx> { impl EnumMemberDescriptionFactory<'ll, 'tcx> { fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<MemberDescription<'ll>> { - let generator_variant_info_data = match self.enum_type.kind { + let generator_variant_info_data = match *self.enum_type.kind() { ty::Generator(def_id, ..) => { Some(generator_layout_and_saved_local_names(cx.tcx, def_id)) } _ => None, }; - let variant_info_for = |index: VariantIdx| match self.enum_type.kind { + let variant_info_for = |index: VariantIdx| match *self.enum_type.kind() { ty::Adt(adt, _) => VariantInfo::Adt(&adt.variants[index]), ty::Generator(def_id, _, _) => { let (generator_layout, generator_saved_local_names) = @@ -1486,14 +1486,14 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { } else { type_metadata(cx, self.enum_type, self.span) }; - let flags = match self.enum_type.kind { + let flags = match self.enum_type.kind() { ty::Generator(..) => DIFlags::FlagArtificial, _ => DIFlags::FlagZero, }; match self.layout.variants { Variants::Single { index } => { - if let ty::Adt(adt, _) = &self.enum_type.kind { + if let ty::Adt(adt, _) = self.enum_type.kind() { if adt.variants.is_empty() { return vec![]; } @@ -1942,7 +1942,7 @@ fn prepare_enum_metadata( let tcx = cx.tcx; let enum_name = compute_debuginfo_type_name(tcx, enum_type, false); // FIXME(tmandry): This doesn't seem to have any effect. - let enum_flags = match enum_type.kind { + let enum_flags = match enum_type.kind() { ty::Generator(..) => DIFlags::FlagArtificial, _ => DIFlags::FlagZero, }; @@ -1957,13 +1957,13 @@ fn prepare_enum_metadata( let file_metadata = unknown_file_metadata(cx); let discriminant_type_metadata = |discr: Primitive| { - let enumerators_metadata: Vec<_> = match enum_type.kind { + let enumerators_metadata: Vec<_> = match enum_type.kind() { ty::Adt(def, _) => def .discriminants(tcx) .zip(&def.variants) .map(|((_, discr), v)| { let name = v.ident.as_str(); - let is_unsigned = match discr.ty.kind { + let is_unsigned = match discr.ty.kind() { ty::Int(_) => false, ty::Uint(_) => true, _ => bug!("non integer discriminant"), @@ -2012,7 +2012,7 @@ fn prepare_enum_metadata( type_metadata(cx, discr.to_ty(tcx), rustc_span::DUMMY_SP); let item_name; - let discriminant_name = match enum_type.kind { + let discriminant_name = match enum_type.kind() { ty::Adt(..) => { item_name = tcx.item_name(enum_def_id).as_str(); &*item_name @@ -2105,7 +2105,7 @@ fn prepare_enum_metadata( ); } - let discriminator_name = match &enum_type.kind { + let discriminator_name = match enum_type.kind() { ty::Generator(..) => "__state", _ => "", }; @@ -2328,7 +2328,7 @@ fn set_members_of_composite_type( /// Computes the type parameters for a type, if any, for the given metadata. fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> Option<&'ll DIArray> { - if let ty::Adt(def, substs) = ty.kind { + if let ty::Adt(def, substs) = *ty.kind() { if substs.types().next().is_some() { let generics = cx.tcx.generics_of(def.did); let names = get_parameter_names(cx, generics); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index b414426af8c..7cdd366175d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -361,9 +361,9 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { // already inaccurate due to ABI adjustments (see #42800). signature.extend(fn_abi.args.iter().map(|arg| { let t = arg.layout.ty; - let t = match t.kind { + let t = match t.kind() { ty::Array(ct, _) - if (ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => + if (*ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => { cx.tcx.mk_imm_ptr(ct) } @@ -467,7 +467,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { // Only "class" methods are generally understood by LLVM, // so avoid methods on other types (e.g., `<*mut T>::null`). - match impl_self_ty.kind { + match impl_self_ty.kind() { ty::Adt(def, ..) if !def.is_box() => { // Again, only create type information if full debuginfo is enabled if cx.sess().opts.debuginfo == DebugInfo::Full diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index ec42bd4a039..a3d6882940a 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -51,17 +51,32 @@ fn declare_raw_fn( llfn } -impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> { - fn declare_global(&self, name: &str, ty: &'ll Type) -> &'ll Value { +impl CodegenCx<'ll, 'tcx> { + /// Declare a global value. + /// + /// If there’s a value with the same name already declared, the function will + /// return its Value instead. + pub fn declare_global(&self, name: &str, ty: &'ll Type) -> &'ll Value { debug!("declare_global(name={:?})", name); unsafe { llvm::LLVMRustGetOrInsertGlobal(self.llmod, name.as_ptr().cast(), name.len(), ty) } } - fn declare_cfn(&self, name: &str, fn_type: &'ll Type) -> &'ll Value { + /// Declare a C ABI function. + /// + /// Only use this for foreign function ABIs and glue. For Rust functions use + /// `declare_fn` instead. + /// + /// If there’s a value with the same name already declared, the function will + /// update the declaration and return existing Value instead. + pub fn declare_cfn(&self, name: &str, fn_type: &'ll Type) -> &'ll Value { declare_raw_fn(self, name, llvm::CCallConv, fn_type) } - fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Value { + /// Declare a Rust function. + /// + /// If there’s a value with the same name already declared, the function will + /// update the declaration and return existing Value instead. + pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Value { debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi); let llfn = declare_raw_fn(self, name, fn_abi.llvm_cconv(), fn_abi.llvm_type(self)); @@ -69,7 +84,13 @@ impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> { llfn } - fn define_global(&self, name: &str, ty: &'ll Type) -> Option<&'ll Value> { + /// Declare a global with an intention to define it. + /// + /// Use this function when you intend to define a global. This function will + /// return `None` if the name already has a definition associated with it. In that + /// case an error should be reported to the user, because it usually happens due + /// to user’s fault (e.g., misuse of `#[no_mangle]` or `#[export_name]` attributes). + pub fn define_global(&self, name: &str, ty: &'ll Type) -> Option<&'ll Value> { if self.get_defined_value(name).is_some() { None } else { @@ -77,16 +98,22 @@ impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } - fn define_private_global(&self, ty: &'ll Type) -> &'ll Value { + /// Declare a private global + /// + /// Use this function when you intend to define a global without a name. + pub fn define_private_global(&self, ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMRustInsertPrivateGlobal(self.llmod, ty) } } - fn get_declared_value(&self, name: &str) -> Option<&'ll Value> { + /// Gets declared value by name. + pub fn get_declared_value(&self, name: &str) -> Option<&'ll Value> { debug!("get_declared_value(name={:?})", name); unsafe { llvm::LLVMRustGetNamedValue(self.llmod, name.as_ptr().cast(), name.len()) } } - fn get_defined_value(&self, name: &str) -> Option<&'ll Value> { + /// Gets defined or externally defined (AvailableExternally linkage) value by + /// name. + pub fn get_defined_value(&self, name: &str) -> Option<&'ll Value> { self.get_declared_value(name).and_then(|val| { let declaration = unsafe { llvm::LLVMIsDeclaration(val) != 0 }; if !declaration { Some(val) } else { None } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index c1dfb83b135..7f5b09eac4f 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -7,15 +7,12 @@ use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; use crate::value::Value; -use rustc_ast as ast; -use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh}; +use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh}; use rustc_codegen_ssa::common::span_invalid_monomorphization_error; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; -use rustc_codegen_ssa::glue; -use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; +use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::MemFlags; use rustc_hir as hir; use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; use rustc_middle::ty::{self, Ty}; @@ -71,8 +68,6 @@ fn get_simple_intrinsic(cx: &CodegenCx<'ll, '_>, name: Symbol) -> Option<&'ll Va sym::nearbyintf64 => "llvm.nearbyint.f64", sym::roundf32 => "llvm.round.f32", sym::roundf64 => "llvm.round.f64", - sym::assume => "llvm.assume", - sym::abort => "llvm.trap", _ => return None, }; Some(cx.get_intrinsic(&llvm_name)) @@ -90,7 +85,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { let tcx = self.tcx; let callee_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - let (def_id, substs) = match callee_ty.kind { + let (def_id, substs) = match *callee_ty.kind() { ty::FnDef(def_id, substs) => (def_id, substs), _ => bug!("expected fn item type, found {}", callee_ty), }; @@ -112,9 +107,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None, ), - sym::unreachable => { - return; - } sym::likely => { let expect = self.get_intrinsic(&("llvm.expect.i1")); self.call(expect, &[args[0].immediate(), self.const_bool(true)], None) @@ -137,8 +129,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { let llfn = self.get_intrinsic(&("llvm.debugtrap")); self.call(llfn, &[], None) } - sym::va_start => self.va_start(args[0].immediate()), - sym::va_end => self.va_end(args[0].immediate()), sym::va_copy => { let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy")); self.call(intrinsic, &[args[0].immediate(), args[1].immediate()], None) @@ -169,123 +159,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { _ => bug!("the va_arg intrinsic does not work with non-scalar types"), } } - sym::size_of_val => { - let tp_ty = substs.type_at(0); - if let OperandValue::Pair(_, meta) = args[0].val { - let (llsize, _) = glue::size_and_align_of_dst(self, tp_ty, Some(meta)); - llsize - } else { - self.const_usize(self.size_of(tp_ty).bytes()) - } - } - sym::min_align_of_val => { - let tp_ty = substs.type_at(0); - if let OperandValue::Pair(_, meta) = args[0].val { - let (_, llalign) = glue::size_and_align_of_dst(self, tp_ty, Some(meta)); - llalign - } else { - self.const_usize(self.align_of(tp_ty).bytes()) - } - } - sym::size_of - | sym::pref_align_of - | sym::min_align_of - | sym::needs_drop - | sym::type_id - | sym::type_name - | sym::variant_count => { - let value = self - .tcx - .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None) - .unwrap(); - OperandRef::from_const(self, value, ret_ty).immediate_or_packed_pair(self) - } - // Effectively no-op - sym::forget => { - return; - } - sym::offset => { - let ptr = args[0].immediate(); - let offset = args[1].immediate(); - self.inbounds_gep(ptr, &[offset]) - } - sym::arith_offset => { - let ptr = args[0].immediate(); - let offset = args[1].immediate(); - self.gep(ptr, &[offset]) - } - sym::copy_nonoverlapping => { - copy_intrinsic( - self, - false, - false, - substs.type_at(0), - args[1].immediate(), - args[0].immediate(), - args[2].immediate(), - ); - return; - } - sym::copy => { - copy_intrinsic( - self, - true, - false, - substs.type_at(0), - args[1].immediate(), - args[0].immediate(), - args[2].immediate(), - ); - return; - } - sym::write_bytes => { - memset_intrinsic( - self, - false, - substs.type_at(0), - args[0].immediate(), - args[1].immediate(), - args[2].immediate(), - ); - return; - } - - sym::volatile_copy_nonoverlapping_memory => { - copy_intrinsic( - self, - false, - true, - substs.type_at(0), - args[0].immediate(), - args[1].immediate(), - args[2].immediate(), - ); - return; - } - sym::volatile_copy_memory => { - copy_intrinsic( - self, - true, - true, - substs.type_at(0), - args[0].immediate(), - args[1].immediate(), - args[2].immediate(), - ); - return; - } - sym::volatile_set_memory => { - memset_intrinsic( - self, - true, - substs.type_at(0), - args[0].immediate(), - args[1].immediate(), - args[2].immediate(), - ); - return; - } sym::volatile_load | sym::unaligned_volatile_load => { let tp_ty = substs.type_at(0); let mut ptr = args[0].immediate(); @@ -301,7 +175,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMSetAlignment(load, align); } - to_immediate(self, load, self.layout_of(tp_ty)) + self.to_immediate(load, self.layout_of(tp_ty)) } sym::volatile_store => { let dst = args[0].deref(self.cx()); @@ -343,20 +217,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { | sym::ctpop | sym::bswap | sym::bitreverse - | sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow - | sym::wrapping_add - | sym::wrapping_sub - | sym::wrapping_mul - | sym::unchecked_div - | sym::unchecked_rem - | sym::unchecked_shl - | sym::unchecked_shr - | sym::unchecked_add - | sym::unchecked_sub - | sym::unchecked_mul - | sym::exact_div | sym::rotate_left | sym::rotate_right | sym::saturating_add @@ -396,84 +256,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { &[args[0].immediate()], None, ), - sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow => { - let intrinsic = format!( - "llvm.{}{}.with.overflow.i{}", - if signed { 's' } else { 'u' }, - &name_str[..3], - width - ); - let llfn = self.get_intrinsic(&intrinsic); - - // Convert `i1` to a `bool`, and write it to the out parameter - let pair = - self.call(llfn, &[args[0].immediate(), args[1].immediate()], None); - let val = self.extract_value(pair, 0); - let overflow = self.extract_value(pair, 1); - let overflow = self.zext(overflow, self.type_bool()); - - let dest = result.project_field(self, 0); - self.store(val, dest.llval, dest.align); - let dest = result.project_field(self, 1); - self.store(overflow, dest.llval, dest.align); - - return; - } - sym::wrapping_add => self.add(args[0].immediate(), args[1].immediate()), - sym::wrapping_sub => self.sub(args[0].immediate(), args[1].immediate()), - sym::wrapping_mul => self.mul(args[0].immediate(), args[1].immediate()), - sym::exact_div => { - if signed { - self.exactsdiv(args[0].immediate(), args[1].immediate()) - } else { - self.exactudiv(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_div => { - if signed { - self.sdiv(args[0].immediate(), args[1].immediate()) - } else { - self.udiv(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_rem => { - if signed { - self.srem(args[0].immediate(), args[1].immediate()) - } else { - self.urem(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_shl => self.shl(args[0].immediate(), args[1].immediate()), - sym::unchecked_shr => { - if signed { - self.ashr(args[0].immediate(), args[1].immediate()) - } else { - self.lshr(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_add => { - if signed { - self.unchecked_sadd(args[0].immediate(), args[1].immediate()) - } else { - self.unchecked_uadd(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_sub => { - if signed { - self.unchecked_ssub(args[0].immediate(), args[1].immediate()) - } else { - self.unchecked_usub(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_mul => { - if signed { - self.unchecked_smul(args[0].immediate(), args[1].immediate()) - } else { - self.unchecked_umul(args[0].immediate(), args[1].immediate()) - } - } sym::rotate_left | sym::rotate_right => { let is_left = name == sym::rotate_left; let val = args[0].immediate(); @@ -513,75 +295,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } } - sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { - match float_type_width(arg_tys[0]) { - Some(_width) => match name { - sym::fadd_fast => self.fadd_fast(args[0].immediate(), args[1].immediate()), - sym::fsub_fast => self.fsub_fast(args[0].immediate(), args[1].immediate()), - sym::fmul_fast => self.fmul_fast(args[0].immediate(), args[1].immediate()), - sym::fdiv_fast => self.fdiv_fast(args[0].immediate(), args[1].immediate()), - sym::frem_fast => self.frem_fast(args[0].immediate(), args[1].immediate()), - _ => bug!(), - }, - None => { - span_invalid_monomorphization_error( - tcx.sess, - span, - &format!( - "invalid monomorphization of `{}` intrinsic: \ - expected basic float type, found `{}`", - name, arg_tys[0] - ), - ); - return; - } - } - } - - sym::float_to_int_unchecked => { - if float_type_width(arg_tys[0]).is_none() { - span_invalid_monomorphization_error( - tcx.sess, - span, - &format!( - "invalid monomorphization of `float_to_int_unchecked` \ - intrinsic: expected basic float type, \ - found `{}`", - arg_tys[0] - ), - ); - return; - } - let (width, signed) = match int_type_width_signed(ret_ty, self.cx) { - Some(pair) => pair, - None => { - span_invalid_monomorphization_error( - tcx.sess, - span, - &format!( - "invalid monomorphization of `float_to_int_unchecked` \ - intrinsic: expected basic integer type, \ - found `{}`", - ret_ty - ), - ); - return; - } - }; - if signed { - self.fptosi(args[0].immediate(), self.cx.type_ix(width)) - } else { - self.fptoui(args[0].immediate(), self.cx.type_ix(width)) - } - } - - sym::discriminant_value => { - if ret_ty.is_integral() { - args[0].deref(self.cx()).codegen_get_discr(self, ret_ty) - } else { - span_bug!(span, "Invalid discriminant type for `{:?}`", arg_tys[0]) - } - } _ if name_str.starts_with("simd_") => { match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) { @@ -589,174 +302,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { Err(()) => return, } } - // This requires that atomic intrinsics follow a specific naming pattern: - // "atomic_<operation>[_<ordering>]", and no ordering means SeqCst - name if name_str.starts_with("atomic_") => { - use rustc_codegen_ssa::common::AtomicOrdering::*; - use rustc_codegen_ssa::common::{AtomicRmwBinOp, SynchronizationScope}; - - let split: Vec<&str> = name_str.split('_').collect(); - - let is_cxchg = split[1] == "cxchg" || split[1] == "cxchgweak"; - let (order, failorder) = match split.len() { - 2 => (SequentiallyConsistent, SequentiallyConsistent), - 3 => match split[2] { - "unordered" => (Unordered, Unordered), - "relaxed" => (Monotonic, Monotonic), - "acq" => (Acquire, Acquire), - "rel" => (Release, Monotonic), - "acqrel" => (AcquireRelease, Acquire), - "failrelaxed" if is_cxchg => (SequentiallyConsistent, Monotonic), - "failacq" if is_cxchg => (SequentiallyConsistent, Acquire), - _ => self.sess().fatal("unknown ordering in atomic intrinsic"), - }, - 4 => match (split[2], split[3]) { - ("acq", "failrelaxed") if is_cxchg => (Acquire, Monotonic), - ("acqrel", "failrelaxed") if is_cxchg => (AcquireRelease, Monotonic), - _ => self.sess().fatal("unknown ordering in atomic intrinsic"), - }, - _ => self.sess().fatal("Atomic intrinsic not in correct format"), - }; - - let invalid_monomorphization = |ty| { - span_invalid_monomorphization_error( - tcx.sess, - span, - &format!( - "invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", - name, ty - ), - ); - }; - - match split[1] { - "cxchg" | "cxchgweak" => { - let ty = substs.type_at(0); - if int_type_width_signed(ty, self).is_some() { - let weak = split[1] == "cxchgweak"; - let pair = self.atomic_cmpxchg( - args[0].immediate(), - args[1].immediate(), - args[2].immediate(), - order, - failorder, - weak, - ); - let val = self.extract_value(pair, 0); - let success = self.extract_value(pair, 1); - let success = self.zext(success, self.type_bool()); - - let dest = result.project_field(self, 0); - self.store(val, dest.llval, dest.align); - let dest = result.project_field(self, 1); - self.store(success, dest.llval, dest.align); - return; - } else { - return invalid_monomorphization(ty); - } - } - - "load" => { - let ty = substs.type_at(0); - if int_type_width_signed(ty, self).is_some() { - let size = self.size_of(ty); - self.atomic_load(args[0].immediate(), order, size) - } else { - return invalid_monomorphization(ty); - } - } - - "store" => { - let ty = substs.type_at(0); - if int_type_width_signed(ty, self).is_some() { - let size = self.size_of(ty); - self.atomic_store( - args[1].immediate(), - args[0].immediate(), - order, - size, - ); - return; - } else { - return invalid_monomorphization(ty); - } - } - - "fence" => { - self.atomic_fence(order, SynchronizationScope::CrossThread); - return; - } - - "singlethreadfence" => { - self.atomic_fence(order, SynchronizationScope::SingleThread); - return; - } - - // These are all AtomicRMW ops - op => { - let atom_op = match op { - "xchg" => AtomicRmwBinOp::AtomicXchg, - "xadd" => AtomicRmwBinOp::AtomicAdd, - "xsub" => AtomicRmwBinOp::AtomicSub, - "and" => AtomicRmwBinOp::AtomicAnd, - "nand" => AtomicRmwBinOp::AtomicNand, - "or" => AtomicRmwBinOp::AtomicOr, - "xor" => AtomicRmwBinOp::AtomicXor, - "max" => AtomicRmwBinOp::AtomicMax, - "min" => AtomicRmwBinOp::AtomicMin, - "umax" => AtomicRmwBinOp::AtomicUMax, - "umin" => AtomicRmwBinOp::AtomicUMin, - _ => self.sess().fatal("unknown atomic operation"), - }; - - let ty = substs.type_at(0); - if int_type_width_signed(ty, self).is_some() { - self.atomic_rmw( - atom_op, - args[0].immediate(), - args[1].immediate(), - order, - ) - } else { - return invalid_monomorphization(ty); - } - } - } - } - - sym::nontemporal_store => { - let dst = args[0].deref(self.cx()); - args[1].val.nontemporal_store(self, dst); - return; - } - - sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { - let a = args[0].immediate(); - let b = args[1].immediate(); - if name == sym::ptr_guaranteed_eq { - self.icmp(IntPredicate::IntEQ, a, b) - } else { - self.icmp(IntPredicate::IntNE, a, b) - } - } - - sym::ptr_offset_from => { - let ty = substs.type_at(0); - let pointee_size = self.size_of(ty); - - // This is the same sequence that Clang emits for pointer subtraction. - // It can be neither `nsw` nor `nuw` because the input is treated as - // unsigned but then the output is treated as signed, so neither works. - let a = args[0].immediate(); - let b = args[1].immediate(); - let a = self.ptrtoint(a, self.type_isize()); - let b = self.ptrtoint(b, self.type_isize()); - let d = self.sub(a, b); - let pointee_size = self.const_usize(pointee_size.bytes()); - // this is where the signed magic happens (notice the `s` in `exactsdiv`) - self.exactsdiv(d, pointee_size) - } _ => bug!("unknown intrinsic '{}'", name), }; @@ -807,39 +352,6 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } -fn copy_intrinsic( - bx: &mut Builder<'a, 'll, 'tcx>, - allow_overlap: bool, - volatile: bool, - ty: Ty<'tcx>, - dst: &'ll Value, - src: &'ll Value, - count: &'ll Value, -) { - let (size, align) = bx.size_and_align_of(ty); - let size = bx.mul(bx.const_usize(size.bytes()), count); - let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() }; - if allow_overlap { - bx.memmove(dst, align, src, align, size, flags); - } else { - bx.memcpy(dst, align, src, align, size, flags); - } -} - -fn memset_intrinsic( - bx: &mut Builder<'a, 'll, 'tcx>, - volatile: bool, - ty: Ty<'tcx>, - dst: &'ll Value, - val: &'ll Value, - count: &'ll Value, -) { - let (size, align) = bx.size_and_align_of(ty); - let size = bx.mul(bx.const_usize(size.bytes()), count); - let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() }; - bx.memset(dst, val, size, align, flags); -} - fn try_intrinsic( bx: &mut Builder<'a, 'll, 'tcx>, try_func: &'ll Value, @@ -1271,7 +783,7 @@ fn generic_simd_intrinsic( if name == sym::simd_select_bitmask { let in_ty = arg_tys[0]; - let m_len = match in_ty.kind { + 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(), @@ -1436,7 +948,7 @@ fn generic_simd_intrinsic( m_len, v_len ); - match m_elem_ty.kind { + match m_elem_ty.kind() { ty::Int(_) => {} _ => return_error!("mask element type is `{}`, expected `i_`", m_elem_ty), } @@ -1455,13 +967,13 @@ fn generic_simd_intrinsic( // If the vector has less than 8 lanes, an u8 is returned with zeroed // trailing bits. let expected_int_bits = in_len.max(8); - match ret_ty.kind { + 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), } // Integer vector <i{in_bitwidth} x in_len>: - let (i_xn, in_elem_bitwidth) = match in_elem.kind { + let (i_xn, in_elem_bitwidth) = match in_elem.kind() { ty::Int(i) => { (args[0].immediate(), i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits())) } @@ -1518,7 +1030,7 @@ fn generic_simd_intrinsic( } } } - let ety = match in_elem.kind { + let ety = match in_elem.kind() { ty::Float(f) if f.bit_width() == 32 => { if in_len < 2 || in_len > 16 { return_error!( @@ -1612,7 +1124,7 @@ fn generic_simd_intrinsic( // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81 fn llvm_vector_str(elem_ty: Ty<'_>, vec_len: u64, no_pointers: usize) -> String { let p0s: String = "p0".repeat(no_pointers); - match elem_ty.kind { + match *elem_ty.kind() { ty::Int(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()), ty::Uint(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()), ty::Float(v) => format!("v{}{}f{}", vec_len, p0s, v.bit_width()), @@ -1627,7 +1139,7 @@ fn generic_simd_intrinsic( mut no_pointers: usize, ) -> &'ll Type { // FIXME: use cx.layout_of(ty).llvm_type() ? - let mut elem_ty = match elem_ty.kind { + let mut elem_ty = match *elem_ty.kind() { ty::Int(v) => cx.type_int_from_ty(v), ty::Uint(v) => cx.type_uint_from_ty(v), ty::Float(v) => cx.type_float_from_ty(v), @@ -1680,7 +1192,7 @@ fn generic_simd_intrinsic( // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { - match t.kind { + match t.kind() { ty::RawPtr(p) => 1 + ptr_count(p.ty), _ => 0, } @@ -1688,7 +1200,7 @@ fn generic_simd_intrinsic( // Non-ptr type fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.kind { + match t.kind() { ty::RawPtr(p) => non_ptr(p.ty), _ => t, } @@ -1696,7 +1208,7 @@ fn generic_simd_intrinsic( // The second argument must be a simd vector with an element type that's a pointer // to the element type of the first argument - let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind { + let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind() { ty::RawPtr(p) if p.ty == in_elem => { (ptr_count(arg_tys[1].simd_type(tcx)), non_ptr(arg_tys[1].simd_type(tcx))) } @@ -1721,7 +1233,7 @@ fn generic_simd_intrinsic( assert_eq!(underlying_ty, non_ptr(arg_tys[0].simd_type(tcx))); // The element type of the third argument must be a signed integer type of any width: - match arg_tys[2].simd_type(tcx).kind { + match arg_tys[2].simd_type(tcx).kind() { ty::Int(_) => (), _ => { require!( @@ -1803,7 +1315,7 @@ fn generic_simd_intrinsic( // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { - match t.kind { + match t.kind() { ty::RawPtr(p) => 1 + ptr_count(p.ty), _ => 0, } @@ -1811,7 +1323,7 @@ fn generic_simd_intrinsic( // Non-ptr type fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.kind { + match t.kind() { ty::RawPtr(p) => non_ptr(p.ty), _ => t, } @@ -1819,7 +1331,7 @@ fn generic_simd_intrinsic( // The second argument must be a simd vector with an element type that's a pointer // to the element type of the first argument - let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind { + let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind() { ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::Mutability::Mut => { (ptr_count(arg_tys[1].simd_type(tcx)), non_ptr(arg_tys[1].simd_type(tcx))) } @@ -1844,7 +1356,7 @@ fn generic_simd_intrinsic( assert_eq!(underlying_ty, non_ptr(arg_tys[0].simd_type(tcx))); // The element type of the third argument must be a signed integer type of any width: - match arg_tys[2].simd_type(tcx).kind { + match arg_tys[2].simd_type(tcx).kind() { ty::Int(_) => (), _ => { require!( @@ -1900,7 +1412,7 @@ fn generic_simd_intrinsic( in_ty, ret_ty ); - return match in_elem.kind { + return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => { let r = bx.$integer_reduce(args[0].immediate()); if $ordered { @@ -1972,7 +1484,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, in_ty, ret_ty ); - return match in_elem.kind { + return match in_elem.kind() { ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)), ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)), ty::Float(_f) => Ok(bx.$float_red(args[0].immediate())), @@ -2007,7 +1519,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, ); args[0].immediate() } else { - match in_elem.kind { + match in_elem.kind() { ty::Int(_) | ty::Uint(_) => {} _ => return_error!( "unsupported {} from `{}` with element `{}` to `{}`", @@ -2023,7 +1535,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, let i1xn = bx.type_vector(i1, in_len as u64); bx.trunc(args[0].immediate(), i1xn) }; - return match in_elem.kind { + return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => { let r = bx.$red(input); Ok(if !$boolean { r } else { bx.zext(r, bx.type_bool()) }) @@ -2071,7 +1583,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, Unsupported, } - let (in_style, in_width) = match in_elem.kind { + let (in_style, in_width) = match in_elem.kind() { // vectors of pointer-sized integers should've been // disallowed before here, so this unwrap is safe. ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), @@ -2079,7 +1591,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, ty::Float(f) => (Style::Float, f.bit_width()), _ => (Style::Unsupported, 0), }; - let (out_style, out_width) = match out_elem.kind { + let (out_style, out_width) = match out_elem.kind() { ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()), ty::Float(f) => (Style::Float, f.bit_width()), @@ -2135,7 +1647,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, macro_rules! arith { ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { $(if name == sym::$name { - match in_elem.kind { + match in_elem.kind() { $($(ty::$p(_))|* => { return Ok(bx.$call(args[0].immediate(), args[1].immediate())) })* @@ -2169,7 +1681,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, let rhs = args[1].immediate(); let is_add = name == sym::simd_saturating_add; let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _; - let (signed, elem_width, elem_ty) = match in_elem.kind { + let (signed, elem_width, elem_ty) = match *in_elem.kind() { ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_int_from_ty(i)), ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_uint_from_ty(i)), _ => { @@ -2204,38 +1716,13 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, // FIXME: there’s multiple of this functions, investigate using some of the already existing // stuffs. fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> { - match ty.kind { - ty::Int(t) => Some(( - match t { - ast::IntTy::Isize => u64::from(cx.tcx.sess.target.ptr_width), - ast::IntTy::I8 => 8, - ast::IntTy::I16 => 16, - ast::IntTy::I32 => 32, - ast::IntTy::I64 => 64, - ast::IntTy::I128 => 128, - }, - true, - )), - ty::Uint(t) => Some(( - match t { - ast::UintTy::Usize => u64::from(cx.tcx.sess.target.ptr_width), - ast::UintTy::U8 => 8, - ast::UintTy::U16 => 16, - ast::UintTy::U32 => 32, - ast::UintTy::U64 => 64, - ast::UintTy::U128 => 128, - }, - false, - )), - _ => None, - } -} - -// Returns the width of a float Ty -// Returns None if the type is not a float -fn float_type_width(ty: Ty<'_>) -> Option<u64> { - match ty.kind { - ty::Float(t) => Some(t.bit_width()), + match ty.kind() { + ty::Int(t) => { + Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.ptr_width)), true)) + } + ty::Uint(t) => { + Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.ptr_width)), false)) + } _ => None, } } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 67d4b2642c0..456e9c7ce75 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -4,7 +4,7 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(const_cstr_unchecked)] #![feature(crate_visibility_modifier)] @@ -130,6 +130,13 @@ impl WriteBackendMethods for LlvmCodegenBackend { llvm::LLVMRustPrintPassTimings(); } } + fn run_link( + cgcx: &CodegenContext<Self>, + diag_handler: &Handler, + modules: Vec<ModuleCodegen<Self::Module>>, + ) -> Result<ModuleCodegen<Self::Module>, FatalError> { + back::write::link(cgcx, diag_handler, modules) + } fn run_fat_lto( cgcx: &CodegenContext<Self>, modules: Vec<FatLTOInput<Self>>, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 32822eba930..45c5f56f447 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -96,7 +96,7 @@ pub enum DLLStorageClass { DllExport = 2, // Function to be accessible from DLL. } -/// Matches LLVMRustAttribute in rustllvm.h +/// Matches LLVMRustAttribute in LLVMWrapper.h /// Semantically a subset of the C++ enum llvm::Attribute::AttrKind, /// though it is not ABI compatible (since it's a C++ enum) #[repr(C)] @@ -948,7 +948,6 @@ extern "C" { // Operations on other types pub fn LLVMVoidTypeInContext(C: &Context) -> &Type; - pub fn LLVMX86MMXTypeInContext(C: &Context) -> &Type; pub fn LLVMRustMetadataTypeInContext(C: &Context) -> &Type; // Operations on all values @@ -1705,7 +1704,7 @@ extern "C" { PM: &PassManager<'_>, ); - // Stuff that's in rustllvm/ because it's not upstream yet. + // Stuff that's in llvm-wrapper/ because it's not upstream yet. /// Opens an object file. pub fn LLVMCreateObjectFile( diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index f0b50459837..900f2df383a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -203,7 +203,6 @@ const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ ("fma", None), ("fxsr", None), ("lzcnt", None), - ("mmx", Some(sym::mmx_target_feature)), ("movbe", Some(sym::movbe_target_feature)), ("pclmulqdq", None), ("popcnt", None), diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 3b53b4fe77b..a43724fd495 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -62,10 +62,6 @@ impl CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMIntTypeInContext(self.llcx, num_bits as c_uint) } } - crate fn type_x86_mmx(&self) -> &'ll Type { - unsafe { llvm::LLVMX86MMXTypeInContext(self.llcx) } - } - crate fn type_vector(&self, ty: &'ll Type, len: u64) -> &'ll Type { unsafe { llvm::LLVMVectorType(ty, len as c_uint) } } diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 40870c66475..e0754d21df1 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -4,7 +4,7 @@ use crate::type_::Type; use rustc_codegen_ssa::traits::*; use rustc_middle::bug; use rustc_middle::ty::layout::{FnAbiExt, TyAndLayout}; -use rustc_middle::ty::print::obsolete::DefPathBasedNames; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TypeFoldable}; use rustc_target::abi::{Abi, AddressSpace, Align, FieldsShape}; use rustc_target::abi::{Int, Pointer, F32, F64}; @@ -21,23 +21,8 @@ fn uncached_llvm_type<'a, 'tcx>( match layout.abi { Abi::Scalar(_) => bug!("handled elsewhere"), Abi::Vector { ref element, count } => { - // LLVM has a separate type for 64-bit SIMD vectors on X86 called - // `x86_mmx` which is needed for some SIMD operations. As a bit of a - // hack (all SIMD definitions are super unstable anyway) we - // recognize any one-element SIMD vector as "this should be an - // x86_mmx" type. In general there shouldn't be a need for other - // one-element SIMD vectors, so it's assumed this won't clash with - // much else. - let use_x86_mmx = count == 1 - && layout.size.bits() == 64 - && (cx.sess().target.target.arch == "x86" - || cx.sess().target.target.arch == "x86_64"); - if use_x86_mmx { - return cx.type_x86_mmx(); - } else { - let element = layout.scalar_llvm_type_at(cx, element, Size::ZERO); - return cx.type_vector(element, count); - } + let element = layout.scalar_llvm_type_at(cx, element, Size::ZERO); + return cx.type_vector(element, count); } Abi::ScalarPair(..) => { return cx.type_struct( @@ -51,33 +36,35 @@ fn uncached_llvm_type<'a, 'tcx>( Abi::Uninhabited | Abi::Aggregate { .. } => {} } - let name = match layout.ty.kind { - ty::Closure(..) | - ty::Generator(..) | - ty::Adt(..) | + let name = match layout.ty.kind() { // FIXME(eddyb) producing readable type names for trait objects can result // in problematically distinct types due to HRTB and subtyping (see #47638). // ty::Dynamic(..) | - ty::Foreign(..) | - ty::Str => { - let mut name = String::with_capacity(32); - let printer = DefPathBasedNames::new(cx.tcx, true, true); - printer.push_type_name(layout.ty, &mut name, false); - if let (&ty::Adt(def, _), &Variants::Single { index }) - = (&layout.ty.kind, &layout.variants) + ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Generator(..) | ty::Str + if !cx.sess().fewer_names() => + { + let mut name = with_no_trimmed_paths(|| layout.ty.to_string()); + if let (&ty::Adt(def, _), &Variants::Single { index }) = + (layout.ty.kind(), &layout.variants) { if def.is_enum() && !def.variants.is_empty() { write!(&mut name, "::{}", def.variants[index].ident).unwrap(); } } - if let (&ty::Generator(_, _, _), &Variants::Single { index }) - = (&layout.ty.kind, &layout.variants) + if let (&ty::Generator(_, _, _), &Variants::Single { index }) = + (layout.ty.kind(), &layout.variants) { write!(&mut name, "::{}", ty::GeneratorSubsts::variant_name(index)).unwrap(); } Some(name) } - _ => None + ty::Adt(..) => { + // If `Some` is returned then a named struct is created in LLVM. Name collisions are + // avoided by LLVM (with increasing suffixes). If rustc doesn't generate names then that + // can improve perf. + Some(String::new()) + } + _ => None, }; match layout.fields { @@ -236,7 +223,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { if let Some(&llty) = cx.scalar_lltypes.borrow().get(&self.ty) { return llty; } - let llty = match self.ty.kind { + let llty = match *self.ty.kind() { ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { cx.type_ptr_to(cx.layout_of(ty).llvm_type(cx)) } @@ -329,7 +316,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { ) -> &'a Type { // HACK(eddyb) special-case fat pointers until LLVM removes // pointee types, to avoid bitcasting every `OperandRef::deref`. - match self.ty.kind { + match self.ty.kind() { ty::Ref(..) | ty::RawPtr(_) => { return self.field(cx, index).llvm_type(cx); } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index bfcf979d125..faeb727202c 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1014,86 +1014,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) { } } -// Because windows-gnu target is meant to be self-contained for pure Rust code it bundles -// own mingw-w64 libraries. These libraries are usually not compatible with mingw-w64 -// installed in the system. This breaks many cases where Rust is mixed with other languages -// (e.g. *-sys crates). -// We prefer system mingw-w64 libraries if they are available to avoid this issue. -fn get_crt_libs_path(sess: &Session) -> Option<PathBuf> { - fn find_exe_in_path<P>(exe_name: P) -> Option<PathBuf> - where - P: AsRef<Path>, - { - for dir in env::split_paths(&env::var_os("PATH")?) { - let full_path = dir.join(&exe_name); - if full_path.is_file() { - return Some(fix_windows_verbatim_for_gcc(&full_path)); - } - } - None - } - - fn probe(sess: &Session) -> Option<PathBuf> { - if let (linker, LinkerFlavor::Gcc) = linker_and_flavor(&sess) { - let linker_path = if cfg!(windows) && linker.extension().is_none() { - linker.with_extension("exe") - } else { - linker - }; - if let Some(linker_path) = find_exe_in_path(linker_path) { - let mingw_arch = match &sess.target.target.arch { - x if x == "x86" => "i686", - x => x, - }; - let mingw_bits = &sess.target.target.target_pointer_width; - let mingw_dir = format!("{}-w64-mingw32", mingw_arch); - // Here we have path/bin/gcc but we need path/ - let mut path = linker_path; - path.pop(); - path.pop(); - // Loosely based on Clang MinGW driver - let probe_paths = vec![ - path.join(&mingw_dir).join("lib"), // Typical path - path.join(&mingw_dir).join("sys-root/mingw/lib"), // Rare path - path.join(format!( - "lib/mingw/tools/install/mingw{}/{}/lib", - &mingw_bits, &mingw_dir - )), // Chocolatey is creative - ]; - for probe_path in probe_paths { - if probe_path.join("crt2.o").exists() { - return Some(probe_path); - }; - } - }; - }; - None - } - - let mut system_library_path = sess.system_library_path.borrow_mut(); - match &*system_library_path { - Some(Some(compiler_libs_path)) => Some(compiler_libs_path.clone()), - Some(None) => None, - None => { - let path = probe(sess); - *system_library_path = Some(path.clone()); - path - } - } -} - fn get_object_file_path(sess: &Session, name: &str, self_contained: bool) -> PathBuf { - // prefer system {,dll}crt2.o libs, see get_crt_libs_path comment for more details - if sess.opts.debugging_opts.link_self_contained.is_none() - && sess.target.target.llvm_target.contains("windows-gnu") - { - if let Some(compiler_libs_path) = get_crt_libs_path(sess) { - let file_path = compiler_libs_path.join(name); - if file_path.exists() { - return file_path; - } - } - } let fs = sess.target_filesearch(PathKind::Native); let file_path = fs.get_lib_path().join(name); if file_path.exists() { @@ -1155,7 +1076,7 @@ fn exec_linker( } .to_string(), ); - args.push_str("\n"); + args.push('\n'); } let file = tmpdir.join("linker-arguments"); let bytes = if sess.target.target.options.is_like_msvc { @@ -1286,10 +1207,32 @@ fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind { } } +// Returns true if linker is located within sysroot +fn detect_self_contained_mingw(sess: &Session) -> bool { + let (linker, _) = linker_and_flavor(&sess); + // Assume `-C linker=rust-lld` as self-contained mode + if linker == Path::new("rust-lld") { + return true; + } + let linker_with_extension = if cfg!(windows) && linker.extension().is_none() { + linker.with_extension("exe") + } else { + linker + }; + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let full_path = dir.join(&linker_with_extension); + // If linker comes from sysroot assume self-contained mode + if full_path.is_file() && !full_path.starts_with(&sess.sysroot) { + return false; + } + } + true +} + /// Whether we link to our own CRT objects instead of relying on gcc to pull them. /// We only provide such support for a very limited number of targets. fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { - if let Some(self_contained) = sess.opts.debugging_opts.link_self_contained { + if let Some(self_contained) = sess.opts.cg.link_self_contained { return self_contained; } @@ -1298,10 +1241,10 @@ fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { // based on host and linker path, for example. // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)), - // FIXME: Find some heuristic for "native mingw toolchain is available", - // likely based on `get_crt_libs_path` (https://github.com/rust-lang/rust/pull/67429). Some(CrtObjectsFallback::Mingw) => { - sess.host == sess.target.target && sess.target.target.target_vendor != "uwp" + sess.host == sess.target.target + && sess.target.target.target_vendor != "uwp" + && detect_self_contained_mingw(&sess) } // FIXME: Figure out cases in which WASM needs to link with a native toolchain. Some(CrtObjectsFallback::Wasm) => true, @@ -1498,16 +1441,6 @@ fn link_local_crate_native_libs_and_dependent_crate_libs<'a, B: ArchiveBuilder<' /// Add sysroot and other globally set directories to the directory search list. fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: bool) { - // Prefer system mingw-w64 libs, see get_crt_libs_path comment for more details. - if sess.opts.debugging_opts.link_self_contained.is_none() - && cfg!(windows) - && sess.target.target.llvm_target.contains("windows-gnu") - { - if let Some(compiler_libs_path) = get_crt_libs_path(sess) { - cmd.include_path(&compiler_libs_path); - } - } - // The default library location, we need this to find the runtime. // The location of crates will be determined as needed. let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); @@ -1668,7 +1601,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // FIXME: Order dependent, applies to the following objects. Where should it be placed? // Try to strip as much out of the generated object by removing unused // sections if possible. See more comments in linker.rs - if sess.opts.cg.link_dead_code != Some(true) { + if !sess.link_dead_code() { let keep_metadata = crate_type == CrateType::Dylib; cmd.gc_sections(keep_metadata); } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7d69bb983dd..0edf0fcd1a2 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -702,6 +702,7 @@ impl<B: WriteBackendMethods> WorkItem<B> { enum WorkItemResult<B: WriteBackendMethods> { Compiled(CompiledModule), + NeedsLink(ModuleCodegen<B::Module>), NeedsFatLTO(FatLTOInput<B>), NeedsThinLTO(String, B::ThinBuffer), } @@ -801,11 +802,8 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( None }; - Ok(match lto_type { - ComputedLtoType::No => { - let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? }; - WorkItemResult::Compiled(module) - } + match lto_type { + ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config), ComputedLtoType::Thin => { let (name, thin_buffer) = B::prepare_thin(module); if let Some(path) = bitcode { @@ -813,7 +811,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); } - WorkItemResult::NeedsThinLTO(name, thin_buffer) + Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer)) } ComputedLtoType::Fat => match bitcode { Some(path) => { @@ -821,11 +819,11 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( fs::write(&path, buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); - WorkItemResult::NeedsFatLTO(FatLTOInput::Serialized { name, buffer }) + Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::Serialized { name, buffer })) } - None => WorkItemResult::NeedsFatLTO(FatLTOInput::InMemory(module)), + None => Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::InMemory(module))), }, - }) + } } fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( @@ -871,12 +869,25 @@ fn execute_lto_work_item<B: ExtraBackendMethods>( mut module: lto::LtoModuleCodegen<B>, module_config: &ModuleConfig, ) -> Result<WorkItemResult<B>, FatalError> { + let module = unsafe { module.optimize(cgcx)? }; + finish_intra_module_work(cgcx, module, module_config) +} + +fn finish_intra_module_work<B: ExtraBackendMethods>( + cgcx: &CodegenContext<B>, + module: ModuleCodegen<B::Module>, + module_config: &ModuleConfig, +) -> Result<WorkItemResult<B>, FatalError> { let diag_handler = cgcx.create_diag_handler(); - unsafe { - let module = module.optimize(cgcx)?; - let module = B::codegen(cgcx, &diag_handler, module, module_config)?; + if !cgcx.opts.debugging_opts.combine_cgu + || module.kind == ModuleKind::Metadata + || module.kind == ModuleKind::Allocator + { + let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? }; Ok(WorkItemResult::Compiled(module)) + } else { + Ok(WorkItemResult::NeedsLink(module)) } } @@ -891,6 +902,10 @@ pub enum Message<B: WriteBackendMethods> { thin_buffer: B::ThinBuffer, worker_id: usize, }, + NeedsLink { + module: ModuleCodegen<B::Module>, + worker_id: usize, + }, Done { result: Result<CompiledModule, Option<WorkerFatalError>>, worker_id: usize, @@ -1178,6 +1193,7 @@ fn start_executing_work<B: ExtraBackendMethods>( let mut compiled_modules = vec![]; let mut compiled_metadata_module = None; let mut compiled_allocator_module = None; + let mut needs_link = Vec::new(); let mut needs_fat_lto = Vec::new(); let mut needs_thin_lto = Vec::new(); let mut lto_import_only_modules = Vec::new(); @@ -1434,6 +1450,10 @@ fn start_executing_work<B: ExtraBackendMethods>( } } } + Message::NeedsLink { module, worker_id } => { + free_worker(worker_id); + needs_link.push(module); + } Message::NeedsFatLTO { result, worker_id } => { assert!(!started_lto); free_worker(worker_id); @@ -1462,6 +1482,18 @@ fn start_executing_work<B: ExtraBackendMethods>( } } + let needs_link = mem::take(&mut needs_link); + if !needs_link.is_empty() { + assert!(compiled_modules.is_empty()); + let diag_handler = cgcx.create_diag_handler(); + let module = B::run_link(&cgcx, &diag_handler, needs_link).map_err(|_| ())?; + let module = unsafe { + B::codegen(&cgcx, &diag_handler, module, cgcx.config(ModuleKind::Regular)) + .map_err(|_| ())? + }; + compiled_modules.push(module); + } + // Drop to print timings drop(llvm_start_time); @@ -1521,6 +1553,9 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B> 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 } } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 77c12c410d5..d82fc2c9f63 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -38,7 +38,7 @@ use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::middle::cstore::{self, LinkagePreference}; use rustc_middle::middle::lang_items; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; -use rustc_middle::ty::layout::{self, HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; @@ -48,7 +48,7 @@ use rustc_session::utils::NativeLibKind; use rustc_session::Session; use rustc_span::Span; use rustc_symbol_mangling::test as symbol_names_test; -use rustc_target::abi::{Abi, Align, LayoutOf, Scalar, VariantIdx}; +use rustc_target::abi::{Align, LayoutOf, VariantIdx}; use std::cmp; use std::ops::{Deref, DerefMut}; @@ -120,7 +120,7 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ret_ty: Bx::Type, op: hir::BinOpKind, ) -> Bx::Value { - let signed = match t.kind { + let signed = match t.kind() { ty::Float(_) => { let cmp = bin_op_to_fcmp_predicate(op); let cmp = bx.fcmp(cmp, lhs, rhs); @@ -153,7 +153,7 @@ pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>( ) -> Cx::Value { let (source, target) = cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, cx.param_env()); - match (&source.kind, &target.kind) { + match (source.kind(), target.kind()) { (&ty::Array(_, len), &ty::Slice(_)) => { cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all())) } @@ -182,7 +182,7 @@ pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( dst_ty: Ty<'tcx>, ) -> (Bx::Value, Bx::Value) { debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); - match (&src_ty.kind, &dst_ty.kind) { + match (src_ty.kind(), dst_ty.kind()) { (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { assert!(bx.cx().type_is_sized(a)); @@ -231,7 +231,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) { let src_ty = src.layout.ty; let dst_ty = dst.layout.ty; - match (&src_ty.kind, &dst_ty.kind) { + match (src_ty.kind(), dst_ty.kind()) { (&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => { let (base, info) = match bx.load_operand(src).val { OperandValue::Pair(base, info) => { @@ -330,35 +330,6 @@ pub fn wants_msvc_seh(sess: &Session) -> bool { sess.target.target.options.is_like_msvc } -pub fn from_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - val: Bx::Value, -) -> Bx::Value { - if bx.cx().val_ty(val) == bx.cx().type_i1() { bx.zext(val, bx.cx().type_i8()) } else { val } -} - -pub fn to_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - val: Bx::Value, - layout: layout::TyAndLayout<'_>, -) -> Bx::Value { - if let Abi::Scalar(ref scalar) = layout.abi { - return to_immediate_scalar(bx, val, scalar); - } - val -} - -pub fn to_immediate_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - val: Bx::Value, - scalar: &Scalar, -) -> Bx::Value { - if scalar.is_bool() { - return bx.trunc(val, bx.cx().type_i1()); - } - val -} - pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, dst: Bx::Value, @@ -436,16 +407,18 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // listing. let main_ret_ty = cx.tcx().erase_regions(&main_ret_ty.no_bound_vars().unwrap()); - if cx.get_declared_value("main").is_some() { - // FIXME: We should be smart and show a better diagnostic here. - cx.sess() - .struct_span_err(sp, "entry symbol `main` declared multiple times") - .help("did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead") - .emit(); - cx.sess().abort_if_errors(); - bug!(); - } - let llfn = cx.declare_cfn("main", llfty); + let llfn = match cx.declare_c_main(llfty) { + Some(llfn) => llfn, + None => { + // FIXME: We should be smart and show a better diagnostic here. + cx.sess() + .struct_span_err(sp, "entry symbol `main` declared multiple times") + .help("did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead") + .emit(); + cx.sess().abort_if_errors(); + bug!(); + } + }; // `main` should respect same config for frame pointer elimination as rest of code cx.set_frame_pointer_elimination(llfn); diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index fb8f5a62989..0c0f1bc681c 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -33,11 +33,11 @@ pub fn push_debuginfo_type_name<'tcx>( // .natvis visualizers (and perhaps other existing native debuggers?) let cpp_like_names = tcx.sess.target.target.options.is_like_msvc; - match t.kind { + match *t.kind() { ty::Bool => output.push_str("bool"), ty::Char => output.push_str("char"), ty::Str => output.push_str("str"), - ty::Never => output.push_str("!"), + ty::Never => output.push('!'), ty::Int(int_ty) => output.push_str(int_ty.name_str()), ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()), ty::Float(float_ty) => output.push_str(float_ty.name_str()), diff --git a/compiler/rustc_codegen_ssa/src/glue.rs b/compiler/rustc_codegen_ssa/src/glue.rs index 5b086bc43ff..b88de0b2411 100644 --- a/compiler/rustc_codegen_ssa/src/glue.rs +++ b/compiler/rustc_codegen_ssa/src/glue.rs @@ -19,7 +19,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let align = bx.const_usize(layout.align.abi.bytes()); return (size, align); } - match t.kind { + match t.kind() { ty::Dynamic(..) => { // load size/align from vtable let vtable = info.unwrap(); @@ -64,7 +64,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let size = bx.add(sized_size, unsized_size); // Packed types ignore the alignment of their fields. - if let ty::Adt(def, _) = t.kind { + if let ty::Adt(def, _) = t.kind() { if def.repr.packed() { unsized_align = sized_align; } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 73e33369175..8568bd64f4c 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -1,4 +1,4 @@ -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(option_expect_none)] #![feature(box_patterns)] @@ -8,8 +8,6 @@ #![feature(or_patterns)] #![feature(trusted_len)] #![feature(associated_type_bounds)] -#![feature(const_fn)] // for rustc_index::newtype_index -#![feature(const_panic)] // for rustc_index::newtype_index #![recursion_limit = "256"] //! This crate contains codegen code that is used by all codegen backends (LLVM and others). diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 2e386c1e594..bdde07d3fa9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -236,7 +236,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { let check = match terminator.kind { mir::TerminatorKind::Call { func: mir::Operand::Constant(ref c), ref args, .. } => { - match c.literal.ty.kind { + match *c.literal.ty.kind() { ty::FnDef(did, _) => Some((did, args)), _ => None, } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 8048a569f79..703a17b200a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -13,9 +13,10 @@ use rustc_ast as ast; use rustc_hir::lang_items::LangItem; use rustc_index::vec::Idx; use rustc_middle::mir; -use rustc_middle::mir::interpret::{AllocId, ConstValue, Pointer, Scalar}; +use rustc_middle::mir::interpret::ConstValue; use rustc_middle::mir::AssertKind; use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; use rustc_span::source_map::Span; use rustc_span::{sym, Symbol}; @@ -331,7 +332,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args1 = [place.llval]; &args1[..] }; - let (drop_fn, fn_abi) = match ty.kind { + let (drop_fn, fn_abi) = match ty.kind() { // FIXME(eddyb) perhaps move some of this logic into // `Instance::resolve_drop_in_place`? ty::Dynamic(..) => { @@ -479,14 +480,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { UninitValid => !layout.might_permit_raw_init(bx, /*zero:*/ false).unwrap(), }; if do_panic { - let msg_str = if layout.abi.is_uninhabited() { - // Use this error even for the other intrinsics as it is more precise. - format!("attempted to instantiate uninhabited type `{}`", ty) - } else if intrinsic == ZeroValid { - format!("attempted to zero-initialize type `{}`, which is invalid", ty) - } else { - format!("attempted to leave type `{}` uninitialized, which is invalid", ty) - }; + let msg_str = with_no_trimmed_paths(|| { + if layout.abi.is_uninhabited() { + // Use this error even for the other intrinsics as it is more precise. + format!("attempted to instantiate uninhabited type `{}`", ty) + } else if intrinsic == ZeroValid { + format!("attempted to zero-initialize type `{}`, which is invalid", ty) + } else { + format!("attempted to leave type `{}` uninitialized, which is invalid", ty) + } + }); let msg = bx.const_str(Symbol::intern(&msg_str)); let location = self.get_caller_location(bx, span).immediate(); @@ -537,7 +540,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.codegen_operand(&mut bx, func); - let (instance, mut llfn) = match callee.layout.ty.kind { + let (instance, mut llfn) = match *callee.layout.ty.kind() { ty::FnDef(def_id, substs) => ( Some( ty::Instance::resolve(bx.tcx(), ty::ParamEnv::reveal_all(), def_id, substs) @@ -684,7 +687,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) .collect(); - bx.codegen_intrinsic_call( + Self::codegen_intrinsic_call( + &mut bx, *instance.as_ref().unwrap(), &fn_abi, &args, @@ -864,27 +868,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = constant.literal.ty; let size = bx.layout_of(ty).size; let scalar = match const_value { - // Promoted constants are evaluated into a ByRef instead of a Scalar, - // but we want the scalar value here. - ConstValue::ByRef { alloc, offset } => { - let ptr = Pointer::new(AllocId(0), offset); - alloc - .read_scalar(&bx, ptr, size) - .and_then(|s| s.check_init()) - .unwrap_or_else(|e| { - bx.tcx().sess.span_err( - span, - &format!("Could not evaluate asm const: {}", e), - ); - - // We are erroring out, just emit a dummy constant. - Scalar::from_u64(0) - }) - } - _ => span_bug!(span, "expected ByRef for promoted asm const"), + ConstValue::Scalar(s) => s, + _ => span_bug!( + span, + "expected Scalar for promoted asm const, but got {:#?}", + const_value + ), }; let value = scalar.assert_bits(size); - let string = match ty.kind { + let string = match ty.kind() { ty::Uint(_) => value.to_string(), ty::Int(int_ty) => { match int_ty.normalize(bx.tcx().sess.target.ptr_width) { @@ -911,7 +903,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::InlineAsmOperand::SymFn { ref value } => { let literal = self.monomorphize(&value.literal); - if let ty::FnDef(def_id, substs) = literal.ty.kind { + if let ty::FnDef(def_id, substs) = *literal.ty.kind() { let instance = ty::Instance::resolve_for_fn_ptr( bx.tcx(), ty::ParamEnv::reveal_all(), @@ -1143,7 +1135,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } // We store bools as `i8` so we need to truncate to `i1`. - llval = base::to_immediate(bx, llval, arg.layout); + llval = bx.to_immediate(llval, arg.layout); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs new file mode 100644 index 00000000000..14f1ed59a67 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -0,0 +1,596 @@ +use super::operand::{OperandRef, OperandValue}; +use super::place::PlaceRef; +use super::FunctionCx; +use crate::common::{span_invalid_monomorphization_error, IntPredicate}; +use crate::glue; +use crate::traits::*; +use crate::MemFlags; + +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::{sym, Span}; +use rustc_target::abi::call::{FnAbi, PassMode}; + +fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + allow_overlap: bool, + volatile: bool, + ty: Ty<'tcx>, + dst: Bx::Value, + src: Bx::Value, + count: Bx::Value, +) { + let layout = bx.layout_of(ty); + let size = layout.size; + let align = layout.align.abi; + let size = bx.mul(bx.const_usize(size.bytes()), count); + let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() }; + if allow_overlap { + bx.memmove(dst, align, src, align, size, flags); + } else { + bx.memcpy(dst, align, src, align, size, flags); + } +} + +fn memset_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + volatile: bool, + ty: Ty<'tcx>, + dst: Bx::Value, + val: Bx::Value, + count: Bx::Value, +) { + let layout = bx.layout_of(ty); + let size = layout.size; + let align = layout.align.abi; + let size = bx.mul(bx.const_usize(size.bytes()), count); + let flags = if volatile { MemFlags::VOLATILE } else { MemFlags::empty() }; + bx.memset(dst, val, size, align, flags); +} + +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + pub fn codegen_intrinsic_call( + bx: &mut Bx, + instance: ty::Instance<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + args: &[OperandRef<'tcx, Bx::Value>], + llresult: Bx::Value, + span: Span, + ) { + let callee_ty = instance.ty(bx.tcx(), ty::ParamEnv::reveal_all()); + + let (def_id, substs) = match *callee_ty.kind() { + ty::FnDef(def_id, substs) => (def_id, substs), + _ => bug!("expected fn item type, found {}", callee_ty), + }; + + let sig = callee_ty.fn_sig(bx.tcx()); + let sig = bx.tcx().normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + let arg_tys = sig.inputs(); + let ret_ty = sig.output(); + let name = bx.tcx().item_name(def_id); + let name_str = &*name.as_str(); + + let llret_ty = bx.backend_type(bx.layout_of(ret_ty)); + let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); + + let llval = match name { + sym::assume => { + bx.assume(args[0].immediate()); + return; + } + sym::abort => { + bx.abort(); + return; + } + + sym::unreachable => { + return; + } + sym::va_start => bx.va_start(args[0].immediate()), + sym::va_end => bx.va_end(args[0].immediate()), + sym::size_of_val => { + let tp_ty = substs.type_at(0); + if let OperandValue::Pair(_, meta) = args[0].val { + let (llsize, _) = glue::size_and_align_of_dst(bx, tp_ty, Some(meta)); + llsize + } else { + bx.const_usize(bx.layout_of(tp_ty).size.bytes()) + } + } + sym::min_align_of_val => { + let tp_ty = substs.type_at(0); + if let OperandValue::Pair(_, meta) = args[0].val { + let (_, llalign) = glue::size_and_align_of_dst(bx, tp_ty, Some(meta)); + llalign + } else { + bx.const_usize(bx.layout_of(tp_ty).align.abi.bytes()) + } + } + sym::size_of + | sym::pref_align_of + | sym::min_align_of + | sym::needs_drop + | sym::type_id + | sym::type_name + | sym::variant_count => { + let value = bx + .tcx() + .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None) + .unwrap(); + OperandRef::from_const(bx, value, ret_ty).immediate_or_packed_pair(bx) + } + // Effectively no-op + sym::forget => { + return; + } + sym::offset => { + let ptr = args[0].immediate(); + let offset = args[1].immediate(); + bx.inbounds_gep(ptr, &[offset]) + } + sym::arith_offset => { + let ptr = args[0].immediate(); + let offset = args[1].immediate(); + bx.gep(ptr, &[offset]) + } + + sym::copy_nonoverlapping => { + copy_intrinsic( + bx, + false, + false, + substs.type_at(0), + args[1].immediate(), + args[0].immediate(), + args[2].immediate(), + ); + return; + } + sym::copy => { + copy_intrinsic( + bx, + true, + false, + substs.type_at(0), + args[1].immediate(), + args[0].immediate(), + args[2].immediate(), + ); + return; + } + sym::write_bytes => { + memset_intrinsic( + bx, + false, + substs.type_at(0), + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + ); + return; + } + + sym::volatile_copy_nonoverlapping_memory => { + copy_intrinsic( + bx, + false, + true, + substs.type_at(0), + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + ); + return; + } + sym::volatile_copy_memory => { + copy_intrinsic( + bx, + true, + true, + substs.type_at(0), + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + ); + return; + } + sym::volatile_set_memory => { + memset_intrinsic( + bx, + true, + substs.type_at(0), + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + ); + return; + } + sym::volatile_store => { + let dst = args[0].deref(bx.cx()); + args[1].val.volatile_store(bx, dst); + return; + } + sym::unaligned_volatile_store => { + let dst = args[0].deref(bx.cx()); + args[1].val.unaligned_volatile_store(bx, dst); + return; + } + sym::add_with_overflow + | sym::sub_with_overflow + | sym::mul_with_overflow + | sym::wrapping_add + | sym::wrapping_sub + | sym::wrapping_mul + | sym::unchecked_div + | sym::unchecked_rem + | sym::unchecked_shl + | sym::unchecked_shr + | sym::unchecked_add + | sym::unchecked_sub + | sym::unchecked_mul + | sym::exact_div => { + let ty = arg_tys[0]; + match int_type_width_signed(ty, bx.tcx()) { + Some((_width, signed)) => match name { + sym::add_with_overflow + | sym::sub_with_overflow + | sym::mul_with_overflow => { + let op = match name { + sym::add_with_overflow => OverflowOp::Add, + sym::sub_with_overflow => OverflowOp::Sub, + sym::mul_with_overflow => OverflowOp::Mul, + _ => bug!(), + }; + let (val, overflow) = + bx.checked_binop(op, ty, args[0].immediate(), args[1].immediate()); + // Convert `i1` to a `bool`, and write it to the out parameter + let val = bx.from_immediate(val); + let overflow = bx.from_immediate(overflow); + + let dest = result.project_field(bx, 0); + bx.store(val, dest.llval, dest.align); + let dest = result.project_field(bx, 1); + bx.store(overflow, dest.llval, dest.align); + + return; + } + sym::wrapping_add => bx.add(args[0].immediate(), args[1].immediate()), + sym::wrapping_sub => bx.sub(args[0].immediate(), args[1].immediate()), + sym::wrapping_mul => bx.mul(args[0].immediate(), args[1].immediate()), + sym::exact_div => { + if signed { + bx.exactsdiv(args[0].immediate(), args[1].immediate()) + } else { + bx.exactudiv(args[0].immediate(), args[1].immediate()) + } + } + sym::unchecked_div => { + if signed { + bx.sdiv(args[0].immediate(), args[1].immediate()) + } else { + bx.udiv(args[0].immediate(), args[1].immediate()) + } + } + sym::unchecked_rem => { + if signed { + bx.srem(args[0].immediate(), args[1].immediate()) + } else { + bx.urem(args[0].immediate(), args[1].immediate()) + } + } + sym::unchecked_shl => bx.shl(args[0].immediate(), args[1].immediate()), + sym::unchecked_shr => { + if signed { + bx.ashr(args[0].immediate(), args[1].immediate()) + } else { + bx.lshr(args[0].immediate(), args[1].immediate()) + } + } + sym::unchecked_add => { + if signed { + bx.unchecked_sadd(args[0].immediate(), args[1].immediate()) + } else { + bx.unchecked_uadd(args[0].immediate(), args[1].immediate()) + } + } + sym::unchecked_sub => { + if signed { + bx.unchecked_ssub(args[0].immediate(), args[1].immediate()) + } else { + bx.unchecked_usub(args[0].immediate(), args[1].immediate()) + } + } + sym::unchecked_mul => { + if signed { + bx.unchecked_smul(args[0].immediate(), args[1].immediate()) + } else { + bx.unchecked_umul(args[0].immediate(), args[1].immediate()) + } + } + _ => bug!(), + }, + None => { + span_invalid_monomorphization_error( + bx.tcx().sess, + span, + &format!( + "invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", + name, ty + ), + ); + return; + } + } + } + sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { + match float_type_width(arg_tys[0]) { + Some(_width) => match name { + sym::fadd_fast => bx.fadd_fast(args[0].immediate(), args[1].immediate()), + sym::fsub_fast => bx.fsub_fast(args[0].immediate(), args[1].immediate()), + sym::fmul_fast => bx.fmul_fast(args[0].immediate(), args[1].immediate()), + sym::fdiv_fast => bx.fdiv_fast(args[0].immediate(), args[1].immediate()), + sym::frem_fast => bx.frem_fast(args[0].immediate(), args[1].immediate()), + _ => bug!(), + }, + None => { + span_invalid_monomorphization_error( + bx.tcx().sess, + span, + &format!( + "invalid monomorphization of `{}` intrinsic: \ + expected basic float type, found `{}`", + name, arg_tys[0] + ), + ); + return; + } + } + } + + sym::float_to_int_unchecked => { + if float_type_width(arg_tys[0]).is_none() { + span_invalid_monomorphization_error( + bx.tcx().sess, + span, + &format!( + "invalid monomorphization of `float_to_int_unchecked` \ + intrinsic: expected basic float type, \ + found `{}`", + arg_tys[0] + ), + ); + return; + } + let (_width, signed) = match int_type_width_signed(ret_ty, bx.tcx()) { + Some(pair) => pair, + None => { + span_invalid_monomorphization_error( + bx.tcx().sess, + span, + &format!( + "invalid monomorphization of `float_to_int_unchecked` \ + intrinsic: expected basic integer type, \ + found `{}`", + ret_ty + ), + ); + return; + } + }; + if signed { + bx.fptosi(args[0].immediate(), llret_ty) + } else { + bx.fptoui(args[0].immediate(), llret_ty) + } + } + + sym::discriminant_value => { + if ret_ty.is_integral() { + args[0].deref(bx.cx()).codegen_get_discr(bx, ret_ty) + } else { + span_bug!(span, "Invalid discriminant type for `{:?}`", arg_tys[0]) + } + } + + // This requires that atomic intrinsics follow a specific naming pattern: + // "atomic_<operation>[_<ordering>]", and no ordering means SeqCst + name if name_str.starts_with("atomic_") => { + use crate::common::AtomicOrdering::*; + use crate::common::{AtomicRmwBinOp, SynchronizationScope}; + + let split: Vec<&str> = name_str.split('_').collect(); + + let is_cxchg = split[1] == "cxchg" || split[1] == "cxchgweak"; + let (order, failorder) = match split.len() { + 2 => (SequentiallyConsistent, SequentiallyConsistent), + 3 => match split[2] { + "unordered" => (Unordered, Unordered), + "relaxed" => (Monotonic, Monotonic), + "acq" => (Acquire, Acquire), + "rel" => (Release, Monotonic), + "acqrel" => (AcquireRelease, Acquire), + "failrelaxed" if is_cxchg => (SequentiallyConsistent, Monotonic), + "failacq" if is_cxchg => (SequentiallyConsistent, Acquire), + _ => bx.sess().fatal("unknown ordering in atomic intrinsic"), + }, + 4 => match (split[2], split[3]) { + ("acq", "failrelaxed") if is_cxchg => (Acquire, Monotonic), + ("acqrel", "failrelaxed") if is_cxchg => (AcquireRelease, Monotonic), + _ => bx.sess().fatal("unknown ordering in atomic intrinsic"), + }, + _ => bx.sess().fatal("Atomic intrinsic not in correct format"), + }; + + let invalid_monomorphization = |ty| { + span_invalid_monomorphization_error( + bx.tcx().sess, + span, + &format!( + "invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", + name, ty + ), + ); + }; + + match split[1] { + "cxchg" | "cxchgweak" => { + let ty = substs.type_at(0); + if int_type_width_signed(ty, bx.tcx()).is_some() { + let weak = split[1] == "cxchgweak"; + let pair = bx.atomic_cmpxchg( + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + order, + failorder, + weak, + ); + let val = bx.extract_value(pair, 0); + let success = bx.extract_value(pair, 1); + let val = bx.from_immediate(val); + let success = bx.from_immediate(success); + + let dest = result.project_field(bx, 0); + bx.store(val, dest.llval, dest.align); + let dest = result.project_field(bx, 1); + bx.store(success, dest.llval, dest.align); + return; + } else { + return invalid_monomorphization(ty); + } + } + + "load" => { + let ty = substs.type_at(0); + if int_type_width_signed(ty, bx.tcx()).is_some() { + let size = bx.layout_of(ty).size; + bx.atomic_load(args[0].immediate(), order, size) + } else { + return invalid_monomorphization(ty); + } + } + + "store" => { + let ty = substs.type_at(0); + if int_type_width_signed(ty, bx.tcx()).is_some() { + let size = bx.layout_of(ty).size; + bx.atomic_store(args[1].immediate(), args[0].immediate(), order, size); + return; + } else { + return invalid_monomorphization(ty); + } + } + + "fence" => { + bx.atomic_fence(order, SynchronizationScope::CrossThread); + return; + } + + "singlethreadfence" => { + bx.atomic_fence(order, SynchronizationScope::SingleThread); + return; + } + + // These are all AtomicRMW ops + op => { + let atom_op = match op { + "xchg" => AtomicRmwBinOp::AtomicXchg, + "xadd" => AtomicRmwBinOp::AtomicAdd, + "xsub" => AtomicRmwBinOp::AtomicSub, + "and" => AtomicRmwBinOp::AtomicAnd, + "nand" => AtomicRmwBinOp::AtomicNand, + "or" => AtomicRmwBinOp::AtomicOr, + "xor" => AtomicRmwBinOp::AtomicXor, + "max" => AtomicRmwBinOp::AtomicMax, + "min" => AtomicRmwBinOp::AtomicMin, + "umax" => AtomicRmwBinOp::AtomicUMax, + "umin" => AtomicRmwBinOp::AtomicUMin, + _ => bx.sess().fatal("unknown atomic operation"), + }; + + let ty = substs.type_at(0); + if int_type_width_signed(ty, bx.tcx()).is_some() { + bx.atomic_rmw(atom_op, args[0].immediate(), args[1].immediate(), order) + } else { + return invalid_monomorphization(ty); + } + } + } + } + + sym::nontemporal_store => { + let dst = args[0].deref(bx.cx()); + args[1].val.nontemporal_store(bx, dst); + return; + } + + sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { + let a = args[0].immediate(); + let b = args[1].immediate(); + if name == sym::ptr_guaranteed_eq { + bx.icmp(IntPredicate::IntEQ, a, b) + } else { + bx.icmp(IntPredicate::IntNE, a, b) + } + } + + sym::ptr_offset_from => { + let ty = substs.type_at(0); + let pointee_size = bx.layout_of(ty).size; + + // This is the same sequence that Clang emits for pointer subtraction. + // It can be neither `nsw` nor `nuw` because the input is treated as + // unsigned but then the output is treated as signed, so neither works. + let a = args[0].immediate(); + let b = args[1].immediate(); + let a = bx.ptrtoint(a, bx.type_isize()); + let b = bx.ptrtoint(b, bx.type_isize()); + let d = bx.sub(a, b); + let pointee_size = bx.const_usize(pointee_size.bytes()); + // this is where the signed magic happens (notice the `s` in `exactsdiv`) + bx.exactsdiv(d, pointee_size) + } + + _ => { + // Need to use backend-specific things in the implementation. + bx.codegen_intrinsic_call(instance, fn_abi, args, llresult, span); + return; + } + }; + + if !fn_abi.ret.is_ignore() { + if let PassMode::Cast(ty) = fn_abi.ret.mode { + let ptr_llty = bx.type_ptr_to(bx.cast_backend_type(&ty)); + let ptr = bx.pointercast(result.llval, ptr_llty); + bx.store(llval, ptr, result.align); + } else { + OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) + .val + .store(bx, result); + } + } + } +} + +// Returns the width of an int Ty, and if it's signed or not +// Returns None if the type is not an integer +// FIXME: there’s multiple of this functions, investigate using some of the already existing +// stuffs. +fn int_type_width_signed(ty: Ty<'_>, tcx: TyCtxt<'_>) -> Option<(u64, bool)> { + match ty.kind() { + ty::Int(t) => Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.ptr_width)), true)), + ty::Uint(t) => Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.ptr_width)), false)), + _ => None, + } +} + +// Returns the width of a float Ty +// Returns None if the type is not a float +fn float_type_width(ty: Ty<'_>) -> Option<u64> { + match ty.kind() { + ty::Float(t) => Some(t.bit_width()), + _ => None, + } +} diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 26e6c354702..64d456fb7aa 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -369,8 +369,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // individual LLVM function arguments. let arg_ty = fx.monomorphize(&arg_decl.ty); - let tupled_arg_tys = match arg_ty.kind { - ty::Tuple(ref tys) => tys, + let tupled_arg_tys = match arg_ty.kind() { + ty::Tuple(tys) => tys, _ => bug!("spread argument isn't a tuple?!"), }; @@ -486,6 +486,7 @@ mod block; pub mod constant; pub mod coverageinfo; pub mod debuginfo; +mod intrinsic; pub mod operand; pub mod place; mod rvalue; diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 937c7457c63..bbd004be875 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -147,8 +147,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", self, llty); // Reconstruct the immediate aggregate. let mut llpair = bx.cx().const_undef(llty); - let imm_a = base::from_immediate(bx, a); - let imm_b = base::from_immediate(bx, b); + let imm_a = bx.from_immediate(a); + let imm_b = bx.from_immediate(b); llpair = bx.insert_value(llpair, imm_a, 0); llpair = bx.insert_value(llpair, imm_b, 1); llpair @@ -168,9 +168,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { // Deconstruct the immediate aggregate. let a_llval = bx.extract_value(llval, 0); - let a_llval = base::to_immediate_scalar(bx, a_llval, a); + let a_llval = bx.to_immediate_scalar(a_llval, a); let b_llval = bx.extract_value(llval, 1); - let b_llval = base::to_immediate_scalar(bx, b_llval, b); + let b_llval = bx.to_immediate_scalar(b_llval, b); OperandValue::Pair(a_llval, b_llval) } else { OperandValue::Immediate(llval) @@ -220,29 +220,23 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { _ => bug!("OperandRef::extract_field({:?}): not applicable", self), }; - // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - // Bools in union fields needs to be truncated. - let to_immediate_or_cast = |bx: &mut Bx, val, ty| { - if ty == bx.cx().type_i1() { bx.trunc(val, ty) } else { bx.bitcast(val, ty) } - }; - - match val { - OperandValue::Immediate(ref mut llval) => { - *llval = to_immediate_or_cast(bx, *llval, bx.cx().immediate_backend_type(field)); + match (&mut val, &field.abi) { + (OperandValue::Immediate(llval), _) => { + // Bools in union fields needs to be truncated. + *llval = bx.to_immediate(*llval, field); + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + *llval = bx.bitcast(*llval, bx.cx().immediate_backend_type(field)); } - OperandValue::Pair(ref mut a, ref mut b) => { - *a = to_immediate_or_cast( - bx, - *a, - bx.cx().scalar_pair_element_backend_type(field, 0, true), - ); - *b = to_immediate_or_cast( - bx, - *b, - bx.cx().scalar_pair_element_backend_type(field, 1, true), - ); + (OperandValue::Pair(a, b), Abi::ScalarPair(a_abi, b_abi)) => { + // Bools in union fields needs to be truncated. + *a = bx.to_immediate_scalar(*a, a_abi); + *b = bx.to_immediate_scalar(*b, b_abi); + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + *a = bx.bitcast(*a, bx.cx().scalar_pair_element_backend_type(field, 0, true)); + *b = bx.bitcast(*b, bx.cx().scalar_pair_element_backend_type(field, 1, true)); } - OperandValue::Ref(..) => bug!(), + (OperandValue::Pair(..), _) => bug!(), + (OperandValue::Ref(..), _) => bug!(), } OperandRef { val, layout: field } @@ -302,7 +296,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { bug!("cannot directly store unsized values"); } OperandValue::Immediate(s) => { - let val = base::from_immediate(bx, s); + let val = bx.from_immediate(s); bx.store_with_flags(val, dest.llval, dest.align, flags); } OperandValue::Pair(a, b) => { @@ -313,12 +307,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { let b_offset = a_scalar.value.size(bx).align_to(b_scalar.value.align(bx).abi); let llptr = bx.struct_gep(dest.llval, 0); - let val = base::from_immediate(bx, a); + let val = bx.from_immediate(a); let align = dest.align; bx.store_with_flags(val, llptr, align, flags); let llptr = bx.struct_gep(dest.llval, 1); - let val = base::from_immediate(bx, b); + let val = bx.from_immediate(b); let align = dest.align.restrict_for_offset(b_offset); bx.store_with_flags(val, llptr, align, flags); } diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 799f3b3a575..91609b22615 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -134,7 +134,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // * no metadata available - just log the case // * known alignment - sized types, `[T]`, `str` or a foreign type // * packed struct - there is no alignment padding - match field.ty.kind { + match field.ty.kind() { _ if self.llextra.is_none() => { debug!( "unsized field `{}`, of `{:?}` has no metadata for adjustment", diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 71f924df119..7ce110dcbfc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -98,7 +98,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } // Use llvm.memset.p0i8.* to initialize byte arrays - let v = base::from_immediate(&mut bx, v); + let v = bx.from_immediate(v); if bx.cx().val_ty(v) == bx.cx().type_i8() { bx.memset(start, v, size, dest.align, MemFlags::empty()); return bx; @@ -185,7 +185,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = match *kind { mir::CastKind::Pointer(PointerCast::ReifyFnPointer) => { - match operand.layout.ty.kind { + match *operand.layout.ty.kind() { ty::FnDef(def_id, substs) => { if bx.cx().tcx().has_attr(def_id, sym::rustc_args_required_const) { bug!("reifying a fn ptr that requires const arguments"); @@ -204,7 +204,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)) => { - match operand.layout.ty.kind { + match *operand.layout.ty.kind() { ty::Closure(def_id, substs) => { let instance = Instance::resolve_closure( bx.cx().tcx(), @@ -327,13 +327,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if er.end != er.start && scalar.valid_range.end() > scalar.valid_range.start() { - // We want `table[e as usize]` to not + // We want `table[e as usize ± k]` to not // have bound checks, and this is the most - // convenient place to put the `assume`. - let ll_t_in_const = + // convenient place to put the `assume`s. + if *scalar.valid_range.start() > 0 { + let enum_value_lower_bound = bx + .cx() + .const_uint_big(ll_t_in, *scalar.valid_range.start()); + let cmp_start = bx.icmp( + IntPredicate::IntUGE, + llval, + enum_value_lower_bound, + ); + bx.assume(cmp_start); + } + + let enum_value_upper_bound = bx.cx().const_uint_big(ll_t_in, *scalar.valid_range.end()); - let cmp = bx.icmp(IntPredicate::IntULE, llval, ll_t_in_const); - bx.assume(cmp); + let cmp_end = bx.icmp( + IntPredicate::IntULE, + llval, + enum_value_upper_bound, + ); + bx.assume(cmp_end); } } } @@ -548,7 +564,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // because codegen_place() panics if Local is operand. if let Some(index) = place.as_local() { if let LocalRef::Operand(Some(op)) = self.locals[index] { - if let ty::Array(_, n) = op.layout.ty.kind { + if let ty::Array(_, n) = op.layout.ty.kind() { let n = n.eval_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all()); return bx.cx().const_usize(n); } diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index fc65149937f..607b5459673 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -21,7 +21,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { fn define<Bx: BuilderMethods<'a, 'tcx>>(&self, cx: &'a Bx::CodegenCx) { debug!( "BEGIN IMPLEMENTING '{} ({})' in cgu {}", - self.to_string(cx.tcx(), true), + self, self.to_raw_string(), cx.codegen_unit().name() ); @@ -45,7 +45,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { debug!( "END IMPLEMENTING '{} ({})' in cgu {}", - self.to_string(cx.tcx(), true), + self, self.to_raw_string(), cx.codegen_unit().name() ); @@ -59,7 +59,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { ) { debug!( "BEGIN PREDEFINING '{} ({})' in cgu {}", - self.to_string(cx.tcx(), true), + self, self.to_raw_string(), cx.codegen_unit().name() ); @@ -80,7 +80,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { debug!( "END PREDEFINING '{} ({})' in cgu {}", - self.to_string(cx.tcx(), true), + self, self.to_raw_string(), cx.codegen_unit().name() ); diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 3522ea01153..90520f77e3c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -15,6 +15,7 @@ use rustc_session::{ }; use rustc_span::symbol::Symbol; use rustc_target::abi::LayoutOf; +use rustc_target::spec::Target; pub use rustc_data_structures::sync::MetadataRef; @@ -54,6 +55,12 @@ pub trait CodegenBackend { fn print_passes(&self) {} fn print_version(&self) {} + /// If this plugin provides additional builtin targets, provide the one enabled by the options here. + /// Be careful: this is called *before* init() is called. + fn target_override(&self, _opts: &config::Options) -> Option<Target> { + None + } + fn metadata_loader(&self) -> Box<MetadataLoaderDyn>; fn provide(&self, _providers: &mut Providers); fn provide_extern(&self, _providers: &mut Providers); diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 5ffc83c5f99..5142922260a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -13,9 +13,9 @@ use crate::mir::operand::OperandRef; use crate::mir::place::PlaceRef; use crate::MemFlags; -use rustc_middle::ty::layout::HasParamEnv; +use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; use rustc_middle::ty::Ty; -use rustc_target::abi::{Align, Size}; +use rustc_target::abi::{Abi, Align, Scalar, Size}; use rustc_target::spec::HasTargetSpec; use std::iter::TrustedLen; @@ -115,6 +115,16 @@ pub trait BuilderMethods<'a, 'tcx>: rhs: Self::Value, ) -> (Self::Value, Self::Value); + fn from_immediate(&mut self, val: Self::Value) -> Self::Value; + fn to_immediate(&mut self, val: Self::Value, layout: TyAndLayout<'_>) -> Self::Value { + if let Abi::Scalar(ref scalar) = layout.abi { + self.to_immediate_scalar(val, scalar) + } else { + val + } + } + fn to_immediate_scalar(&mut self, val: Self::Value, scalar: &Scalar) -> Self::Value; + fn alloca(&mut self, ty: Self::Type, align: Align) -> Self::Value; fn dynamic_alloca(&mut self, ty: Self::Type, align: Align) -> Self::Value; fn array_alloca(&mut self, ty: Self::Type, len: Self::Value, align: Align) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/declare.rs b/compiler/rustc_codegen_ssa/src/traits/declare.rs index 690aacd2056..655afcd17f0 100644 --- a/compiler/rustc_codegen_ssa/src/traits/declare.rs +++ b/compiler/rustc_codegen_ssa/src/traits/declare.rs @@ -1,51 +1,7 @@ use super::BackendTypes; use rustc_hir::def_id::DefId; use rustc_middle::mir::mono::{Linkage, Visibility}; -use rustc_middle::ty::{Instance, Ty}; -use rustc_target::abi::call::FnAbi; - -pub trait DeclareMethods<'tcx>: BackendTypes { - /// Declare a global value. - /// - /// If there’s a value with the same name already declared, the function will - /// return its Value instead. - fn declare_global(&self, name: &str, ty: Self::Type) -> Self::Value; - - /// Declare a C ABI function. - /// - /// Only use this for foreign function ABIs and glue. For Rust functions use - /// `declare_fn` instead. - /// - /// If there’s a value with the same name already declared, the function will - /// update the declaration and return existing Value instead. - fn declare_cfn(&self, name: &str, fn_type: Self::Type) -> Self::Function; - - /// Declare a Rust function. - /// - /// If there’s a value with the same name already declared, the function will - /// update the declaration and return existing Value instead. - fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Function; - - /// Declare a global with an intention to define it. - /// - /// Use this function when you intend to define a global. This function will - /// return `None` if the name already has a definition associated with it. In that - /// case an error should be reported to the user, because it usually happens due - /// to user’s fault (e.g., misuse of `#[no_mangle]` or `#[export_name]` attributes). - fn define_global(&self, name: &str, ty: Self::Type) -> Option<Self::Value>; - - /// Declare a private global - /// - /// Use this function when you intend to define a global without a name. - fn define_private_global(&self, ty: Self::Type) -> Self::Value; - - /// Gets declared value by name. - fn get_declared_value(&self, name: &str) -> Option<Self::Value>; - - /// Gets defined or externally defined (AvailableExternally linkage) value by - /// name. - fn get_defined_value(&self, name: &str) -> Option<Self::Value>; -} +use rustc_middle::ty::Instance; pub trait PreDefineMethods<'tcx>: BackendTypes { fn predefine_static( diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index fc57a9a80b2..6fff64bfcb6 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -19,4 +19,6 @@ pub trait MiscMethods<'tcx>: BackendTypes { fn set_frame_pointer_elimination(&self, llfn: Self::Function); fn apply_target_cpu_attr(&self, llfn: Self::Function); fn create_used_variable(&self); + /// Declares the extern "C" main function for the entry point. Returns None if the symbol already exists. + fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function>; } diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 0ac519dd0b1..698ef6083e6 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -35,7 +35,7 @@ pub use self::builder::{BuilderMethods, OverflowOp}; pub use self::consts::ConstMethods; pub use self::coverageinfo::{CoverageInfoBuilderMethods, CoverageInfoMethods}; pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoMethods}; -pub use self::declare::{DeclareMethods, PreDefineMethods}; +pub use self::declare::PreDefineMethods; pub use self::intrinsic::IntrinsicCallMethods; pub use self::misc::MiscMethods; pub use self::statics::{StaticBuilderMethods, StaticMethods}; @@ -60,7 +60,6 @@ pub trait CodegenMethods<'tcx>: + StaticMethods + CoverageInfoMethods + DebugInfoMethods<'tcx> - + DeclareMethods<'tcx> + AsmMethods + PreDefineMethods<'tcx> + HasParamEnv<'tcx> @@ -77,7 +76,6 @@ impl<'tcx, T> CodegenMethods<'tcx> for T where + StaticMethods + CoverageInfoMethods + DebugInfoMethods<'tcx> - + DeclareMethods<'tcx> + AsmMethods + PreDefineMethods<'tcx> + HasParamEnv<'tcx> diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 726d948cfd4..cec07b977e6 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -89,7 +89,7 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { } let tail = self.tcx().struct_tail_erasing_lifetimes(ty, param_env); - match tail.kind { + match tail.kind() { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, _ => bug!("unexpected unsized tail: {:?}", tail), diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 27d52e9b9c5..264e7c2aa92 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -13,6 +13,12 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { type ThinData: Send + Sync; type ThinBuffer: ThinBufferMethods; + /// Merge all modules into main_module and returning it + fn run_link( + cgcx: &CodegenContext<Self>, + diag_handler: &Handler, + modules: Vec<ModuleCodegen<Self::Module>>, + ) -> Result<ModuleCodegen<Self::Module>, FatalError>; /// Performs fat LTO by merging all modules into a single one and returning it /// for further optimization. fn run_fat_lto( diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index f929d9911f9..caaf7c0c3c2 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -8,12 +8,11 @@ edition = "2018" doctest = false [dependencies] +arrayvec = { version = "0.5.1", default-features = false } ena = "0.14" indexmap = "1.5.1" tracing = "0.1" jobserver_crate = { version = "0.1.13", package = "jobserver" } -lazy_static = "1" -once_cell = { version = "1", features = ["parking_lot"] } rustc_serialize = { path = "../rustc_serialize" } rustc_macros = { path = "../rustc_macros" } rustc_graphviz = { path = "../rustc_graphviz" } @@ -28,11 +27,11 @@ rustc_index = { path = "../rustc_index", package = "rustc_index" } bitflags = "1.2.1" measureme = "0.7.1" libc = "0.2" -stacker = "0.1.11" +stacker = "0.1.12" tempfile = "3.0.5" [dependencies.parking_lot] -version = "0.10" +version = "0.11" features = ["nightly"] [target.'cfg(windows)'.dependencies] diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs index f8d631ce01e..aba0bbbac80 100644 --- a/compiler/rustc_data_structures/src/fingerprint.rs +++ b/compiler/rustc_data_structures/src/fingerprint.rs @@ -3,9 +3,10 @@ use rustc_serialize::{ opaque::{self, EncodeResult}, Decodable, Encodable, }; +use std::hash::{Hash, Hasher}; use std::mem; -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy)] +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)] pub struct Fingerprint(u64, u64); impl Fingerprint { @@ -76,6 +77,33 @@ impl ::std::fmt::Display for Fingerprint { } } +impl Hash for Fingerprint { + #[inline] + fn hash<H: Hasher>(&self, state: &mut H) { + state.write_fingerprint(self); + } +} + +trait FingerprintHasher { + fn write_fingerprint(&mut self, fingerprint: &Fingerprint); +} + +impl<H: Hasher> FingerprintHasher for H { + #[inline] + default fn write_fingerprint(&mut self, fingerprint: &Fingerprint) { + self.write_u64(fingerprint.0); + self.write_u64(fingerprint.1); + } +} + +impl FingerprintHasher for crate::unhash::Unhasher { + #[inline] + fn write_fingerprint(&mut self, fingerprint: &Fingerprint) { + // `Unhasher` only wants a single `u64` + self.write_u64(fingerprint.0); + } +} + impl stable_hasher::StableHasherResult for Fingerprint { #[inline] fn finish(hasher: stable_hasher::StableHasher) -> Self { diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs index 64ff6130ddf..bc3d1ce53ba 100644 --- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs +++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs @@ -87,11 +87,8 @@ where } /// Allows searches to terminate early with a value. -#[derive(Clone, Copy, Debug)] -pub enum ControlFlow<T> { - Break(T), - Continue, -} +// FIXME (#75744): remove the alias once the generics are in a better order and `C=()`. +pub type ControlFlow<T> = std::ops::ControlFlow<(), T>; /// The status of a node in the depth-first search. /// @@ -260,12 +257,12 @@ where _node: G::Node, _prior_status: Option<NodeStatus>, ) -> ControlFlow<Self::BreakVal> { - ControlFlow::Continue + ControlFlow::CONTINUE } /// Called after all nodes reachable from this one have been examined. fn node_settled(&mut self, _node: G::Node) -> ControlFlow<Self::BreakVal> { - ControlFlow::Continue + ControlFlow::CONTINUE } /// Behave as if no edges exist from `source` to `target`. @@ -289,8 +286,8 @@ where prior_status: Option<NodeStatus>, ) -> ControlFlow<Self::BreakVal> { match prior_status { - Some(NodeStatus::Visited) => ControlFlow::Break(()), - _ => ControlFlow::Continue, + Some(NodeStatus::Visited) => ControlFlow::BREAK, + _ => ControlFlow::CONTINUE, } } } diff --git a/compiler/rustc_data_structures/src/jobserver.rs b/compiler/rustc_data_structures/src/jobserver.rs index a811c88839d..41605afb44e 100644 --- a/compiler/rustc_data_structures/src/jobserver.rs +++ b/compiler/rustc_data_structures/src/jobserver.rs @@ -1,33 +1,31 @@ pub use jobserver_crate::Client; -use lazy_static::lazy_static; +use std::lazy::SyncLazy; -lazy_static! { - // We can only call `from_env` once per process +// We can only call `from_env` once per process - // Note that this is unsafe because it may misinterpret file descriptors - // on Unix as jobserver file descriptors. We hopefully execute this near - // the beginning of the process though to ensure we don't get false - // positives, or in other words we try to execute this before we open - // any file descriptors ourselves. - // - // Pick a "reasonable maximum" if we don't otherwise have - // a jobserver in our environment, capping out at 32 so we - // don't take everything down by hogging the process run queue. - // The fixed number is used to have deterministic compilation - // across machines. - // - // Also note that we stick this in a global because there could be - // multiple rustc instances in this process, and the jobserver is - // per-process. - static ref GLOBAL_CLIENT: Client = unsafe { - Client::from_env().unwrap_or_else(|| { - let client = Client::new(32).expect("failed to create jobserver"); - // Acquire a token for the main thread which we can release later - client.acquire_raw().ok(); - client - }) - }; -} +// Note that this is unsafe because it may misinterpret file descriptors +// on Unix as jobserver file descriptors. We hopefully execute this near +// the beginning of the process though to ensure we don't get false +// positives, or in other words we try to execute this before we open +// any file descriptors ourselves. +// +// Pick a "reasonable maximum" if we don't otherwise have +// a jobserver in our environment, capping out at 32 so we +// don't take everything down by hogging the process run queue. +// The fixed number is used to have deterministic compilation +// across machines. +// +// Also note that we stick this in a global because there could be +// multiple rustc instances in this process, and the jobserver is +// per-process. +static GLOBAL_CLIENT: SyncLazy<Client> = SyncLazy::new(|| unsafe { + Client::from_env().unwrap_or_else(|| { + let client = Client::new(32).expect("failed to create jobserver"); + // Acquire a token for the main thread which we can release later + client.acquire_raw().ok(); + client + }) +}); pub fn client() -> Client { GLOBAL_CLIENT.clone() diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index af4a7bd1881..9ded10e9c26 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -6,13 +6,15 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![allow(incomplete_features)] +#![feature(array_windows)] +#![feature(control_flow_enum)] #![feature(in_band_lifetimes)] #![feature(unboxed_closures)] -#![feature(generators)] #![feature(generator_trait)] #![feature(fn_traits)] +#![feature(int_bits_const)] #![feature(min_specialization)] #![feature(optin_builtin_traits)] #![feature(nll)] @@ -25,7 +27,8 @@ #![feature(thread_id_value)] #![feature(extend_one)] #![feature(const_panic)] -#![feature(const_generics)] +#![feature(min_const_generics)] +#![feature(once_cell)] #![allow(rustc::default_hash_types)] #[macro_use] @@ -85,23 +88,27 @@ pub mod sorted_map; pub mod stable_set; #[macro_use] pub mod stable_hasher; +mod atomic_ref; +pub mod fingerprint; +pub mod profiling; pub mod sharded; pub mod stack; pub mod sync; pub mod thin_vec; pub mod tiny_list; pub mod transitive_relation; -pub use ena::undo_log; -pub use ena::unify; -mod atomic_ref; -pub mod fingerprint; -pub mod profiling; pub mod vec_linked_list; pub mod work_queue; pub use atomic_ref::AtomicRef; pub mod frozen; +pub mod mini_map; +pub mod mini_set; pub mod tagged_ptr; pub mod temp_dir; +pub mod unhash; + +pub use ena::undo_log; +pub use ena::unify; pub struct OnDrop<F: Fn()>(pub F); diff --git a/compiler/rustc_data_structures/src/macros.rs b/compiler/rustc_data_structures/src/macros.rs index 67fbe3058cd..b918ed9458c 100644 --- a/compiler/rustc_data_structures/src/macros.rs +++ b/compiler/rustc_data_structures/src/macros.rs @@ -1,15 +1,3 @@ -/// A simple static assertion macro. -#[macro_export] -#[allow_internal_unstable(type_ascription)] -macro_rules! static_assert { - ($test:expr) => { - // Use the bool to access an array such that if the bool is false, the access - // is out-of-bounds. - #[allow(dead_code)] - const _: () = [()][!($test: bool) as usize]; - }; -} - /// Type size assertion. The first argument is a type and the second argument is its expected size. #[macro_export] macro_rules! static_assert_size { diff --git a/compiler/rustc_data_structures/src/mini_map.rs b/compiler/rustc_data_structures/src/mini_map.rs new file mode 100644 index 00000000000..cd3e949d383 --- /dev/null +++ b/compiler/rustc_data_structures/src/mini_map.rs @@ -0,0 +1,61 @@ +use crate::fx::FxHashMap; +use arrayvec::ArrayVec; + +use std::hash::Hash; + +/// Small-storage-optimized implementation of a map +/// made specifically for caching results. +/// +/// Stores elements in a small array up to a certain length +/// and switches to `HashMap` when that length is exceeded. +pub enum MiniMap<K, V> { + Array(ArrayVec<[(K, V); 8]>), + Map(FxHashMap<K, V>), +} + +impl<K: Eq + Hash, V> MiniMap<K, V> { + /// Creates an empty `MiniMap`. + pub fn new() -> Self { + MiniMap::Array(ArrayVec::new()) + } + + /// Inserts or updates value in the map. + pub fn insert(&mut self, key: K, value: V) { + match self { + MiniMap::Array(array) => { + for pair in array.iter_mut() { + if pair.0 == key { + pair.1 = value; + return; + } + } + if let Err(error) = array.try_push((key, value)) { + let mut map: FxHashMap<K, V> = array.drain(..).collect(); + let (key, value) = error.element(); + map.insert(key, value); + *self = MiniMap::Map(map); + } + } + MiniMap::Map(map) => { + map.insert(key, value); + } + } + } + + /// Return value by key if any. + pub fn get(&self, key: &K) -> Option<&V> { + match self { + MiniMap::Array(array) => { + for pair in array { + if pair.0 == *key { + return Some(&pair.1); + } + } + return None; + } + MiniMap::Map(map) => { + return map.get(key); + } + } + } +} diff --git a/compiler/rustc_data_structures/src/mini_set.rs b/compiler/rustc_data_structures/src/mini_set.rs new file mode 100644 index 00000000000..9d45af723de --- /dev/null +++ b/compiler/rustc_data_structures/src/mini_set.rs @@ -0,0 +1,41 @@ +use crate::fx::FxHashSet; +use arrayvec::ArrayVec; +use std::hash::Hash; +/// Small-storage-optimized implementation of a set. +/// +/// Stores elements in a small array up to a certain length +/// and switches to `HashSet` when that length is exceeded. +pub enum MiniSet<T> { + Array(ArrayVec<[T; 8]>), + Set(FxHashSet<T>), +} + +impl<T: Eq + Hash> MiniSet<T> { + /// Creates an empty `MiniSet`. + pub fn new() -> Self { + MiniSet::Array(ArrayVec::new()) + } + + /// Adds a value to the set. + /// + /// If the set did not have this value present, true is returned. + /// + /// If the set did have this value present, false is returned. + pub fn insert(&mut self, elem: T) -> bool { + match self { + MiniSet::Array(array) => { + if array.iter().any(|e| *e == elem) { + false + } else { + if let Err(error) = array.try_push(elem) { + let mut set: FxHashSet<T> = array.drain(..).collect(); + set.insert(error.element()); + *self = MiniSet::Set(set); + } + true + } + } + MiniSet::Set(set) => set.insert(elem), + } + } +} diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 07d16c6483e..363879cbb1d 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -600,10 +600,7 @@ pub fn print_time_passes_entry(do_it: bool, what: &str, dur: Duration) { // Hack up our own formatting for the duration to make it easier for scripts // to parse (always use the same number of decimal places and the same unit). pub fn duration_to_secs_str(dur: std::time::Duration) -> String { - const NANOS_PER_SEC: f64 = 1_000_000_000.0; - let secs = dur.as_secs() as f64 + dur.subsec_nanos() as f64 / NANOS_PER_SEC; - - format!("{:.3}", secs) + format!("{:.3}", dur.as_secs_f64()) } // Memory reporting diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index 856eb73e629..4807380595d 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -34,7 +34,7 @@ impl<K: Ord, V> SortedMap<K, V> { /// and that there are no duplicates. #[inline] pub fn from_presorted_elements(elements: Vec<(K, V)>) -> SortedMap<K, V> { - debug_assert!(elements.windows(2).all(|w| w[0].0 < w[1].0)); + debug_assert!(elements.array_windows().all(|[fst, snd]| fst.0 < snd.0)); SortedMap { data: elements } } @@ -159,7 +159,7 @@ impl<K: Ord, V> SortedMap<K, V> { return; } - debug_assert!(elements.windows(2).all(|w| w[0].0 < w[1].0)); + debug_assert!(elements.array_windows().all(|[fst, snd]| fst.0 < snd.0)); let start_index = self.lookup_index_for(&elements[0].0); diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 53d831749ce..d22f3adfb01 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -229,7 +229,7 @@ cfg_if! { pub use std::cell::RefMut as LockGuard; pub use std::cell::RefMut as MappedLockGuard; - pub use once_cell::unsync::OnceCell; + pub use std::lazy::OnceCell; use std::cell::RefCell as InnerRwLock; use std::cell::RefCell as InnerLock; @@ -314,7 +314,7 @@ cfg_if! { pub use parking_lot::MutexGuard as LockGuard; pub use parking_lot::MappedMutexGuard as MappedLockGuard; - pub use once_cell::sync::OnceCell; + pub use std::lazy::SyncOnceCell as OnceCell; pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32, AtomicU64}; diff --git a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs index d39d146db31..d63bcdb3c2b 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs @@ -48,7 +48,7 @@ where P: Pointer, T: Tag, { - const TAG_BIT_SHIFT: usize = (8 * std::mem::size_of::<usize>()) - T::BITS; + const TAG_BIT_SHIFT: usize = usize::BITS as usize - T::BITS; const ASSERTION: () = { assert!(T::BITS <= P::BITS); // Used for the transmute_copy's below diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs index 0d9b3e3ca25..a780d2386a6 100644 --- a/compiler/rustc_data_structures/src/temp_dir.rs +++ b/compiler/rustc_data_structures/src/temp_dir.rs @@ -12,7 +12,7 @@ pub struct MaybeTempDir { impl Drop for MaybeTempDir { fn drop(&mut self) { - // Safety: We are in the destructor, and no further access will + // SAFETY: We are in the destructor, and no further access will // occur. let dir = unsafe { ManuallyDrop::take(&mut self.dir) }; if self.keep { diff --git a/compiler/rustc_data_structures/src/unhash.rs b/compiler/rustc_data_structures/src/unhash.rs new file mode 100644 index 00000000000..48e21a9dab1 --- /dev/null +++ b/compiler/rustc_data_structures/src/unhash.rs @@ -0,0 +1,29 @@ +use std::collections::{HashMap, HashSet}; +use std::hash::{BuildHasherDefault, Hasher}; + +pub type UnhashMap<K, V> = HashMap<K, V, BuildHasherDefault<Unhasher>>; +pub type UnhashSet<V> = HashSet<V, BuildHasherDefault<Unhasher>>; + +/// This no-op hasher expects only a single `write_u64` call. It's intended for +/// map keys that already have hash-like quality, like `Fingerprint`. +#[derive(Default)] +pub struct Unhasher { + value: u64, +} + +impl Hasher for Unhasher { + #[inline] + fn finish(&self) -> u64 { + self.value + } + + fn write(&mut self, _bytes: &[u8]) { + unimplemented!("use write_u64"); + } + + #[inline] + fn write_u64(&mut self, value: u64) { + debug_assert_eq!(0, self.value, "Unhasher doesn't mix values!"); + self.value = value; + } +} diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 76e8592254c..adfce1008e1 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -8,9 +8,8 @@ edition = "2018" crate-type = ["dylib"] [dependencies] -lazy_static = "1.0" libc = "0.2" -tracing = { version = "0.1.18", features = ["release_max_level_info"] } +tracing = { version = "0.1.18" } tracing-subscriber = { version = "0.2.10", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } rustc_middle = { path = "../rustc_middle" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } @@ -39,3 +38,4 @@ winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] [features] llvm = ['rustc_interface/llvm'] +max_level_info = ['tracing/max_level_info'] diff --git a/compiler/rustc_driver/src/args.rs b/compiler/rustc_driver/src/args.rs index 5686819c61b..4f2febf04b1 100644 --- a/compiler/rustc_driver/src/args.rs +++ b/compiler/rustc_driver/src/args.rs @@ -4,8 +4,7 @@ use std::fs; use std::io; pub fn arg_expand(arg: String) -> Result<Vec<String>, Error> { - if arg.starts_with('@') { - let path = &arg[1..]; + if let Some(path) = arg.strip_prefix('@') { let file = match fs::read_to_string(path) { Ok(file) => file, Err(ref err) if err.kind() == io::ErrorKind::InvalidData => { diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 09f5b22cc63..7118437c0c8 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -4,14 +4,13 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(nll)] +#![feature(once_cell)] #![recursion_limit = "256"] #[macro_use] extern crate tracing; -#[macro_use] -extern crate lazy_static; pub extern crate rustc_plugin_impl as plugin; @@ -33,7 +32,7 @@ use rustc_save_analysis as save; use rustc_save_analysis::DumpHandler; use rustc_serialize::json::{self, ToJson}; use rustc_session::config::nightly_options; -use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest}; +use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths}; use rustc_session::getopts; use rustc_session::lint::{Lint, LintId}; use rustc_session::{config, DiagnosticOutput, Session}; @@ -48,6 +47,7 @@ use std::env; use std::ffi::OsString; use std::fs; use std::io::{self, Read, Write}; +use std::lazy::SyncLazy; use std::mem; use std::panic::{self, catch_unwind}; use std::path::PathBuf; @@ -126,6 +126,7 @@ impl Callbacks for TimePassesCallbacks { // time because it will mess up the --prints output. See #64339. self.time_passes = config.opts.prints.is_empty() && (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time); + config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath; } } @@ -1141,13 +1142,12 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { } } -lazy_static! { - static ref DEFAULT_HOOK: Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static> = { +static DEFAULT_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = + SyncLazy::new(|| { let hook = panic::take_hook(); panic::set_hook(Box::new(|info| report_ice(info, BUG_REPORT_URL))); hook - }; -} + }); /// Prints the ICE message, including backtrace and query stack. /// @@ -1222,7 +1222,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { /// /// A custom rustc driver can skip calling this to set up a custom ICE hook. pub fn install_ice_hook() { - lazy_static::initialize(&DEFAULT_HOOK); + SyncLazy::force(&DEFAULT_HOOK); } /// This allows tools to enable rust logging without having to magically match rustc's diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 4e5e77f80c2..a202736ea6c 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -440,6 +440,8 @@ E0751: include_str!("./error_codes/E0751.md"), E0752: include_str!("./error_codes/E0752.md"), E0753: include_str!("./error_codes/E0753.md"), E0754: include_str!("./error_codes/E0754.md"), +E0755: include_str!("./error_codes/E0755.md"), +E0756: include_str!("./error_codes/E0756.md"), E0758: include_str!("./error_codes/E0758.md"), E0759: include_str!("./error_codes/E0759.md"), E0760: include_str!("./error_codes/E0760.md"), @@ -454,6 +456,8 @@ E0768: include_str!("./error_codes/E0768.md"), E0769: include_str!("./error_codes/E0769.md"), E0770: include_str!("./error_codes/E0770.md"), E0771: include_str!("./error_codes/E0771.md"), +E0773: include_str!("./error_codes/E0773.md"), +E0774: include_str!("./error_codes/E0774.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard @@ -630,8 +634,6 @@ E0771: include_str!("./error_codes/E0771.md"), E0722, // Malformed `#[optimize]` attribute E0726, // non-explicit (not `'_`) elided lifetime in unsupported position // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. - E0755, // `#[ffi_pure]` is only allowed on foreign functions - E0756, // `#[ffi_const]` is only allowed on foreign functions E0757, // `#[ffi_const]` functions cannot be `#[ffi_pure]` E0772, // `'static' obligation coming from `impl dyn Trait {}` or `impl Foo for dyn Bar {}`. } diff --git a/compiler/rustc_error_codes/src/error_codes/E0118.md b/compiler/rustc_error_codes/src/error_codes/E0118.md index 5cb5f506e0a..345ec341c3f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0118.md +++ b/compiler/rustc_error_codes/src/error_codes/E0118.md @@ -1,10 +1,10 @@ -An inherent implementation was defined for something which isn't a struct nor -an enum. +An inherent implementation was defined for something which isn't a struct, +enum, union, or trait object. Erroneous code example: ```compile_fail,E0118 -impl (u8, u8) { // error: no base type found for inherent implementation +impl (u8, u8) { // error: no nominal type found for inherent implementation fn get_state(&self) -> String { // ... } @@ -41,3 +41,24 @@ impl TypeWrapper { } } ``` + +Instead of defining an inherent implementation on a reference, you could also +move the reference inside the implementation: + +```compile_fail,E0118 +struct Foo; + +impl &Foo { // error: no nominal type found for inherent implementation + fn bar(self, other: Self) {} +} +``` + +becomes + +``` +struct Foo; + +impl Foo { + fn bar(&self, other: &Self) {} +} +``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0224.md b/compiler/rustc_error_codes/src/error_codes/E0224.md index fd89c1d5256..628488575b2 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0224.md +++ b/compiler/rustc_error_codes/src/error_codes/E0224.md @@ -1,4 +1,4 @@ -A trait object was declaired with no traits. +A trait object was declared with no traits. Erroneous code example: @@ -8,7 +8,7 @@ type Foo = dyn 'static +; Rust does not currently support this. -To solve ensure the the trait object has at least one trait: +To solve, ensure that the trait object has at least one trait: ``` type Foo = dyn 'static + Copy; diff --git a/compiler/rustc_error_codes/src/error_codes/E0433.md b/compiler/rustc_error_codes/src/error_codes/E0433.md index f9e333e8ccd..5a64c13c9af 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0433.md +++ b/compiler/rustc_error_codes/src/error_codes/E0433.md @@ -1,17 +1,27 @@ -An undeclared type or module was used. +An undeclared crate, module, or type was used. Erroneous code example: ```compile_fail,E0433 let map = HashMap::new(); -// error: failed to resolve: use of undeclared type or module `HashMap` +// error: failed to resolve: use of undeclared type `HashMap` ``` Please verify you didn't misspell the type/module's name or that you didn't forget to import it: - ``` use std::collections::HashMap; // HashMap has been imported. let map: HashMap<u32, u32> = HashMap::new(); // So it can be used! ``` + +If you've expected to use a crate name: + +```compile_fail +use ferris_wheel::BigO; +// error: failed to resolve: use of undeclared crate or module `ferris_wheel` +``` + +Make sure the crate has been added as a dependency in `Cargo.toml`. + +To use a module from your current crate, add the `crate::` prefix to the path. diff --git a/compiler/rustc_error_codes/src/error_codes/E0607.md b/compiler/rustc_error_codes/src/error_codes/E0607.md index ea6e10105b0..0545246929f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0607.md +++ b/compiler/rustc_error_codes/src/error_codes/E0607.md @@ -12,15 +12,15 @@ First: what are thin and fat pointers? Thin pointers are "simple" pointers: they are purely a reference to a memory address. -Fat pointers are pointers referencing Dynamically Sized Types (also called DST). -DST don't have a statically known size, therefore they can only exist behind -some kind of pointers that contain additional information. Slices and trait -objects are DSTs. In the case of slices, the additional information the fat -pointer holds is their size. +Fat pointers are pointers referencing Dynamically Sized Types (also called +DSTs). DSTs don't have a statically known size, therefore they can only exist +behind some kind of pointer that contains additional information. For example, +slices and trait objects are DSTs. In the case of slices, the additional +information the fat pointer holds is their size. To fix this error, don't try to cast directly between thin and fat pointers. -For more information about casts, take a look at the Type cast section in -[The Reference Book][1]. +For more information about type casts, take a look at the section of the +[The Rust Reference][1] on type cast expressions. [1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions diff --git a/compiler/rustc_error_codes/src/error_codes/E0755.md b/compiler/rustc_error_codes/src/error_codes/E0755.md new file mode 100644 index 00000000000..88b7f484969 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0755.md @@ -0,0 +1,28 @@ +The `ffi_pure` attribute was used on a non-foreign function. + +Erroneous code example: + +```compile_fail,E0755 +#![feature(ffi_pure)] + +#[ffi_pure] // error! +pub fn foo() {} +# fn main() {} +``` + +The `ffi_pure` attribute can only be used on foreign functions which do not have +side effects or infinite loops: + +``` +#![feature(ffi_pure)] + +extern "C" { + #[ffi_pure] // ok! + pub fn strlen(s: *const i8) -> isize; +} +# fn main() {} +``` + +You can find more information about it in the [unstable Rust Book]. + +[unstable Rust Book]: https://doc.rust-lang.org/unstable-book/language-features/ffi-pure.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0756.md b/compiler/rustc_error_codes/src/error_codes/E0756.md new file mode 100644 index 00000000000..ffdc421aab5 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0756.md @@ -0,0 +1,29 @@ +The `ffi_const` attribute was used on something other than a foreign function +declaration. + +Erroneous code example: + +```compile_fail,E0756 +#![feature(ffi_const)] + +#[ffi_const] // error! +pub fn foo() {} +# fn main() {} +``` + +The `ffi_const` attribute can only be used on foreign function declarations +which have no side effects except for their return value: + +``` +#![feature(ffi_const)] + +extern "C" { + #[ffi_const] // ok! + pub fn strlen(s: *const i8) -> i32; +} +# fn main() {} +``` + +You can get more information about it in the [unstable Rust Book]. + +[unstable Rust Book]: https://doc.rust-lang.org/nightly/unstable-book/language-features/ffi-const.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0764.md b/compiler/rustc_error_codes/src/error_codes/E0764.md index e9061f988ac..0a2e2290e77 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0764.md +++ b/compiler/rustc_error_codes/src/error_codes/E0764.md @@ -1,12 +1,4 @@ -Mutable references (`&mut`) can only be used in constant functions, not statics -or constants. This limitation exists to prevent the creation of constants that -have a mutable reference in their final value. If you had a constant of `&mut -i32` type, you could modify the value through that reference, making the -constant essentially mutable. While there could be a more fine-grained scheme -in the future that allows mutable references if they are not "leaked" to the -final value, a more conservative approach was chosen for now. `const fn` do not -have this problem, as the borrow checker will prevent the `const fn` from -returning new mutable references. +A mutable reference was used in a constant. Erroneous code example: @@ -19,6 +11,18 @@ fn main() { } ``` +Mutable references (`&mut`) can only be used in constant functions, not statics +or constants. This limitation exists to prevent the creation of constants that +have a mutable reference in their final value. If you had a constant of +`&mut i32` type, you could modify the value through that reference, making the +constant essentially mutable. + +While there could be a more fine-grained scheme in the future that allows +mutable references if they are not "leaked" to the final value, a more +conservative approach was chosen for now. `const fn` do not have this problem, +as the borrow checker will prevent the `const fn` from returning new mutable +references. + Remember: you cannot use a function call inside a constant or static. However, you can totally use it in constant functions: diff --git a/compiler/rustc_error_codes/src/error_codes/E0769.md b/compiler/rustc_error_codes/src/error_codes/E0769.md index d1995be9899..4a3b674b058 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0769.md +++ b/compiler/rustc_error_codes/src/error_codes/E0769.md @@ -1,5 +1,5 @@ -A tuple struct or tuple variant was used in a pattern as if it were a -struct or struct variant. +A tuple struct or tuple variant was used in a pattern as if it were a struct or +struct variant. Erroneous code example: @@ -7,9 +7,13 @@ Erroneous code example: enum E { A(i32), } + let e = E::A(42); + match e { - E::A { number } => println!("{}", x), + E::A { number } => { // error! + println!("{}", number); + } } ``` @@ -21,12 +25,14 @@ To fix this error, you can use the tuple pattern: # } # let e = E::A(42); match e { - E::A(number) => println!("{}", number), + E::A(number) => { // ok! + println!("{}", number); + } } ``` -Alternatively, you can also use the struct pattern by using the correct -field names and binding them to new identifiers: +Alternatively, you can also use the struct pattern by using the correct field +names and binding them to new identifiers: ``` # enum E { @@ -34,6 +40,8 @@ field names and binding them to new identifiers: # } # let e = E::A(42); match e { - E::A { 0: number } => println!("{}", number), + E::A { 0: number } => { // ok! + println!("{}", number); + } } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0773.md b/compiler/rustc_error_codes/src/error_codes/E0773.md new file mode 100644 index 00000000000..b19a58bf33d --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0773.md @@ -0,0 +1,38 @@ +A builtin-macro was defined more than once. + +Erroneous code example: + +```compile_fail,E0773 +#![feature(decl_macro)] +#![feature(rustc_attrs)] + +#[rustc_builtin_macro] +pub macro test($item:item) { + /* compiler built-in */ +} + +mod inner { + #[rustc_builtin_macro] + pub macro test($item:item) { + /* compiler built-in */ + } +} +``` + +To fix the issue, remove the duplicate declaration: + +``` +#![feature(decl_macro)] +#![feature(rustc_attrs)] + +#[rustc_builtin_macro] +pub macro test($item:item) { + /* compiler built-in */ +} +``` + +In very rare edge cases, this may happen when loading `core` or `std` twice, +once with `check` metadata and once with `build` metadata. +For more information, see [#75176]. + +[#75176]: https://github.com/rust-lang/rust/pull/75176#issuecomment-683234468 diff --git a/compiler/rustc_error_codes/src/error_codes/E0774.md b/compiler/rustc_error_codes/src/error_codes/E0774.md new file mode 100644 index 00000000000..79793ba9d7d --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0774.md @@ -0,0 +1,24 @@ +`derive` was applied on something which is not a struct, a union or an enum. + +Erroneous code example: + +```compile_fail,E0774 +trait Foo { + #[derive(Clone)] // error! + type Bar; +} +``` + +As said above, the `derive` attribute is only allowed on structs, unions or +enums: + +``` +#[derive(Clone)] // ok! +struct Bar { + field: u32, +} +``` + +You can find more information about `derive` in the [Rust Book]. + +[Rust Book]: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 5a654e83aed..98cbf98df92 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -959,15 +959,15 @@ impl EmitterWriter { '_', line_offset + pos, width_offset + depth, - code_offset + annotation.start_col - left, + (code_offset + annotation.start_col).saturating_sub(left), style, ); } _ if self.teach => { buffer.set_style_range( line_offset, - code_offset + annotation.start_col - left, - code_offset + annotation.end_col - left, + (code_offset + annotation.start_col).saturating_sub(left), + (code_offset + annotation.end_col).saturating_sub(left), style, annotation.is_primary, ); @@ -1227,18 +1227,14 @@ impl EmitterWriter { } draw_note_separator(&mut buffer, 0, max_line_num_len + 1); if *level != Level::FailureNote { - let level_str = level.to_string(); - if !level_str.is_empty() { - buffer.append(0, &level_str, Style::MainHeaderMsg); - buffer.append(0, ": ", Style::NoStyle); - } + buffer.append(0, level.to_str(), Style::MainHeaderMsg); + buffer.append(0, ": ", Style::NoStyle); } self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None); } else { - let level_str = level.to_string(); // The failure note level itself does not provide any useful diagnostic information - if *level != Level::FailureNote && !level_str.is_empty() { - buffer.append(0, &level_str, Style::Level(*level)); + if *level != Level::FailureNote { + buffer.append(0, level.to_str(), Style::Level(*level)); } // only render error codes, not lint codes if let Some(DiagnosticId::Error(ref code)) = *code { @@ -1246,7 +1242,7 @@ impl EmitterWriter { buffer.append(0, &code, Style::Level(*level)); buffer.append(0, "]", Style::Level(*level)); } - if *level != Level::FailureNote && !level_str.is_empty() { + if *level != Level::FailureNote { buffer.append(0, ": ", header_style); } for &(ref text, _) in msg.iter() { @@ -1548,11 +1544,9 @@ impl EmitterWriter { let mut buffer = StyledBuffer::new(); // Render the suggestion message - let level_str = level.to_string(); - if !level_str.is_empty() { - buffer.append(0, &level_str, Style::Level(*level)); - buffer.append(0, ": ", Style::HeaderMsg); - } + buffer.append(0, level.to_str(), Style::Level(*level)); + buffer.append(0, ": ", Style::HeaderMsg); + self.msg_to_buffer( &mut buffer, &[(suggestion.msg.to_owned(), Style::NoStyle)], diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index d4f0a9d83ef..2e8a4ef327a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -2,8 +2,9 @@ //! //! This module contains the code for creating and emitting diagnostics. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(crate_visibility_modifier)] +#![feature(backtrace)] #![feature(nll)] #[macro_use] @@ -296,9 +297,11 @@ struct HandlerInner { /// This is not necessarily the count that's reported to the user once /// compilation ends. err_count: usize, + warn_count: usize, deduplicated_err_count: usize, emitter: Box<dyn Emitter + sync::Send>, delayed_span_bugs: Vec<Diagnostic>, + delayed_good_path_bugs: Vec<Diagnostic>, /// This set contains the `DiagnosticId` of all emitted diagnostics to avoid /// emitting the same diagnostic with extended help (`--teach`) twice, which @@ -361,13 +364,15 @@ impl Drop for HandlerInner { if !self.has_errors() { let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new()); - let has_bugs = !bugs.is_empty(); - for bug in bugs { - self.emit_diagnostic(&bug); - } - if has_bugs { - panic!("no errors encountered even though `delay_span_bug` issued"); - } + self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued"); + } + + if !self.has_any_message() { + let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new()); + self.flush_delayed( + bugs, + "no warnings or errors encountered even though `delayed_good_path_bugs` issued", + ); } } } @@ -422,10 +427,12 @@ impl Handler { inner: Lock::new(HandlerInner { flags, err_count: 0, + warn_count: 0, deduplicated_err_count: 0, deduplicated_warn_count: 0, emitter, delayed_span_bugs: Vec::new(), + delayed_good_path_bugs: Vec::new(), taught_diagnostics: Default::default(), emitted_diagnostic_codes: Default::default(), emitted_diagnostics: Default::default(), @@ -448,11 +455,13 @@ impl Handler { pub fn reset_err_count(&self) { let mut inner = self.inner.borrow_mut(); inner.err_count = 0; + inner.warn_count = 0; inner.deduplicated_err_count = 0; inner.deduplicated_warn_count = 0; // actually free the underlying memory (which `clear` would not do) inner.delayed_span_bugs = Default::default(); + inner.delayed_good_path_bugs = Default::default(); inner.taught_diagnostics = Default::default(); inner.emitted_diagnostic_codes = Default::default(); inner.emitted_diagnostics = Default::default(); @@ -629,6 +638,10 @@ impl Handler { self.inner.borrow_mut().delay_span_bug(span, msg) } + pub fn delay_good_path_bug(&self, msg: &str) { + self.inner.borrow_mut().delay_good_path_bug(msg) + } + pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: &str) { self.emit_diag_at_span(Diagnostic::new(Bug, msg), span); } @@ -768,6 +781,8 @@ impl HandlerInner { } if diagnostic.is_error() { self.bump_err_count(); + } else { + self.bump_warn_count(); } } @@ -859,6 +874,9 @@ impl HandlerInner { 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 + } fn abort_if_errors(&mut self) { self.emit_stashed_diagnostics(); @@ -892,6 +910,15 @@ impl HandlerInner { self.delay_as_bug(diagnostic) } + fn delay_good_path_bug(&mut self, msg: &str) { + let mut diagnostic = Diagnostic::new(Level::Bug, msg); + if self.flags.report_delayed_bugs { + self.emit_diagnostic(&diagnostic); + } + diagnostic.note(&format!("delayed at {}", std::backtrace::Backtrace::force_capture())); + self.delayed_good_path_bugs.push(diagnostic); + } + fn failure(&mut self, msg: &str) { self.emit_diagnostic(&Diagnostic::new(FailureNote, msg)); } @@ -925,23 +952,35 @@ impl HandlerInner { self.delayed_span_bugs.push(diagnostic); } + fn flush_delayed(&mut self, bugs: Vec<Diagnostic>, explanation: &str) { + let has_bugs = !bugs.is_empty(); + for bug in bugs { + self.emit_diagnostic(&bug); + } + if has_bugs { + panic!("{}", explanation); + } + } + fn bump_err_count(&mut self) { self.err_count += 1; self.panic_if_treat_err_as_bug(); } + fn bump_warn_count(&mut self) { + self.warn_count += 1; + } + fn panic_if_treat_err_as_bug(&self) { if self.treat_err_as_bug() { - let s = match (self.err_count(), self.flags.treat_err_as_bug.unwrap_or(0)) { - (0, _) => return, - (1, 1) => "aborting due to `-Z treat-err-as-bug=1`".to_string(), - (1, _) => return, - (count, as_bug) => format!( + match (self.err_count(), self.flags.treat_err_as_bug.unwrap_or(0)) { + (1, 1) => panic!("aborting due to `-Z treat-err-as-bug=1`"), + (0, _) | (1, _) => {} + (count, as_bug) => panic!( "aborting after {} errors due to `-Z treat-err-as-bug={}`", count, as_bug, ), - }; - panic!(s); + } } } } diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index 160bf577799..fae5b94b3a8 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -118,17 +118,15 @@ pub struct Annotation { impl Annotation { /// Whether this annotation is a vertical line placeholder. pub fn is_line(&self) -> bool { - if let AnnotationType::MultilineLine(_) = self.annotation_type { true } else { false } + matches!(self.annotation_type, AnnotationType::MultilineLine(_)) } pub fn is_multiline(&self) -> bool { - match self.annotation_type { + matches!(self.annotation_type, AnnotationType::Multiline(_) | AnnotationType::MultilineStart(_) | AnnotationType::MultilineLine(_) - | AnnotationType::MultilineEnd(_) => true, - _ => false, - } + | AnnotationType::MultilineEnd(_)) } pub fn len(&self) -> usize { diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 4c01cb8159a..926e3dbfc52 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -400,6 +400,7 @@ macro_rules! make_stmts_default { id: ast::DUMMY_NODE_ID, span: e.span, kind: ast::StmtKind::Expr(e), + tokens: None }] }) }; @@ -607,6 +608,7 @@ impl DummyResult { id: ast::DUMMY_NODE_ID, kind: if is_error { ast::TyKind::Err } else { ast::TyKind::Tup(Vec::new()) }, span: sp, + tokens: None, }) } } @@ -641,6 +643,7 @@ impl MacResult for DummyResult { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Expr(DummyResult::raw_expr(self.span, self.is_error)), span: self.span, + tokens: None }]) } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 9490b62aa17..a5a7ee6c9a3 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -3,7 +3,7 @@ use crate::base::ExtCtxt; use rustc_ast::attr; use rustc_ast::ptr::P; use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, PatKind, UnOp}; -use rustc_span::source_map::{respan, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -46,7 +46,7 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, args, }); - ast::Path { span, segments } + ast::Path { span, segments, tokens: None } } pub fn ty_mt(&self, ty: P<ast::Ty>, mutbl: ast::Mutability) -> ast::MutTy { @@ -54,7 +54,7 @@ impl<'a> ExtCtxt<'a> { } pub fn ty(&self, span: Span, kind: ast::TyKind) -> P<ast::Ty> { - P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind }) + P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None }) } pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> { @@ -158,7 +158,12 @@ impl<'a> ExtCtxt<'a> { } pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt { - ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) } + ast::Stmt { + id: ast::DUMMY_NODE_ID, + span: expr.span, + kind: ast::StmtKind::Expr(expr), + tokens: None, + } } pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt { @@ -176,7 +181,12 @@ impl<'a> ExtCtxt<'a> { span: sp, attrs: AttrVec::new(), }); - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp } + ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Local(local), + span: sp, + tokens: None, + } } // Generates `let _: Type;`, which is usually used for type assertions. @@ -189,11 +199,16 @@ impl<'a> ExtCtxt<'a> { span, attrs: AttrVec::new(), }); - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span } + ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span, tokens: None } } pub fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt { - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Item(item), span: sp } + ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Item(item), + span: sp, + tokens: None, + } } pub fn block_expr(&self, expr: P<ast::Expr>) -> P<ast::Block> { @@ -203,11 +218,18 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr), + tokens: None, }], ) } pub fn block(&self, span: Span, stmts: Vec<ast::Stmt>) -> P<ast::Block> { - P(ast::Block { stmts, id: ast::DUMMY_NODE_ID, rules: BlockCheckMode::Default, span }) + P(ast::Block { + stmts, + id: ast::DUMMY_NODE_ID, + rules: BlockCheckMode::Default, + span, + tokens: None, + }) } pub fn expr(&self, span: Span, kind: ast::ExprKind) -> P<ast::Expr> { @@ -578,7 +600,11 @@ impl<'a> ExtCtxt<'a> { attrs, id: ast::DUMMY_NODE_ID, kind, - vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, span, tokens: None, }) @@ -592,7 +618,11 @@ impl<'a> ExtCtxt<'a> { span: ty.span, ty, ident: None, - vis: respan(vis_span, ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: vis_span, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, attrs: Vec::new(), id: ast::DUMMY_NODE_ID, is_placeholder: false, @@ -611,7 +641,11 @@ impl<'a> ExtCtxt<'a> { disr_expr: None, id: ast::DUMMY_NODE_ID, ident, - vis: respan(vis_span, ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: vis_span, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, span, is_placeholder: false, } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index afd1e606402..dd087ab9150 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -399,14 +399,10 @@ impl<'a> StripUnconfigured<'a> { } pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { - let ast::ForeignMod { abi: _, items } = foreign_mod; + let ast::ForeignMod { unsafety: _, abi: _, items } = foreign_mod; items.flat_map_in_place(|item| self.configure(item)); } - pub fn configure_generic_params(&mut self, params: &mut Vec<ast::GenericParam>) { - params.flat_map_in_place(|param| self.configure(param)); - } - fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) { match vdata { ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => { @@ -496,6 +492,13 @@ impl<'a> MutVisitor for StripUnconfigured<'a> { Some(expr) } + fn flat_map_generic_param( + &mut self, + param: ast::GenericParam, + ) -> SmallVec<[ast::GenericParam; 1]> { + noop_flat_map_generic_param(configure!(self, param), self) + } + fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { noop_flat_map_stmt(configure!(self, stmt), self) } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 7a21caf255a..e5cfb866938 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -13,7 +13,7 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrItem, Block, LitKind, NodeId, PatKind, Path}; -use rustc_ast::{ItemKind, MacArgs, MacStmtStyle, StmtKind}; +use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind, Unsafe}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, is_builtin_attr, HasAttrs}; use rustc_data_structures::map_in_place::MapInPlace; @@ -26,7 +26,6 @@ use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::{feature_err, ParseSess}; use rustc_session::Limit; -use rustc_span::source_map::respan; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{ExpnId, FileName, Span, DUMMY_SP}; @@ -358,7 +357,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> { kind: ast::ItemKind::Mod(krate.module), ident: Ident::invalid(), id: ast::DUMMY_NODE_ID, - vis: respan(krate.span.shrink_to_lo(), ast::VisibilityKind::Public), + vis: ast::Visibility { + span: krate.span.shrink_to_lo(), + kind: ast::VisibilityKind::Public, + tokens: None, + }, tokens: None, })]); @@ -370,11 +373,21 @@ impl<'a, 'b> MacroExpander<'a, 'b> { None => { // Resolution failed so we return an empty expansion krate.attrs = vec![]; - krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true }; + krate.module = ast::Mod { + inner: orig_mod_span, + unsafety: Unsafe::No, + items: vec![], + inline: true, + }; } Some(ast::Item { span, kind, .. }) => { krate.attrs = vec![]; - krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true }; + krate.module = ast::Mod { + inner: orig_mod_span, + unsafety: Unsafe::No, + items: vec![], + inline: true, + }; self.cx.span_err( span, &format!( @@ -529,9 +542,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { fn error_derive_forbidden_on_non_adt(&self, derives: &[Path], item: &Annotatable) { let attr = self.cx.sess.find_by_name(item.attrs(), sym::derive); let span = attr.map_or(item.span(), |attr| attr.span); - let mut err = self - .cx - .struct_span_err(span, "`derive` may only be applied to structs, enums and unions"); + let mut err = rustc_errors::struct_span_err!( + self.cx.sess, + span, + E0774, + "`derive` may only be applied to structs, enums and unions", + ); if let Some(ast::Attribute { style: ast::AttrStyle::Inner, .. }) = attr { let trait_list = derives.iter().map(|t| pprust::path_to_string(t)).collect::<Vec<_>>(); let suggestion = format!("#[derive({})]", trait_list.join(", ")); @@ -1363,7 +1379,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } if let StmtKind::MacCall(mac) = stmt.kind { - let (mac, style, attrs) = mac.into_inner(); + let MacCallStmt { mac, style, attrs } = mac.into_inner(); self.check_attributes(&attrs); let mut placeholder = self.collect_bang(mac, stmt.span, AstFragmentKind::Stmts).make_stmts(); @@ -1380,10 +1396,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } // The placeholder expander gives ids to statements, so we avoid folding the id here. - let ast::Stmt { id, kind, span } = stmt; + let ast::Stmt { id, kind, span, tokens } = stmt; noop_flat_map_stmt_kind(kind, self) .into_iter() - .map(|kind| ast::Stmt { id, kind, span }) + .map(|kind| ast::Stmt { id, kind, span, tokens: tokens.clone() }) .collect() } @@ -1438,8 +1454,15 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { push_directory(&self.cx.sess, ident, &item.attrs, dir) } else { // We have an outline `mod foo;` so we need to parse the file. - let (new_mod, dir) = - parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed); + let (new_mod, dir) = parse_external_mod( + &self.cx.sess, + ident, + span, + old_mod.unsafety, + dir, + &mut attrs, + pushed, + ); let krate = ast::Crate { span: new_mod.inner, @@ -1757,6 +1780,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { kind: ast::AttrKind::Normal(AttrItem { path: meta.path, args: meta.kind.mac_args(meta.span), + tokens: None, }), span: at.span, id: at.id, @@ -1788,6 +1812,7 @@ pub struct ExpansionConfig<'feat> { pub should_test: bool, // If false, strip `#[test]` nodes pub keep_macs: bool, pub span_debug: bool, // If true, use verbose debugging for `proc_macro::Span` + pub proc_macro_backtrace: bool, // If true, show backtraces for proc-macro panics } impl<'feat> ExpansionConfig<'feat> { @@ -1800,6 +1825,7 @@ impl<'feat> ExpansionConfig<'feat> { should_test: false, keep_macs: false, span_debug: false, + proc_macro_backtrace: false, } } diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 7f631cb71af..47247294f5d 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,5 +1,4 @@ #![feature(bool_to_option)] -#![feature(cow_is_borrowed)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![feature(or_patterns)] @@ -39,11 +38,6 @@ mod tests; mod parse { #[cfg(test)] mod tests; - #[cfg(test)] - mod lexer { - #[cfg(test)] - mod tests; - } } #[cfg(test)] mod tokenstream { diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index b908a12c1fc..0e5c5fe4d44 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -4,7 +4,7 @@ use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, NtTT, Token}; -use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint}; +use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; use rustc_ast::MacCall; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; @@ -111,7 +111,7 @@ pub(super) fn transcribe<'a>( // // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level // again, and we are done transcribing. - let mut result: Vec<TreeAndJoint> = Vec::new(); + let mut result: Vec<TreeAndSpacing> = Vec::new(); let mut result_stack = Vec::new(); let mut marker = Marker(cx.current_expansion.id, transparency); diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 1e123a2e145..171cb3fa8e6 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -1,4 +1,4 @@ -use rustc_ast::{token, Attribute, Mod}; +use rustc_ast::{token, Attribute, Mod, Unsafe}; use rustc_errors::{struct_span_err, PResult}; use rustc_parse::new_parser_from_file; use rustc_session::parse::ParseSess; @@ -42,6 +42,7 @@ crate fn parse_external_mod( sess: &Session, id: Ident, span: Span, // The span to blame on errors. + unsafety: Unsafe, Directory { mut ownership, path }: Directory, attrs: &mut Vec<Attribute>, pop_mod_stack: &mut bool, @@ -60,13 +61,16 @@ crate fn parse_external_mod( drop(included_mod_stack); // Actually parse the external file as a module. - let mut module = - new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)).parse_mod(&token::Eof)?; + let mut parser = new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)); + let mut module = parser.parse_mod(&token::Eof, unsafety)?; module.0.inline = false; module }; // (1) ...instead, we return a dummy module. - let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_default(); + let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_else(|_| { + let module = Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false }; + (module, Vec::new()) + }); attrs.append(&mut new_attrs); // Extract the directory path for submodules of `module`. @@ -219,8 +223,7 @@ fn error_cannot_declare_mod_here<'a, T>( /// Derive a submodule path from the first found `#[path = "path_string"]`. /// The provided `dir_path` is joined with the `path_string`. -// Public for rustfmt usage. -pub fn submod_path_from_attr( +pub(super) fn submod_path_from_attr( sess: &Session, attrs: &[Attribute], dir_path: &Path, diff --git a/compiler/rustc_expand/src/parse/lexer/tests.rs b/compiler/rustc_expand/src/parse/lexer/tests.rs deleted file mode 100644 index 87184444283..00000000000 --- a/compiler/rustc_expand/src/parse/lexer/tests.rs +++ /dev/null @@ -1,252 +0,0 @@ -use rustc_ast::ast::AttrStyle; -use rustc_ast::token::{self, CommentKind, Token, TokenKind}; -use rustc_data_structures::sync::Lrc; -use rustc_errors::{emitter::EmitterWriter, Handler}; -use rustc_parse::lexer::StringReader; -use rustc_session::parse::ParseSess; -use rustc_span::source_map::{FilePathMapping, SourceMap}; -use rustc_span::symbol::Symbol; -use rustc_span::with_default_session_globals; -use rustc_span::{BytePos, Span}; - -use std::io; -use std::path::PathBuf; - -fn mk_sess(sm: Lrc<SourceMap>) -> ParseSess { - let emitter = EmitterWriter::new( - Box::new(io::sink()), - Some(sm.clone()), - false, - false, - false, - None, - false, - ); - ParseSess::with_span_handler(Handler::with_emitter(true, None, Box::new(emitter)), sm) -} - -// Creates a string reader for the given string. -fn setup<'a>(sm: &SourceMap, sess: &'a ParseSess, teststr: String) -> StringReader<'a> { - let sf = sm.new_source_file(PathBuf::from(teststr.clone()).into(), teststr); - StringReader::new(sess, sf, None) -} - -#[test] -fn t1() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - let mut string_reader = setup( - &sm, - &sh, - "/* my source file */ fn main() { println!(\"zebra\"); }\n".to_string(), - ); - assert_eq!(string_reader.next_token(), token::Comment); - assert_eq!(string_reader.next_token(), token::Whitespace); - let tok1 = string_reader.next_token(); - let tok2 = Token::new(mk_ident("fn"), Span::with_root_ctxt(BytePos(21), BytePos(23))); - assert_eq!(tok1.kind, tok2.kind); - assert_eq!(tok1.span, tok2.span); - assert_eq!(string_reader.next_token(), token::Whitespace); - // Read another token. - let tok3 = string_reader.next_token(); - assert_eq!(string_reader.pos(), BytePos(28)); - let tok4 = Token::new(mk_ident("main"), Span::with_root_ctxt(BytePos(24), BytePos(28))); - assert_eq!(tok3.kind, tok4.kind); - assert_eq!(tok3.span, tok4.span); - - assert_eq!(string_reader.next_token(), token::OpenDelim(token::Paren)); - assert_eq!(string_reader.pos(), BytePos(29)) - }) -} - -// Checks that the given reader produces the desired stream -// of tokens (stop checking after exhausting `expected`). -fn check_tokenization(mut string_reader: StringReader<'_>, expected: Vec<TokenKind>) { - for expected_tok in &expected { - assert_eq!(&string_reader.next_token(), expected_tok); - } -} - -// Makes the identifier by looking up the string in the interner. -fn mk_ident(id: &str) -> TokenKind { - token::Ident(Symbol::intern(id), false) -} - -fn mk_lit(kind: token::LitKind, symbol: &str, suffix: Option<&str>) -> TokenKind { - TokenKind::lit(kind, Symbol::intern(symbol), suffix.map(Symbol::intern)) -} - -#[test] -fn doublecolon_parsing() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - check_tokenization( - setup(&sm, &sh, "a b".to_string()), - vec![mk_ident("a"), token::Whitespace, mk_ident("b")], - ); - }) -} - -#[test] -fn doublecolon_parsing_2() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - check_tokenization( - setup(&sm, &sh, "a::b".to_string()), - vec![mk_ident("a"), token::Colon, token::Colon, mk_ident("b")], - ); - }) -} - -#[test] -fn doublecolon_parsing_3() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - check_tokenization( - setup(&sm, &sh, "a ::b".to_string()), - vec![mk_ident("a"), token::Whitespace, token::Colon, token::Colon, mk_ident("b")], - ); - }) -} - -#[test] -fn doublecolon_parsing_4() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - check_tokenization( - setup(&sm, &sh, "a:: b".to_string()), - vec![mk_ident("a"), token::Colon, token::Colon, token::Whitespace, mk_ident("b")], - ); - }) -} - -#[test] -fn character_a() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - assert_eq!(setup(&sm, &sh, "'a'".to_string()).next_token(), mk_lit(token::Char, "a", None),); - }) -} - -#[test] -fn character_space() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - assert_eq!(setup(&sm, &sh, "' '".to_string()).next_token(), mk_lit(token::Char, " ", None),); - }) -} - -#[test] -fn character_escaped() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - assert_eq!( - setup(&sm, &sh, "'\\n'".to_string()).next_token(), - mk_lit(token::Char, "\\n", None), - ); - }) -} - -#[test] -fn lifetime_name() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - assert_eq!( - setup(&sm, &sh, "'abc".to_string()).next_token(), - token::Lifetime(Symbol::intern("'abc")), - ); - }) -} - -#[test] -fn raw_string() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - assert_eq!( - setup(&sm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string()).next_token(), - mk_lit(token::StrRaw(3), "\"#a\\b\x00c\"", None), - ); - }) -} - -#[test] -fn literal_suffixes() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - macro_rules! test { - ($input: expr, $tok_type: ident, $tok_contents: expr) => {{ - assert_eq!( - setup(&sm, &sh, format!("{}suffix", $input)).next_token(), - mk_lit(token::$tok_type, $tok_contents, Some("suffix")), - ); - // with a whitespace separator - assert_eq!( - setup(&sm, &sh, format!("{} suffix", $input)).next_token(), - mk_lit(token::$tok_type, $tok_contents, None), - ); - }}; - } - - test!("'a'", Char, "a"); - test!("b'a'", Byte, "a"); - test!("\"a\"", Str, "a"); - test!("b\"a\"", ByteStr, "a"); - test!("1234", Integer, "1234"); - test!("0b101", Integer, "0b101"); - test!("0xABC", Integer, "0xABC"); - test!("1.0", Float, "1.0"); - test!("1.0e10", Float, "1.0e10"); - - assert_eq!( - setup(&sm, &sh, "2us".to_string()).next_token(), - mk_lit(token::Integer, "2", Some("us")), - ); - assert_eq!( - setup(&sm, &sh, "r###\"raw\"###suffix".to_string()).next_token(), - mk_lit(token::StrRaw(3), "raw", Some("suffix")), - ); - assert_eq!( - setup(&sm, &sh, "br###\"raw\"###suffix".to_string()).next_token(), - mk_lit(token::ByteStrRaw(3), "raw", Some("suffix")), - ); - }) -} - -#[test] -fn nested_block_comments() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - let mut lexer = setup(&sm, &sh, "/* /* */ */'a'".to_string()); - assert_eq!(lexer.next_token(), token::Comment); - assert_eq!(lexer.next_token(), mk_lit(token::Char, "a", None)); - }) -} - -#[test] -fn crlf_comments() { - with_default_session_globals(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let sh = mk_sess(sm.clone()); - let mut lexer = setup(&sm, &sh, "// test\r\n/// test\r\n".to_string()); - let comment = lexer.next_token(); - assert_eq!(comment.kind, token::Comment); - assert_eq!((comment.span.lo(), comment.span.hi()), (BytePos(0), BytePos(7))); - assert_eq!(lexer.next_token(), token::Whitespace); - assert_eq!( - lexer.next_token(), - token::DocComment(CommentKind::Line, AttrStyle::Outer, Symbol::intern(" test")) - ); - }) -} diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 29fb4c95ec6..4c9271a58df 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -4,7 +4,7 @@ use crate::expand::{AstFragment, AstFragmentKind}; use rustc_ast as ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_span::source_map::{dummy_spanned, DUMMY_SP}; +use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::Ident; use smallvec::{smallvec, SmallVec}; @@ -18,7 +18,7 @@ pub fn placeholder( ) -> AstFragment { fn mac_placeholder() -> ast::MacCall { ast::MacCall { - path: ast::Path { span: DUMMY_SP, segments: Vec::new() }, + path: ast::Path { span: DUMMY_SP, segments: Vec::new(), tokens: None }, args: P(ast::MacArgs::Empty), prior_type_ascription: None, } @@ -26,7 +26,11 @@ pub fn placeholder( let ident = Ident::invalid(); let attrs = Vec::new(); - let vis = vis.unwrap_or_else(|| dummy_spanned(ast::VisibilityKind::Inherited)); + let vis = vis.unwrap_or(ast::Visibility { + span: DUMMY_SP, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }); let span = DUMMY_SP; let expr_placeholder = || { P(ast::Expr { @@ -37,7 +41,8 @@ pub fn placeholder( tokens: None, }) }; - let ty = || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span }); + let ty = + || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span, tokens: None }); let pat = || P(ast::Pat { id, kind: ast::PatKind::MacCall(mac_placeholder()), span, tokens: None }); @@ -88,12 +93,19 @@ pub fn placeholder( kind: ast::PatKind::MacCall(mac_placeholder()), tokens: None, })), - AstFragmentKind::Ty => { - AstFragment::Ty(P(ast::Ty { id, span, kind: ast::TyKind::MacCall(mac_placeholder()) })) - } + AstFragmentKind::Ty => AstFragment::Ty(P(ast::Ty { + id, + span, + kind: ast::TyKind::MacCall(mac_placeholder()), + tokens: None, + })), AstFragmentKind::Stmts => AstFragment::Stmts(smallvec![{ - let mac = P((mac_placeholder(), ast::MacStmtStyle::Braces, ast::AttrVec::new())); - ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac) } + let mac = P(ast::MacCallStmt { + mac: mac_placeholder(), + style: ast::MacStmtStyle::Braces, + attrs: ast::AttrVec::new(), + }); + ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac), tokens: None } }]), AstFragmentKind::Arms => AstFragment::Arms(smallvec![ast::Arm { attrs: Default::default(), @@ -293,7 +305,7 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> { fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { let (style, mut stmts) = match stmt.kind { - ast::StmtKind::MacCall(mac) => (mac.1, self.remove(stmt.id).make_stmts()), + ast::StmtKind::MacCall(mac) => (mac.style, self.remove(stmt.id).make_stmts()), _ => return noop_flat_map_stmt(stmt, self), }; diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 4e865c20d6f..94b3fcf2850 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -24,7 +24,7 @@ impl base::ProcMacro for BangProcMacro { input: TokenStream, ) -> Result<TokenStream, ErrorReported> { let server = proc_macro_server::Rustc::new(ecx); - self.client.run(&EXEC_STRATEGY, server, input).map_err(|e| { + self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.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,14 +48,16 @@ impl base::AttrProcMacro for AttrProcMacro { annotated: TokenStream, ) -> Result<TokenStream, ErrorReported> { let server = proc_macro_server::Rustc::new(ecx); - self.client.run(&EXEC_STRATEGY, server, annotation, annotated).map_err(|e| { - let mut err = ecx.struct_span_err(span, "custom attribute panicked"); - if let Some(s) = e.as_str() { - err.help(&format!("message: {}", s)); - } - err.emit(); - ErrorReported - }) + self.client + .run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace) + .map_err(|e| { + let mut err = ecx.struct_span_err(span, "custom attribute panicked"); + if let Some(s) = e.as_str() { + err.help(&format!("message: {}", s)); + } + err.emit(); + ErrorReported + }) } } @@ -111,17 +113,18 @@ impl MultiItemModifier for ProcMacroDerive { }; let server = proc_macro_server::Rustc::new(ecx); - let stream = match self.client.run(&EXEC_STRATEGY, server, input) { - 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)); + 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![]); } - 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 409784812f5..4cfb188783b 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -2,7 +2,7 @@ use crate::base::ExtCtxt; use rustc_ast as ast; use rustc_ast::token; -use rustc_ast::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint}; +use rustc_ast::tokenstream::{self, DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::Diagnostic; @@ -47,15 +47,15 @@ impl ToInternal<token::DelimToken> for Delimiter { } } -impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)> +impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec<Self>)> for TokenTree<Group, Punct, Ident, Literal> { fn from_internal( - ((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec<Self>), + ((tree, spacing), sess, stack): (TreeAndSpacing, &ParseSess, &mut Vec<Self>), ) -> Self { use rustc_ast::token::*; - let joint = is_joint == Joint; + let joint = spacing == Joint; let Token { kind, span } = match tree { tokenstream::TokenTree::Delimited(span, delim, tts) => { let delimiter = Delimiter::from_internal(delim); @@ -189,7 +189,7 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)> } OpenDelim(..) | CloseDelim(..) => unreachable!(), - Whitespace | Comment | Shebang(..) | Unknown(..) | Eof => unreachable!(), + Eof => unreachable!(), } } } @@ -261,7 +261,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> { }; let tree = tokenstream::TokenTree::token(kind, span); - TokenStream::new(vec![(tree, if joint { Joint } else { NonJoint })]) + TokenStream::new(vec![(tree, if joint { Joint } else { Alone })]) } } @@ -444,7 +444,7 @@ impl server::TokenStreamIter for Rustc<'_> { ) -> Option<TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> { loop { let tree = iter.stack.pop().or_else(|| { - let next = iter.cursor.next_with_joint()?; + let next = iter.cursor.next_with_spacing()?; Some(TokenTree::from_internal((next, self.sess, &mut iter.stack))) })?; // A hack used to pass AST fragments to attribute and derive macros @@ -584,12 +584,12 @@ impl server::Literal for Rustc<'_> { let start = match start { Bound::Included(lo) => lo, - Bound::Excluded(lo) => lo + 1, + Bound::Excluded(lo) => lo.checked_add(1)?, Bound::Unbounded => 0, }; let end = match end { - Bound::Included(hi) => hi + 1, + Bound::Included(hi) => hi.checked_add(1)?, Bound::Excluded(hi) => hi, Bound::Unbounded => length, }; diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml index 3f8047e931e..7a06bce13c8 100644 --- a/compiler/rustc_feature/Cargo.toml +++ b/compiler/rustc_feature/Cargo.toml @@ -9,5 +9,4 @@ doctest = false [dependencies] rustc_data_structures = { path = "../rustc_data_structures" } -lazy_static = "1.0.0" rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index d16f023c00a..0477f6f149b 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -268,6 +268,8 @@ declare_features! ( /// 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), // ------------------------------------------------------------------------- // feature-group-end: accepted features diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index e858980738d..6452bda293e 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -229,7 +229,6 @@ declare_features! ( (active, powerpc_target_feature, "1.27.0", Some(44839), None), (active, mips_target_feature, "1.27.0", Some(44839), None), (active, avx512_target_feature, "1.27.0", Some(44839), None), - (active, mmx_target_feature, "1.27.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), @@ -404,9 +403,6 @@ declare_features! ( /// Allows dereferencing raw pointers during const eval. (active, const_raw_ptr_deref, "1.27.0", Some(51911), None), - /// Allows `#[doc(alias = "...")]`. - (active, doc_alias, "1.27.0", Some(50146), None), - /// Allows inconsistent bounds in where clauses. (active, trivial_bounds, "1.28.0", Some(48214), None), @@ -585,6 +581,9 @@ declare_features! ( /// Allows `if let` guard in match arms. (active, if_let_guard, "1.47.0", Some(51114), None), + /// Allows non trivial generic constants which have to be manually propageted upwards. + (active, const_evaluatable_checked, "1.48.0", Some(76560), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -600,8 +599,14 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::const_generics, sym::let_chains, sym::raw_dylib, + sym::const_evaluatable_checked, sym::const_trait_impl, sym::const_trait_bound_opt_out, sym::lazy_normalization_consts, sym::specialization, ]; + +/// Some features are not allowed to be used together at the same time, if +/// the two are present, produce an error. +pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = + &[(sym::const_generics, sym::min_const_generics)]; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 879f06f89a7..8b7fd59cd87 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -5,10 +5,11 @@ use AttributeType::*; use crate::{Features, Stability}; -use lazy_static::lazy_static; use rustc_data_structures::fx::FxHashMap; use rustc_span::symbol::{sym, Symbol}; +use std::lazy::SyncLazy; + type GateFn = fn(&Features) -> bool; macro_rules! cfg_fn { @@ -25,6 +26,11 @@ const GATED_CFGS: &[GatedCfg] = &[ (sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)), (sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), + ( + sym::target_has_atomic_equal_alignment, + sym::cfg_target_has_atomic, + cfg_fn!(cfg_target_has_atomic), + ), (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)), (sym::version, sym::cfg_version, cfg_fn!(cfg_version)), ]; @@ -589,8 +595,8 @@ pub fn is_builtin_attr_name(name: Symbol) -> bool { BUILTIN_ATTRIBUTE_MAP.get(&name).is_some() } -lazy_static! { - pub static ref BUILTIN_ATTRIBUTE_MAP: FxHashMap<Symbol, &'static BuiltinAttribute> = { +pub static BUILTIN_ATTRIBUTE_MAP: SyncLazy<FxHashMap<Symbol, &'static BuiltinAttribute>> = + SyncLazy::new(|| { let mut map = FxHashMap::default(); for attr in BUILTIN_ATTRIBUTES.iter() { if map.insert(attr.0, attr).is_some() { @@ -598,5 +604,4 @@ lazy_static! { } } map - }; -} + }); diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index f8bf0315d0c..15564a59658 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -11,6 +11,8 @@ //! even if it is stabilized or removed, *do not remove it*. Instead, move the //! symbol to the `accepted` or `removed` modules respectively. +#![feature(once_cell)] + mod accepted; mod active; mod builtin_attrs; @@ -129,7 +131,7 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZeroU3 } pub use accepted::ACCEPTED_FEATURES; -pub use active::{Features, ACTIVE_FEATURES, INCOMPLETE_FEATURES}; +pub use active::{Features, ACTIVE_FEATURES, INCOMPATIBLE_FEATURES, INCOMPLETE_FEATURES}; 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_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index 4339092b63e..76e33bed97f 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -272,7 +272,7 @@ //! * [DOT language](http://www.graphviz.org/doc/info/lang.html) #![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", + html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(allow(unused_variables), deny(warnings))) )] #![feature(nll)] @@ -591,14 +591,15 @@ pub trait GraphWalk<'a> { fn target(&'a self, edge: &Self::Edge) -> Self::Node; } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] pub enum RenderOption { NoEdgeLabels, NoNodeLabels, NoEdgeStyles, NoNodeStyles, - Monospace, + Fontname(String), + DarkTheme, } /// Returns vec holding all the default render options. @@ -630,10 +631,26 @@ where writeln!(w, "digraph {} {{", g.graph_id().as_slice())?; // Global graph properties - if options.contains(&RenderOption::Monospace) { - writeln!(w, r#" graph[fontname="monospace"];"#)?; - writeln!(w, r#" node[fontname="monospace"];"#)?; - writeln!(w, r#" edge[fontname="monospace"];"#)?; + let mut graph_attrs = Vec::new(); + let mut content_attrs = Vec::new(); + let font; + if let Some(fontname) = options.iter().find_map(|option| { + if let RenderOption::Fontname(fontname) = option { Some(fontname) } else { None } + }) { + font = format!(r#"fontname="{}""#, fontname); + graph_attrs.push(&font[..]); + content_attrs.push(&font[..]); + } + if options.contains(&RenderOption::DarkTheme) { + graph_attrs.push(r#"bgcolor="black""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + if !(graph_attrs.is_empty() && content_attrs.is_empty()) { + writeln!(w, r#" graph[{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node[{}];"#, content_attrs_str)?; + writeln!(w, r#" edge[{}];"#, content_attrs_str)?; } for n in g.nodes().iter() { diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index ed295ff0058..b24c208c76a 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -15,6 +15,5 @@ rustc_index = { path = "../rustc_index" } rustc_span = { path = "../rustc_span" } rustc_serialize = { path = "../rustc_serialize" } rustc_ast = { path = "../rustc_ast" } -lazy_static = "1" tracing = "0.1" smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 0d61dc037c6..96fde48d96c 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -6,6 +6,7 @@ use rustc_ast::NodeId; use rustc_macros::HashStable_Generic; use rustc_span::hygiene::MacroKind; +use std::array::IntoIter; use std::fmt::Debug; /// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct. @@ -198,7 +199,16 @@ pub enum Res<Id = hir::HirId> { // Type namespace PrimTy(hir::PrimTy), - SelfTy(Option<DefId> /* trait */, Option<DefId> /* impl */), + /// `Self`, with both an optional trait and impl `DefId`. + /// + /// HACK(min_const_generics): impl self types also have an optional requirement to not mention + /// any generic parameters to allow the following with `min_const_generics`: + /// ```rust + /// impl Foo { fn test() -> [u8; std::mem::size_of::<Self>()] {} } + /// ``` + /// + /// FIXME(lazy_normalization_consts): Remove this bodge once this feature is stable. + SelfTy(Option<DefId> /* trait */, Option<(DefId, bool)> /* impl */), ToolMod, // e.g., `rustfmt` in `#[rustfmt::skip]` // Value namespace @@ -291,6 +301,14 @@ impl<T> PerNS<T> { pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> PerNS<U> { PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) } } + + pub fn into_iter(self) -> IntoIter<T, 3> { + IntoIter::new([self.value_ns, self.type_ns, self.macro_ns]) + } + + pub fn iter(&self) -> IntoIter<&T, 3> { + IntoIter::new([&self.value_ns, &self.type_ns, &self.macro_ns]) + } } impl<T> ::std::ops::Index<Namespace> for PerNS<T> { diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index 45735ead256..45befc7b115 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -313,6 +313,7 @@ impl Definitions { } #[inline] + #[track_caller] pub fn local_def_id_to_hir_id(&self, id: LocalDefId) -> hir::HirId { self.def_id_to_hir_id[id].unwrap() } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index acf6847c014..5e4c03bec83 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -17,7 +17,7 @@ use rustc_macros::HashStable_Generic; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -use lazy_static::lazy_static; +use std::lazy::SyncLazy; pub enum LangItemGroup { Op, @@ -117,14 +117,12 @@ macro_rules! language_item_table { )* } - lazy_static! { - /// A mapping from the name of the lang item to its order and the form it must be of. - pub static ref ITEM_REFS: FxHashMap<Symbol, (usize, Target)> = { - let mut item_refs = FxHashMap::default(); - $( item_refs.insert($name, (LangItem::$variant as usize, $target)); )* - item_refs - }; - } + /// A mapping from the name of the lang item to its order and the form it must be of. + pub static ITEM_REFS: SyncLazy<FxHashMap<Symbol, (usize, Target)>> = SyncLazy::new(|| { + let mut item_refs = FxHashMap::default(); + $( item_refs.insert($name, (LangItem::$variant as usize, $target)); )* + item_refs + }); // End of the macro } diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 19ea1de5683..9d931b3a9e1 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(array_value_iter)] #![feature(crate_visibility_modifier)] #![feature(const_fn)] // For the unsizing cast on `&[]` #![feature(const_panic)] #![feature(in_band_lifetimes)] +#![feature(once_cell)] #![feature(or_patterns)] #![recursion_limit = "256"] diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index 129eec7d29e..52f28bf8f4c 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -7,18 +7,16 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_span::symbol::{sym, Symbol}; -use lazy_static::lazy_static; +use std::lazy::SyncLazy; macro_rules! weak_lang_items { ($($name:ident, $item:ident, $sym:ident;)*) => ( -lazy_static! { - pub static ref WEAK_ITEMS_REFS: FxHashMap<Symbol, LangItem> = { - let mut map = FxHashMap::default(); - $(map.insert(sym::$name, LangItem::$item);)* - map - }; -} +pub static WEAK_ITEMS_REFS: SyncLazy<FxHashMap<Symbol, LangItem>> = SyncLazy::new(|| { + let mut map = FxHashMap::default(); + $(map.insert(sym::$name, LangItem::$item);)* + map +}); /// The `check_name` argument avoids the need for `librustc_hir` to depend on /// `librustc_session`. diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index ad189138054..a80c4be3e93 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -1,6 +1,6 @@ //! Support for serializing the dep-graph and reloading it. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(in_band_lifetimes)] #![feature(nll)] #![recursion_limit = "256"] diff --git a/compiler/rustc_index/Cargo.toml b/compiler/rustc_index/Cargo.toml index 6ac7c06ee83..6e1471df195 100644 --- a/compiler/rustc_index/Cargo.toml +++ b/compiler/rustc_index/Cargo.toml @@ -8,6 +8,6 @@ edition = "2018" doctest = false [dependencies] -arrayvec = "0.5.1" +arrayvec = { version = "0.5.1", default-features = false } rustc_serialize = { path = "../rustc_serialize" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index c43d1a6830d..8e00e54650d 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -28,13 +28,20 @@ pub const WORD_BITS: usize = WORD_BYTES * 8; /// will panic if the bitsets have differing domain sizes. /// /// [`GrowableBitSet`]: struct.GrowableBitSet.html -#[derive(Clone, Eq, PartialEq, Decodable, Encodable)] -pub struct BitSet<T: Idx> { +#[derive(Eq, PartialEq, Decodable, Encodable)] +pub struct BitSet<T> { domain_size: usize, words: Vec<Word>, marker: PhantomData<T>, } +impl<T> BitSet<T> { + /// Gets the domain size. + pub fn domain_size(&self) -> usize { + self.domain_size + } +} + impl<T: Idx> BitSet<T> { /// Creates a new, empty bitset with a given `domain_size`. #[inline] @@ -52,11 +59,6 @@ impl<T: Idx> BitSet<T> { result } - /// Gets the domain size. - pub fn domain_size(&self) -> usize { - self.domain_size - } - /// Clear all elements. #[inline] pub fn clear(&mut self) { @@ -75,12 +77,6 @@ impl<T: Idx> BitSet<T> { } } - /// Efficiently overwrite `self` with `other`. - pub fn overwrite(&mut self, other: &BitSet<T>) { - assert!(self.domain_size == other.domain_size); - self.words.clone_from_slice(&other.words); - } - /// Count the number of set bits in the set. pub fn count(&self) -> usize { self.words.iter().map(|e| e.count_ones() as usize).sum() @@ -243,6 +239,21 @@ impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> { } } +impl<T> Clone for BitSet<T> { + fn clone(&self) -> Self { + BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData } + } + + fn clone_from(&mut self, from: &Self) { + if self.domain_size != from.domain_size { + self.words.resize(from.domain_size, 0); + self.domain_size = from.domain_size; + } + + self.words.copy_from_slice(&from.words); + } +} + impl<T: Idx> fmt::Debug for BitSet<T> { fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { w.debug_list().entries(self.iter()).finish() @@ -363,7 +374,7 @@ const SPARSE_MAX: usize = 8; /// /// This type is used by `HybridBitSet`; do not use directly. #[derive(Clone, Debug)] -pub struct SparseBitSet<T: Idx> { +pub struct SparseBitSet<T> { domain_size: usize, elems: ArrayVec<[T; SPARSE_MAX]>, } @@ -464,18 +475,27 @@ impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> { /// All operations that involve an element will panic if the element is equal /// to or greater than the domain size. All operations that involve two bitsets /// will panic if the bitsets have differing domain sizes. -#[derive(Clone, Debug)] -pub enum HybridBitSet<T: Idx> { +#[derive(Clone)] +pub enum HybridBitSet<T> { Sparse(SparseBitSet<T>), Dense(BitSet<T>), } +impl<T: Idx> fmt::Debug for HybridBitSet<T> { + fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Sparse(b) => b.fmt(w), + Self::Dense(b) => b.fmt(w), + } + } +} + impl<T: Idx> HybridBitSet<T> { pub fn new_empty(domain_size: usize) -> Self { HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size)) } - fn domain_size(&self) -> usize { + pub fn domain_size(&self) -> usize { match self { HybridBitSet::Sparse(sparse) => sparse.domain_size, HybridBitSet::Dense(dense) => dense.domain_size, diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index ea32a1ae5a5..871fc4fafe2 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -340,7 +340,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind { + match *t.kind() { ty::Infer(ty::TyVar(vid)) => { debug!("canonical: type var found with vid {:?}", vid); match self.infcx.unwrap().probe_ty_var(vid) { @@ -418,7 +418,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { | ty::Foreign(..) | ty::Param(..) | ty::Opaque(..) => { - if t.flags.intersects(self.needs_canonical_flags) { + if t.flags().intersects(self.needs_canonical_flags) { t.super_fold_with(self) } else { t diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 0dbebac7e36..93e19521893 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -422,7 +422,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { match result_value.unpack() { GenericArgKind::Type(result_value) => { // e.g., here `result_value` might be `?0` in the example above... - if let ty::Bound(debruijn, b) = result_value.kind { + if let ty::Bound(debruijn, b) = *result_value.kind() { // ...in which case we would set `canonical_vars[0]` to `Some(?U)`. // We only allow a `ty::INNERMOST` index in substitutions. diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 3a54a647530..a540face4f2 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -35,6 +35,7 @@ use super::{InferCtxt, MiscVariable, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; use rustc_ast as ast; +use rustc_data_structures::mini_map::MiniMap; use rustc_hir::def_id::DefId; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::TypeError; @@ -42,7 +43,7 @@ use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{IntType, UintType}; -use rustc_span::DUMMY_SP; +use rustc_span::{Span, DUMMY_SP}; #[derive(Clone)] pub struct CombineFields<'infcx, 'tcx> { @@ -72,7 +73,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { { let a_is_expected = relation.a_is_expected(); - match (&a.kind, &b.kind) { + match (a.kind(), b.kind()) { // Relate integral variables to other types (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => { self.inner @@ -159,11 +160,11 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { } (ty::ConstKind::Infer(InferConst::Var(vid)), _) => { - return self.unify_const_variable(a_is_expected, vid, b); + return self.unify_const_variable(relation.param_env(), vid, b, a_is_expected); } (_, ty::ConstKind::Infer(InferConst::Var(vid))) => { - return self.unify_const_variable(!a_is_expected, vid, a); + return self.unify_const_variable(relation.param_env(), vid, a, !a_is_expected); } (ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => { // FIXME(#59490): Need to remove the leak check to accommodate @@ -187,17 +188,66 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { ty::relate::super_relate_consts(relation, a, b) } - pub fn unify_const_variable( + /// Unifies the const variable `target_vid` with the given constant. + /// + /// This also tests if the given const `ct` contains an inference variable which was previously + /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` + /// would result in an infinite type as we continously replace an inference variable + /// in `ct` with `ct` itself. + /// + /// This is especially important as unevaluated consts use their parents generics. + /// They therefore often contain unused substs, making these errors far more likely. + /// + /// A good example of this is the following: + /// + /// ```rust + /// #![feature(const_generics)] + /// + /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] { + /// todo!() + /// } + /// + /// fn main() { + /// let mut arr = Default::default(); + /// arr = bind(arr); + /// } + /// ``` + /// + /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// of `fn bind` (meaning that its substs contain `N`). + /// + /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. + /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. + /// + /// As `3 + 4` contains `N` in its substs, this must not succeed. + /// + /// See `src/test/ui/const-generics/occurs-check/` for more examples where this is relevant. + fn unify_const_variable( &self, + param_env: ty::ParamEnv<'tcx>, + target_vid: ty::ConstVid<'tcx>, + ct: &'tcx ty::Const<'tcx>, vid_is_expected: bool, - vid: ty::ConstVid<'tcx>, - value: &'tcx ty::Const<'tcx>, ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + let (for_universe, span) = { + let mut inner = self.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); + let var_value = variable_table.probe_value(target_vid); + match var_value.val { + ConstVariableValue::Known { value } => { + bug!("instantiating {:?} which has a known value {:?}", target_vid, value) + } + ConstVariableValue::Unknown { universe } => (universe, var_value.origin.span), + } + }; + let value = ConstInferUnifier { infcx: self, span, param_env, for_universe, target_vid } + .relate(ct, ct)?; + self.inner .borrow_mut() .const_unification_table() .unify_var_value( - vid, + target_vid, ConstVarValue { origin: ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, @@ -206,8 +256,8 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { val: ConstVariableValue::Known { value }, }, ) - .map_err(|e| const_unification_error(vid_is_expected, e))?; - Ok(value) + .map(|()| value) + .map_err(|e| const_unification_error(vid_is_expected, e)) } fn unify_integral_variable( @@ -362,7 +412,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { let for_universe = match self.infcx.inner.borrow_mut().type_variables().probe(for_vid) { v @ TypeVariableValue::Known { .. } => { - panic!("instantiating {:?} which has a known value {:?}", for_vid, v,) + bug!("instantiating {:?} which has a known value {:?}", for_vid, v,) } TypeVariableValue::Unknown { universe } => universe, }; @@ -379,6 +429,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { needs_wf: false, root_ty: ty, param_env: self.param_env, + cache: MiniMap::new(), }; let ty = match generalize.relate(ty, ty) { @@ -438,6 +489,8 @@ struct Generalizer<'cx, 'tcx> { root_ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>, + + cache: MiniMap<Ty<'tcx>, RelateResult<'tcx, Ty<'tcx>>>, } /// Result from a generalization operation. This includes @@ -535,13 +588,16 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { assert_eq!(t, t2); // we are abusing TypeRelation here; both LHS and RHS ought to be == + if let Some(result) = self.cache.get(&t) { + return result.clone(); + } debug!("generalize: t={:?}", t); // Check to see whether the type we are generalizing references // any other type variable related to `vid` via // subtyping. This is basically our "occurs check", preventing // us from creating infinitely sized types. - match t.kind { + let result = match *t.kind() { ty::Infer(ty::TyVar(vid)) => { let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid); let sub_vid = self.infcx.inner.borrow_mut().type_variables().sub_root_var(vid); @@ -598,7 +654,10 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { Ok(t) } _ => relate::super_relate_tys(self, t, t), - } + }; + + self.cache.insert(t, result.clone()); + return result; } fn regions( @@ -671,7 +730,6 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } } } - ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(c), _ => relate::super_relate_consts(self, c, c), } } @@ -721,3 +779,175 @@ fn float_unification_error<'tcx>( let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v; TypeError::FloatMismatch(ty::relate::expected_found_bool(a_is_expected, a, b)) } + +struct ConstInferUnifier<'cx, 'tcx> { + infcx: &'cx InferCtxt<'cx, 'tcx>, + + span: Span, + + param_env: ty::ParamEnv<'tcx>, + + for_universe: ty::UniverseIndex, + + /// The vid of the const variable that is in the process of being + /// instantiated; if we find this within the const we are folding, + /// that means we would have created a cyclic const. + target_vid: ty::ConstVid<'tcx>, +} + +// We use `TypeRelation` here to propagate `RelateResult` upwards. +// +// Both inputs are expected to be the same. +impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } + + fn tag(&self) -> &'static str { + "ConstInferUnifier" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + _variance: ty::Variance, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + // We don't care about variance here. + self.relate(a, b) + } + + fn binders<T>( + &mut self, + a: ty::Binder<T>, + b: ty::Binder<T>, + ) -> RelateResult<'tcx, ty::Binder<T>> + where + T: Relate<'tcx>, + { + Ok(ty::Binder::bind(self.relate(a.skip_binder(), b.skip_binder())?)) + } + + fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + debug_assert_eq!(t, _t); + debug!("ConstInferUnifier: t={:?}", t); + + match t.kind() { + &ty::Infer(ty::TyVar(vid)) => { + let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid); + let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid); + match probe { + TypeVariableValue::Known { value: u } => { + debug!("ConstOccursChecker: known value {:?}", u); + self.tys(u, u) + } + TypeVariableValue::Unknown { universe } => { + if self.for_universe.can_name(universe) { + return Ok(t); + } + + let origin = + *self.infcx.inner.borrow_mut().type_variables().var_origin(vid); + let new_var_id = self.infcx.inner.borrow_mut().type_variables().new_var( + self.for_universe, + false, + origin, + ); + let u = self.tcx().mk_ty_var(new_var_id); + debug!( + "ConstInferUnifier: replacing original vid={:?} with new={:?}", + vid, u + ); + Ok(u) + } + } + } + _ => relate::super_relate_tys(self, t, t), + } + } + + fn regions( + &mut self, + r: ty::Region<'tcx>, + _r: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug_assert_eq!(r, _r); + debug!("ConstInferUnifier: r={:?}", r); + + match r { + // Never make variables for regions bound within the type itself, + // nor for erased regions. + ty::ReLateBound(..) | ty::ReErased => { + return Ok(r); + } + + ty::RePlaceholder(..) + | ty::ReVar(..) + | ty::ReEmpty(_) + | ty::ReStatic + | ty::ReEarlyBound(..) + | ty::ReFree(..) => { + // see common code below + } + } + + let r_universe = self.infcx.universe_of_region(r); + if self.for_universe.can_name(r_universe) { + return Ok(r); + } else { + // FIXME: This is non-ideal because we don't give a + // very descriptive origin for this region variable. + Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.span), self.for_universe)) + } + } + + fn consts( + &mut self, + c: &'tcx ty::Const<'tcx>, + _c: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + debug_assert_eq!(c, _c); + debug!("ConstInferUnifier: c={:?}", c); + + 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) { + return Err(TypeError::CyclicConst(c)); + } + + let var_value = variable_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 }, + }); + Ok(self.tcx().mk_const_var(new_var_id, c.ty)) + } + } + } + } + _ => relate::super_relate_consts(self, c, c), + } + } +} diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index 7de752d1de0..7c388b5503e 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -77,7 +77,7 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> { debug!("{}.tys: replacements ({:?}, {:?})", self.tag(), a, b); - match (&a.kind, &b.kind) { + match (a.kind(), b.kind()) { (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { infcx.inner.borrow_mut().type_variables().equate(a_id, b_id); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index d72ed72e3a8..bcfcee23d13 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -70,7 +70,7 @@ use rustc_middle::ty::{ subst::{Subst, SubstsRef}, Region, Ty, TyCtxt, TypeFoldable, }; -use rustc_span::{DesugaringKind, Pos, Span}; +use rustc_span::{BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; use std::{cmp, fmt}; @@ -570,7 +570,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // if they are both "path types", there's a chance of ambiguity // due to different versions of the same crate if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) = - (&exp_found.expected.kind, &exp_found.found.kind) + (exp_found.expected.kind(), exp_found.found.kind()) { report_path_match(err, exp_adt.did, found_adt.did); } @@ -617,11 +617,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ref prior_arms, last_ty, scrut_hir_id, + opt_suggest_box_span, + arm_span, .. }) => match source { hir::MatchSource::IfLetDesugar { .. } => { let msg = "`if let` arms have incompatible types"; err.span_label(cause.span, msg); + if let Some(ret_sp) = opt_suggest_box_span { + self.suggest_boxing_for_return_impl_trait( + err, + ret_sp, + prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s), + ); + } } hir::MatchSource::TryDesugar => { if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found { @@ -675,9 +684,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } + if let Some(ret_sp) = opt_suggest_box_span { + // Get return type span and point to it. + self.suggest_boxing_for_return_impl_trait( + err, + ret_sp, + prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s), + ); + } } }, - ObligationCauseCode::IfExpression(box IfExpressionCause { then, outer, semicolon }) => { + ObligationCauseCode::IfExpression(box IfExpressionCause { + then, + else_sp, + outer, + semicolon, + opt_suggest_box_span, + }) => { err.span_label(then, "expected because of this"); if let Some(sp) = outer { err.span_label(sp, "`if` and `else` have incompatible types"); @@ -690,11 +713,48 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } + if let Some(ret_sp) = opt_suggest_box_span { + self.suggest_boxing_for_return_impl_trait( + err, + ret_sp, + vec![then, else_sp].into_iter(), + ); + } } _ => (), } } + fn suggest_boxing_for_return_impl_trait( + &self, + err: &mut DiagnosticBuilder<'tcx>, + return_sp: Span, + arm_spans: impl Iterator<Item = Span>, + ) { + err.multipart_suggestion( + "you could change the return type to be a boxed trait object", + vec![ + (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()), + (return_sp.shrink_to_hi(), ">".to_string()), + ], + Applicability::MaybeIncorrect, + ); + let sugg = arm_spans + .flat_map(|sp| { + vec![ + (sp.shrink_to_lo(), "Box::new(".to_string()), + (sp.shrink_to_hi(), ")".to_string()), + ] + .into_iter() + }) + .collect::<Vec<_>>(); + err.multipart_suggestion( + "if you change the return type to expect trait objects, box the returned expressions", + sugg, + Applicability::MaybeIncorrect, + ); + } + /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value` /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and /// populate `other_value` with `other_ty`. @@ -796,7 +856,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); return Some(()); } - if let &ty::Adt(def, _) = &ta.kind { + if let &ty::Adt(def, _) = ta.kind() { let path_ = self.tcx.def_path_str(def.did); if path_ == other_path { self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); @@ -977,11 +1037,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Compares two given types, eliding parts that are the same between them and highlighting /// relevant differences, and return two representation of those types for highlighted printing. fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) { - debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind, t2, t2.kind); + debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind(), t2, t2.kind()); // helper functions fn equals<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.kind, &b.kind) { + match (a.kind(), b.kind()) { (a, b) if *a == *b => true, (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_))) | ( @@ -1014,7 +1074,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } // process starts here - match (&t1.kind, &t2.kind) { + match (t1.kind(), t2.kind()) { (&ty::Adt(def1, sub1), &ty::Adt(def2, sub2)) => { let sub_no_defaults_1 = self.strip_generic_default_params(def1.did, sub1); let sub_no_defaults_2 = self.strip_generic_default_params(def2.did, sub2); @@ -1476,7 +1536,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; match (&terr, expected == found) { (TypeError::Sorts(values), extra) => { - let sort_string = |ty: Ty<'tcx>| match (extra, &ty.kind) { + let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) { (true, ty::Opaque(def_id, _)) => format!( " (opaque type at {})", self.tcx @@ -1563,7 +1623,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_span, exp_found.expected, exp_found.found ); - if let ty::Opaque(def_id, _) = exp_found.expected.kind { + if let ty::Opaque(def_id, _) = *exp_found.expected.kind() { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); // Future::Output let item_def_id = self @@ -1616,9 +1676,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { diag: &mut DiagnosticBuilder<'tcx>, ) { if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) = - (&exp_found.expected.kind, &exp_found.found.kind) + (exp_found.expected.kind(), exp_found.found.kind()) { - if let ty::Adt(found_def, found_substs) = found_ty.kind { + if let ty::Adt(found_def, found_substs) = *found_ty.kind() { let path_str = format!("{:?}", exp_def); if exp_def == &found_def { let opt_msg = "you can convert from `&Option<T>` to `Option<&T>` using \ @@ -1637,9 +1697,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { let mut show_suggestion = true; for (exp_ty, found_ty) in exp_substs.types().zip(found_substs.types()) { - match exp_ty.kind { + match *exp_ty.kind() { ty::Ref(_, exp_ty, _) => { - match (&exp_ty.kind, &found_ty.kind) { + match (exp_ty.kind(), found_ty.kind()) { (_, ty::Param(_)) | (_, ty::Infer(_)) | (ty::Param(_), _) @@ -1989,7 +2049,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); if let Some(infer::RelateParamBound(_, t)) = origin { let t = self.resolve_vars_if_possible(&t); - match t.kind { + match t.kind() { // We've got: // fn get_later<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ // suggest: @@ -2093,7 +2153,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => String::new(), }; if !s.is_empty() { - s.push_str(" "); + s.push(' '); } s }; @@ -2231,7 +2291,7 @@ impl TyCategory { } pub fn from_ty(ty: Ty<'_>) -> Option<(Self, DefId)> { - match ty.kind { + match *ty.kind() { ty::Closure(def_id, _) => Some((Self::Closure, def_id)), ty::Opaque(def_id, _) => Some((Self::Opaque, def_id)), ty::Generator(def_id, ..) => Some((Self::Generator, def_id)), 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 bf087dfacfa..f87406c2ce4 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 @@ -6,9 +6,10 @@ use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, 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, Ty}; +use rustc_middle::ty::{self, DefIdTree, InferConst, Ty}; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::kw; use rustc_span::Span; @@ -53,7 +54,7 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { inner == self.target || match (inner.unpack(), self.target.unpack()) { (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { - match (&inner_ty.kind, &target_ty.kind) { + match (inner_ty.kind(), target_ty.kind()) { ( &ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid)), @@ -222,7 +223,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ty: Ty<'tcx>, highlight: Option<ty::print::RegionHighlightMode>, ) -> (String, Option<Span>, Cow<'static, str>, Option<String>, Option<&'static str>) { - if let ty::Infer(ty::TyVar(ty_vid)) = ty.kind { + if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() { let mut inner = self.inner.borrow_mut(); let ty_vars = &inner.type_variables(); let var_origin = ty_vars.var_origin(ty_vid); @@ -288,7 +289,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { None }; printer.name_resolver = Some(Box::new(&getter)); - let _ = if let ty::FnDef(..) = ty.kind { + let _ = if let ty::FnDef(..) = ty.kind() { // We don't want the regular output for `fn`s because it includes its path in // invalid pseudo-syntax, we want the `fn`-pointer output instead. ty.fn_sig(self.tcx).print(printer) @@ -336,7 +337,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ty_msg = match (local_visitor.found_node_ty, local_visitor.found_exact_method_call) { (_, Some(_)) => String::new(), - (Some(ty::TyS { kind: ty::Closure(_, substs), .. }), _) => { + (Some(ty), _) if ty.is_closure() => { + let substs = + if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() }; let fn_sig = substs.as_closure().sig(); let args = closure_args(&fn_sig); let ret = fn_sig.output().skip_binder().to_string(); @@ -370,7 +373,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); let suffix = match local_visitor.found_node_ty { - Some(ty::TyS { kind: ty::Closure(_, substs), .. }) => { + Some(ty) if ty.is_closure() => { + let substs = + if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() }; let fn_sig = substs.as_closure().sig(); let ret = fn_sig.output().skip_binder().to_string(); @@ -565,14 +570,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { local_visitor.visit_expr(expr); } + let mut param_name = None; + let span = if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.val { + let origin = self.inner.borrow_mut().const_unification_table().probe_value(vid).origin; + if let ConstVariableOriginKind::ConstParameterDefinition(param) = origin.kind { + param_name = Some(param); + } + origin.span + } else { + local_visitor.target_span + }; + let error_code = error_code.into(); - let mut err = self.tcx.sess.struct_span_err_with_code( - local_visitor.target_span, - "type annotations needed", - error_code, - ); + let mut err = + self.tcx.sess.struct_span_err_with_code(span, "type annotations needed", error_code); - err.note("unable to infer the value of a const parameter"); + if let Some(param_name) = param_name { + err.note(&format!("cannot infer the value of the const parameter `{}`", param_name)); + } else { + err.note("unable to infer the value of a const parameter"); + } err } @@ -611,11 +628,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let sig = self.tcx.fn_sig(did); let bound_output = sig.output(); let output = bound_output.skip_binder(); - err.span_label(e.span, &format!("this method call resolves to `{:?}`", output)); - let kind = &output.kind; + err.span_label(e.span, &format!("this method call resolves to `{}`", output)); + let kind = output.kind(); if let ty::Projection(proj) = kind { if let Some(span) = self.tcx.hir().span_if_local(proj.item_def_id) { - err.span_label(span, &format!("`{:?}` defined here", output)); + err.span_label(span, &format!("`{}` defined here", output)); } } } 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 89142edb2dc..e3c613b1d6a 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 @@ -85,7 +85,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { debug!("try_report_named_anon_conflict: ret ty {:?}", ty); if sub == &ty::ReStatic - && v.0.into_iter().find(|t| t.span.desugaring_kind().is_none()).is_some() + && 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 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 7493b8b0a9f..441cfeea20a 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 @@ -465,7 +465,7 @@ struct TraitObjectVisitor(Vec<DefId>); impl TypeVisitor<'_> for TraitObjectVisitor { fn visit_ty(&mut self, t: Ty<'_>) -> bool { - match t.kind { + match t.kind() { ty::Dynamic(preds, RegionKind::ReStatic) => { if let Some(def_id) = preds.principal_def_id() { self.0.push(def_id); @@ -488,18 +488,16 @@ impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor { } fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { - match t.kind { - TyKind::TraitObject( - poly_trait_refs, - Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. }, - ) => { - for ptr in poly_trait_refs { - if Some(self.1) == ptr.trait_ref.trait_def_id() { - self.0.push(ptr.span); - } + if let TyKind::TraitObject( + poly_trait_refs, + Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. }, + ) = t.kind + { + for ptr in poly_trait_refs { + if Some(self.1) == ptr.trait_ref.trait_def_id() { + self.0.push(ptr.span); } } - _ => {} } walk_ty(self, t); } 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 788eabf296d..c061f485c1c 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 @@ -58,8 +58,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { .tcx() .sess .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); - err.span_label(sp, &format!("found `{:?}`", found)); - err.span_label(trait_sp, &format!("expected `{:?}`", expected)); + err.span_label(sp, &format!("found `{}`", found)); + err.span_label(trait_sp, &format!("expected `{}`", expected)); // Get the span of all the used type parameters in the method. let assoc_item = self.tcx().associated_item(trait_def_id); @@ -92,7 +92,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { 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, 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 6e2d49f1ad7..c055fed43f6 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 @@ -95,7 +95,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { decl: &hir::FnDecl<'_>, ) -> Option<Span> { let ret_ty = self.tcx().type_of(scope_def_id); - if let ty::FnDef(_, _) = ret_ty.kind { + if let ty::FnDef(_, _) = ret_ty.kind() { let sig = ret_ty.fn_sig(self.tcx()); let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&sig.output()); diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 02bebe10ed0..b3d7876c6e8 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -144,7 +144,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { let tcx = self.infcx.tcx; - match t.kind { + match *t.kind() { ty::Infer(ty::TyVar(v)) => { let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy) diff --git a/compiler/rustc_infer/src/infer/fudge.rs b/compiler/rustc_infer/src/infer/fudge.rs index c6651108df5..d7bc636db8f 100644 --- a/compiler/rustc_infer/src/infer/fudge.rs +++ b/compiler/rustc_infer/src/infer/fudge.rs @@ -182,7 +182,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { + match *ty.kind() { ty::Infer(ty::InferTy::TyVar(vid)) => { if self.type_vars.0.contains(&vid) { // This variable was created during the fudging. diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs index 1bf43e74dcd..c47d4769637 100644 --- a/compiler/rustc_infer/src/infer/lattice.rs +++ b/compiler/rustc_infer/src/infer/lattice.rs @@ -58,7 +58,7 @@ where let infcx = this.infcx(); let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); - match (&a.kind, &b.kind) { + match (a.kind(), b.kind()) { // If one side is known to be a variable and one is not, // create a variable (`v`) to represent the LUB. Make sure to // relate `v` to the non-type-variable first (by passing it diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs index 3e2ea3d0f8f..9f43fac0916 100644 --- a/compiler/rustc_infer/src/infer/lub.rs +++ b/compiler/rustc_infer/src/infer/lub.rs @@ -50,7 +50,7 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> { ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), ty::Covariant => self.relate(a, b), // FIXME(#41044) -- not correct, need test - ty::Bivariant => Ok(a.clone()), + ty::Bivariant => Ok(a), ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b), } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 3744ad5d032..2cbdc954e20 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -21,7 +21,7 @@ use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; use rustc_middle::mir; -use rustc_middle::mir::interpret::ConstEvalResult; +use rustc_middle::mir::interpret::EvalToConstValueResult; use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; @@ -680,7 +680,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool { - match ty.kind { + match *ty.kind() { ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid), _ => false, } @@ -693,7 +693,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric { use rustc_middle::ty::error::UnconstrainedNumeric::Neither; use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; - match ty.kind { + match *ty.kind() { ty::Infer(ty::IntVar(vid)) => { if self.inner.borrow_mut().int_unification_table().probe_value(vid).is_some() { Neither @@ -1542,7 +1542,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { substs: SubstsRef<'tcx>, promoted: Option<mir::Promoted>, span: Option<Span>, - ) -> ConstEvalResult<'tcx> { + ) -> EvalToConstValueResult<'tcx> { let mut original_values = OriginalQueryValues::default(); let canonical = self.canonicalize_query(&(param_env, substs), &mut original_values); @@ -1557,7 +1557,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// not a type variable, just return it unmodified. // FIXME(eddyb) inline into `ShallowResolver::visit_ty`. fn shallow_resolve_ty(&self, typ: Ty<'tcx>) -> Ty<'tcx> { - match typ.kind { + match *typ.kind() { ty::Infer(ty::TyVar(v)) => { // Not entirely obvious: if `typ` is a type variable, // it can be resolved to an int/float variable, which @@ -1677,7 +1677,7 @@ impl TyOrConstInferVar<'tcx> { /// Tries to extract an inference variable from a type, returns `None` /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`). pub fn maybe_from_ty(ty: Ty<'tcx>) -> Option<Self> { - match ty.kind { + match *ty.kind() { ty::Infer(ty::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)), ty::Infer(ty::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)), ty::Infer(ty::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)), diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 3f5ed36035c..839891f322c 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -265,7 +265,7 @@ where use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_span::DUMMY_SP; - match value_ty.kind { + match *value_ty.kind() { ty::Projection(other_projection_ty) => { let var = self.infcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, @@ -311,7 +311,7 @@ where // This only presently applies to chalk integration, as NLL // doesn't permit type variables to appear on both sides (and // doesn't use lazy norm). - match value_ty.kind { + match *value_ty.kind() { ty::Infer(ty::TyVar(value_vid)) => { // Two type variables: just equate them. self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid); @@ -531,7 +531,7 @@ where } } - match (&a.kind, &b.kind) { + match (a.kind(), b.kind()) { (_, &ty::Infer(ty::TyVar(vid))) => { if D::forbid_inference_vars() { // Forbid inference variables in the RHS. @@ -868,7 +868,7 @@ where debug!("TypeGeneralizer::tys(a={:?})", a); - match a.kind { + match *a.kind() { ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) if D::forbid_inference_vars() => { diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index a1e7f1fa3e5..de98cccf256 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -26,7 +26,8 @@ pub fn explicit_outlives_bounds<'tcx>( | ty::PredicateAtom::ClosureKind(..) | ty::PredicateAtom::TypeOutlives(..) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => None, + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => { Some(OutlivesBound::RegionSubRegion(r_b, r_a)) } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 48f6d937f2f..2851da89ab2 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -383,7 +383,7 @@ where // #55756) in cases where you have e.g., `<T as Foo<'a>>::Item: // 'a` in the environment but `trait Foo<'b> { type Item: 'b // }` in the trait definition. - approx_env_bounds.retain(|bound| match bound.0.kind { + approx_env_bounds.retain(|bound| match *bound.0.kind() { ty::Projection(projection_ty) => self .verify_bound .projection_declared_bounds_from_trait(projection_ty) diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 8f20b5743df..21b0836563f 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -1,6 +1,7 @@ use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::{GenericKind, VerifyBound}; use rustc_data_structures::captures::Captures; +use rustc_data_structures::mini_set::MiniSet; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -31,16 +32,23 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// Returns a "verify bound" that encodes what we know about /// `generic` and the regions it outlives. pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> { + let mut visited = MiniSet::new(); match generic { GenericKind::Param(param_ty) => self.param_bound(param_ty), - GenericKind::Projection(projection_ty) => self.projection_bound(projection_ty), + GenericKind::Projection(projection_ty) => { + self.projection_bound(projection_ty, &mut visited) + } } } - fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - match ty.kind { + fn type_bound( + &self, + ty: Ty<'tcx>, + visited: &mut MiniSet<GenericArg<'tcx>>, + ) -> VerifyBound<'tcx> { + match *ty.kind() { ty::Param(p) => self.param_bound(p), - ty::Projection(data) => self.projection_bound(data), + ty::Projection(data) => self.projection_bound(data, visited), ty::FnDef(_, substs) => { // HACK(eddyb) ignore lifetimes found shallowly in `substs`. // This is inconsistent with `ty::Adt` (including all substs), @@ -50,9 +58,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { let mut bounds = substs .iter() .filter_map(|child| match child.unpack() { - GenericArgKind::Type(ty) => Some(self.type_bound(ty)), + GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)), GenericArgKind::Lifetime(_) => None, - GenericArgKind::Const(_) => Some(self.recursive_bound(child)), + GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)), }) .filter(|bound| { // Remove bounds that must hold, since they are not interesting. @@ -66,7 +74,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { ), } } - _ => self.recursive_bound(ty.into()), + _ => self.recursive_bound(ty.into(), visited), } } @@ -118,7 +126,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); let erased_projection_ty = self.tcx.erase_regions(&projection_ty); self.declared_generic_bounds_from_env_with_compare_fn(|ty| { - if let ty::Projection(..) = ty.kind { + if let ty::Projection(..) = ty.kind() { let erased_ty = self.tcx.erase_regions(&ty); erased_ty == erased_projection_ty } else { @@ -137,7 +145,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { self.declared_projection_bounds_from_trait(projection_ty) } - pub fn projection_bound(&self, projection_ty: ty::ProjectionTy<'tcx>) -> VerifyBound<'tcx> { + pub fn projection_bound( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + visited: &mut MiniSet<GenericArg<'tcx>>, + ) -> VerifyBound<'tcx> { debug!("projection_bound(projection_ty={:?})", projection_ty); let projection_ty_as_ty = @@ -166,21 +178,25 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // see the extensive comment in projection_must_outlive let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - let recursive_bound = self.recursive_bound(ty.into()); + let recursive_bound = self.recursive_bound(ty.into(), visited); VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound) } - fn recursive_bound(&self, parent: GenericArg<'tcx>) -> VerifyBound<'tcx> { + fn recursive_bound( + &self, + parent: GenericArg<'tcx>, + visited: &mut MiniSet<GenericArg<'tcx>>, + ) -> VerifyBound<'tcx> { let mut bounds = parent - .walk_shallow() + .walk_shallow(visited) .filter_map(|child| match child.unpack() { - GenericArgKind::Type(ty) => Some(self.type_bound(ty)), + GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)), GenericArgKind::Lifetime(lt) => { // Ignore late-bound regions. if !lt.is_late_bound() { Some(VerifyBound::OutlivedBy(lt)) } else { None } } - GenericArgKind::Const(_) => Some(self.recursive_bound(child)), + GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)), }) .filter(|bound| { // Remove bounds that must hold, since they are not interesting. diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 74f365ced23..337772d70b8 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -124,7 +124,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { let t = self.infcx.shallow_resolve(t); if t.has_infer_types() { - if let ty::Infer(infer_ty) = t.kind { + if let ty::Infer(infer_ty) = *t.kind() { // Since we called `shallow_resolve` above, this must // be an (as yet...) unresolved inference variable. let ty_var_span = if let ty::TyVar(ty_vid) = infer_ty { @@ -191,7 +191,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { t // micro-optimize -- if there is nothing in this type that this fold affects... } else { let t = self.infcx.shallow_resolve(t); - match t.kind { + match *t.kind() { ty::Infer(ty::TyVar(vid)) => { self.err = Some(FixupError::UnresolvedTy(vid)); self.tcx().ty_error() diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index 308f884f9a6..a676c5e65a7 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -83,7 +83,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { let infcx = self.fields.infcx; let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); - match (&a.kind, &b.kind) { + match (a.kind(), b.kind()) { (&ty::Infer(TyVar(a_vid)), &ty::Infer(TyVar(b_vid))) => { // Shouldn't have any LBR here, so we can safely put // this under a binder below without fear of accidental diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 53c7dcc6377..35b97fff3da 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -306,7 +306,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// instantiated, then return the with which it was /// instantiated. Otherwise, returns `t`. pub fn replace_if_possible(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind { + match *t.kind() { ty::Infer(ty::TyVar(v)) => match self.probe(v) { TypeVariableValue::Unknown { .. } => t, TypeVariableValue::Known { value } => value, diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index e05041d8846..ea9a4661348 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -12,8 +12,7 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(bindings_after_at)] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] @@ -23,7 +22,6 @@ #![feature(never_type)] #![feature(or_patterns)] #![feature(in_band_lifetimes)] -#![feature(crate_visibility_modifier)] #![recursion_limit = "512"] // For rustdoc #[macro_use] diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 93fc7f1f3b8..9c0d934a035 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -236,6 +236,9 @@ impl Elaborator<'tcx> { .map(|predicate| predicate_obligation(predicate, None)), ); } + ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + // Nothing to elaborate + } } } } diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 9affe4ec6d8..e214493a567 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -43,7 +43,6 @@ rustc_resolve = { path = "../rustc_resolve" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty = { path = "../rustc_ty" } tempfile = "3.0.5" -once_cell = "1" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["libloaderapi"] } diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index fe40c615f79..88d2efe96d1 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -4,6 +4,7 @@ #![feature(nll)] #![feature(generator_trait)] #![feature(generators)] +#![feature(once_cell)] #![recursion_limit = "256"] mod callbacks; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 403aea8b304..66d3765d347 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -2,7 +2,6 @@ use crate::interface::{Compiler, Result}; use crate::proc_macro_decls; use crate::util; -use once_cell::sync::Lazy; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::{self as ast, visit}; use rustc_codegen_ssa::back::link::emit_metadata; @@ -46,6 +45,7 @@ use std::any::Any; use std::cell::RefCell; use std::ffi::OsString; use std::io::{self, BufWriter, Write}; +use std::lazy::SyncLazy; use std::path::PathBuf; use std::rc::Rc; use std::{env, fs, iter, mem}; @@ -291,6 +291,7 @@ fn configure_and_expand_inner<'a>( trace_mac: sess.opts.debugging_opts.trace_macros, should_test: sess.opts.test, span_debug: sess.opts.debugging_opts.span_debug, + proc_macro_backtrace: sess.opts.debugging_opts.proc_macro_backtrace, ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) }; @@ -680,7 +681,7 @@ pub fn prepare_outputs( Ok(outputs) } -pub static DEFAULT_QUERY_PROVIDERS: Lazy<Providers> = Lazy::new(|| { +pub static DEFAULT_QUERY_PROVIDERS: SyncLazy<Providers> = SyncLazy::new(|| { let providers = &mut Providers::default(); providers.analysis = analysis; proc_macro_decls::provide(providers); @@ -703,7 +704,7 @@ pub static DEFAULT_QUERY_PROVIDERS: Lazy<Providers> = Lazy::new(|| { *providers }); -pub static DEFAULT_EXTERN_QUERY_PROVIDERS: Lazy<Providers> = Lazy::new(|| { +pub static DEFAULT_EXTERN_QUERY_PROVIDERS: SyncLazy<Providers> = SyncLazy::new(|| { let mut extern_providers = *DEFAULT_QUERY_PROVIDERS; rustc_metadata::provide_extern(&mut extern_providers); rustc_codegen_ssa::provide_extern(&mut extern_providers); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index e94745519a4..72e10bc4304 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -40,6 +40,7 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) { DiagnosticOutput::Default, Default::default(), None, + None, ); (sess, cfg) } @@ -402,6 +403,7 @@ fn test_codegen_options_tracking_hash() { // `link_arg` is omitted because it just forwards to `link_args`. untracked!(link_args, vec![String::from("abc"), String::from("def")]); untracked!(link_dead_code, Some(true)); + untracked!(link_self_contained, Some(true)); untracked!(linker, Some(PathBuf::from("linker"))); untracked!(linker_flavor, Some(LinkerFlavor::Gcc)); untracked!(no_stack_check, true); @@ -502,6 +504,7 @@ fn test_debugging_options_tracking_hash() { untracked!(print_llvm_passes, true); untracked!(print_mono_items, Some(String::from("abc"))); untracked!(print_type_sizes, true); + untracked!(proc_macro_backtrace, true); untracked!(query_dep_graph, true); untracked!(query_stats, true); untracked!(save_analysis, true); @@ -516,6 +519,7 @@ fn test_debugging_options_tracking_hash() { untracked!(time_llvm_passes, true); untracked!(time_passes, true); untracked!(trace_macros, true); + untracked!(trim_diagnostic_paths, false); untracked!(ui_testing, true); untracked!(unpretty, Some("expanded".to_string())); untracked!(unstable_options, true); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 8816ba198cf..0eed6938c31 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -25,6 +25,7 @@ use rustc_span::symbol::{sym, Symbol}; use smallvec::SmallVec; use std::env; use std::io::{self, Write}; +use std::lazy::SyncOnceCell; use std::mem; use std::ops::DerefMut; use std::path::{Path, PathBuf}; @@ -64,6 +65,10 @@ pub fn create_session( lint_caps: FxHashMap<lint::LintId, lint::Level>, descriptions: Registry, ) -> (Lrc<Session>, Lrc<Box<dyn CodegenBackend>>) { + let codegen_backend = get_codegen_backend(&sopts); + // target_override is documented to be called before init(), so this is okay + let target_override = codegen_backend.target_override(&sopts); + let mut sess = session::build_session( sopts, input_path, @@ -71,9 +76,10 @@ pub fn create_session( diagnostic_output, lint_caps, file_loader, + target_override, ); - let codegen_backend = get_codegen_backend(&sess); + codegen_backend.init(&sess); let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg)); add_configuration(&mut cfg, &mut sess, &*codegen_backend); @@ -218,13 +224,13 @@ fn load_backend_from_dylib(path: &Path) -> fn() -> Box<dyn CodegenBackend> { } } -pub fn get_codegen_backend(sess: &Session) -> Box<dyn CodegenBackend> { +pub fn get_codegen_backend(sopts: &config::Options) -> Box<dyn CodegenBackend> { static INIT: Once = Once::new(); static mut LOAD: fn() -> Box<dyn CodegenBackend> = || unreachable!(); INIT.call_once(|| { - let codegen_name = sess.opts.debugging_opts.codegen_backend.as_deref().unwrap_or("llvm"); + let codegen_name = sopts.debugging_opts.codegen_backend.as_deref().unwrap_or("llvm"); let backend = match codegen_name { filename if filename.contains('.') => load_backend_from_dylib(filename.as_ref()), codegen_name => get_builtin_codegen_backend(codegen_name), @@ -234,17 +240,14 @@ pub fn get_codegen_backend(sess: &Session) -> Box<dyn CodegenBackend> { LOAD = backend; } }); - let backend = unsafe { LOAD() }; - backend.init(sess); - backend + unsafe { LOAD() } } // This is used for rustdoc, but it uses similar machinery to codegen backend // loading, so we leave the code here. It is potentially useful for other tools // that want to invoke the rustc binary while linking to rustc as well. pub fn rustc_path<'a>() -> Option<&'a Path> { - static RUSTC_PATH: once_cell::sync::OnceCell<Option<PathBuf>> = - once_cell::sync::OnceCell::new(); + static RUSTC_PATH: SyncOnceCell<Option<PathBuf>> = SyncOnceCell::new(); const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR"); @@ -693,6 +696,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { rules, id: resolver.next_node_id(), span: rustc_span::DUMMY_SP, + tokens: None, } } @@ -709,6 +713,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { id: resolver.next_node_id(), kind: ast::StmtKind::Expr(expr), span: rustc_span::DUMMY_SP, + tokens: None, } } @@ -725,6 +730,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { id: self.resolver.next_node_id(), span: rustc_span::DUMMY_SP, kind: ast::StmtKind::Expr(loop_expr), + tokens: None, }; if self.within_static_or_const { diff --git a/compiler/rustc_lexer/Cargo.toml b/compiler/rustc_lexer/Cargo.toml index 28b56f6fef4..12101776de2 100644 --- a/compiler/rustc_lexer/Cargo.toml +++ b/compiler/rustc_lexer/Cargo.toml @@ -14,11 +14,10 @@ Rust lexer used by rustc. No stability guarantees are provided. # This will be used when publishing this crate as `rustc-ap-rustc_lexer`. [lib] doctest = false -name = "rustc_lexer" # Note that this crate purposefully does not depend on other rustc crates [dependencies] unicode-xid = "0.2.0" [dev-dependencies] -expect-test = "0.1" +expect-test = "1.0" diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index b7d6194cd77..d784a86f14c 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -2,7 +2,7 @@ //! //! The idea with `librustc_lexer` is to make a reusable library, //! by separating out pure lexing and rustc-specific concerns, like spans, -//! error reporting an interning. So, rustc_lexer operates directly on `&str`, +//! error reporting, and interning. So, rustc_lexer operates directly on `&str`, //! produces simple tokens which are a pair of type-tag and a bit of original text, //! and does not report errors, instead storing them as flags on the token. //! @@ -191,12 +191,16 @@ pub fn strip_shebang(input: &str) -> Option<usize> { // For simplicity we consider any line starting with `#!` a shebang, // regardless of restrictions put on shebangs by specific platforms. if let Some(input_tail) = input.strip_prefix("#!") { - // Ok, this is a shebang but if the next non-whitespace token is `[` or maybe - // a doc comment (due to `TokenKind::(Line,Block)Comment` ambiguity at lexer level), + // Ok, this is a shebang but if the next non-whitespace token is `[`, // then it may be valid Rust code, so consider it Rust code. - let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).find(|tok| - !matches!(tok, TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }) - ); + let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).find(|tok| { + !matches!( + tok, + TokenKind::Whitespace + | TokenKind::LineComment { doc_style: None } + | TokenKind::BlockComment { doc_style: None, .. } + ) + }); if next_non_whitespace_token != Some(TokenKind::OpenBracket) { // No other choice than to consider this a shebang. return Some(2 + input_tail.lines().next().unwrap_or_default().len()); diff --git a/compiler/rustc_lexer/src/tests.rs b/compiler/rustc_lexer/src/tests.rs index a1ea5ceb1f6..94017b7b286 100644 --- a/compiler/rustc_lexer/src/tests.rs +++ b/compiler/rustc_lexer/src/tests.rs @@ -129,6 +129,34 @@ fn check_lexing(src: &str, expect: Expect) { } #[test] +fn smoke_test() { + check_lexing( + "/* my source file */ fn main() { println!(\"zebra\"); }\n", + expect![[r#" + Token { kind: BlockComment { doc_style: None, terminated: true }, len: 20 } + Token { kind: Whitespace, len: 1 } + Token { kind: Ident, len: 2 } + Token { kind: Whitespace, len: 1 } + Token { kind: Ident, len: 4 } + Token { kind: OpenParen, len: 1 } + Token { kind: CloseParen, len: 1 } + Token { kind: Whitespace, len: 1 } + Token { kind: OpenBrace, len: 1 } + Token { kind: Whitespace, len: 1 } + Token { kind: Ident, len: 7 } + Token { kind: Bang, len: 1 } + Token { kind: OpenParen, len: 1 } + Token { kind: Literal { kind: Str { terminated: true }, suffix_start: 7 }, len: 7 } + Token { kind: CloseParen, len: 1 } + Token { kind: Semi, len: 1 } + Token { kind: Whitespace, len: 1 } + Token { kind: CloseBrace, len: 1 } + Token { kind: Whitespace, len: 1 } + "#]], + ) +} + +#[test] fn comment_flavors() { check_lexing( r" @@ -143,25 +171,117 @@ fn comment_flavors() { /*! inner doc block */ ", expect![[r#" - Token { kind: Whitespace, len: 1 } - Token { kind: LineComment { doc_style: None }, len: 7 } - Token { kind: Whitespace, len: 1 } - Token { kind: LineComment { doc_style: None }, len: 17 } - Token { kind: Whitespace, len: 1 } - Token { kind: LineComment { doc_style: Some(Outer) }, len: 18 } - Token { kind: Whitespace, len: 1 } - Token { kind: LineComment { doc_style: Some(Inner) }, len: 18 } - Token { kind: Whitespace, len: 1 } - Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 } - Token { kind: Whitespace, len: 1 } - Token { kind: BlockComment { doc_style: None, terminated: true }, len: 4 } - Token { kind: Whitespace, len: 1 } - Token { kind: BlockComment { doc_style: None, terminated: true }, len: 18 } - Token { kind: Whitespace, len: 1 } - Token { kind: BlockComment { doc_style: Some(Outer), terminated: true }, len: 22 } - Token { kind: Whitespace, len: 1 } - Token { kind: BlockComment { doc_style: Some(Inner), terminated: true }, len: 22 } - Token { kind: Whitespace, len: 1 } - "#]], + Token { kind: Whitespace, len: 1 } + Token { kind: LineComment { doc_style: None }, len: 7 } + Token { kind: Whitespace, len: 1 } + Token { kind: LineComment { doc_style: None }, len: 17 } + Token { kind: Whitespace, len: 1 } + Token { kind: LineComment { doc_style: Some(Outer) }, len: 18 } + Token { kind: Whitespace, len: 1 } + Token { kind: LineComment { doc_style: Some(Inner) }, len: 18 } + Token { kind: Whitespace, len: 1 } + Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 } + Token { kind: Whitespace, len: 1 } + Token { kind: BlockComment { doc_style: None, terminated: true }, len: 4 } + Token { kind: Whitespace, len: 1 } + Token { kind: BlockComment { doc_style: None, terminated: true }, len: 18 } + Token { kind: Whitespace, len: 1 } + Token { kind: BlockComment { doc_style: Some(Outer), terminated: true }, len: 22 } + Token { kind: Whitespace, len: 1 } + Token { kind: BlockComment { doc_style: Some(Inner), terminated: true }, len: 22 } + Token { kind: Whitespace, len: 1 } + "#]], + ) +} + +#[test] +fn nested_block_comments() { + check_lexing( + "/* /* */ */'a'", + expect![[r#" + Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 } + Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } + "#]], + ) +} + +#[test] +fn characters() { + check_lexing( + "'a' ' ' '\\n'", + expect![[r#" + Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 4 }, len: 4 } + "#]], + ); +} + +#[test] +fn lifetime() { + check_lexing( + "'abc", + expect![[r#" + Token { kind: Lifetime { starts_with_number: false }, len: 4 } + "#]], + ); +} + +#[test] +fn raw_string() { + check_lexing( + "r###\"\"#a\\b\x00c\"\"###", + expect![[r#" + Token { kind: Literal { kind: RawStr { n_hashes: 3, err: None }, suffix_start: 17 }, len: 17 } + "#]], + ) +} + +#[test] +fn literal_suffixes() { + check_lexing( + r####" +'a' +b'a' +"a" +b"a" +1234 +0b101 +0xABC +1.0 +1.0e10 +2us +r###"raw"###suffix +br###"raw"###suffix +"####, + expect![[r#" + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Byte { terminated: true }, suffix_start: 4 }, len: 4 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Str { terminated: true }, suffix_start: 3 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: ByteStr { terminated: true }, suffix_start: 4 }, len: 4 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Int { base: Decimal, empty_int: false }, suffix_start: 4 }, len: 4 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Int { base: Binary, empty_int: false }, suffix_start: 5 }, len: 5 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Int { base: Hexadecimal, empty_int: false }, suffix_start: 5 }, len: 5 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Float { base: Decimal, empty_exponent: false }, suffix_start: 3 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Float { base: Decimal, empty_exponent: false }, suffix_start: 6 }, len: 6 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: Int { base: Decimal, empty_int: false }, suffix_start: 1 }, len: 3 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: RawStr { n_hashes: 3, err: None }, suffix_start: 12 }, len: 18 } + Token { kind: Whitespace, len: 1 } + Token { kind: Literal { kind: RawByteStr { n_hashes: 3, err: None }, suffix_start: 13 }, len: 19 } + Token { kind: Whitespace, len: 1 } + "#]], ) } diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index 9d74ad3b2f5..e6be082da0e 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -7,6 +7,31 @@ use rustc_session::lint::FutureIncompatibleInfo; use rustc_span::symbol::sym; declare_lint! { + /// The `array_into_iter` lint detects calling `into_iter` on arrays. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// [1, 2, 3].into_iter().for_each(|n| { *n; }); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In the future, it is planned to add an `IntoIter` implementation for + /// arrays such that it will iterate over *values* of the array instead of + /// references. Due to how method resolution works, this will change + /// existing code that uses `into_iter` on arrays. The solution to avoid + /// this warning is to use `iter()` instead of `into_iter()`. + /// + /// This is a [future-incompatible] lint to transition this to a hard error + /// in the future. See [issue #66145] for more details and a more thorough + /// description of the lint. + /// + /// [issue #66145]: https://github.com/rust-lang/rust/issues/66145 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub ARRAY_INTO_ITER, Warn, "detects calling `into_iter` on arrays", @@ -53,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { } // Make sure we found an array after peeling the boxes. - if !matches!(recv_ty.kind, ty::Array(..)) { + if !matches!(recv_ty.kind(), ty::Array(..)) { return; } @@ -66,9 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { } // Emit lint diagnostic. - let target = match cx.typeck_results().expr_ty_adjusted(receiver_arg).kind { - ty::Ref(_, ty::TyS { kind: ty::Array(..), .. }, _) => "[T; N]", - ty::Ref(_, ty::TyS { kind: ty::Slice(..), .. }, _) => "[T]", + let target = match *cx.typeck_results().expr_ty_adjusted(receiver_arg).kind() { + ty::Ref(_, inner_ty, _) if inner_ty.is_array() => "[T; N]", + ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => "[T]", // We know the original first argument type is an array type, // we know that the first adjustment was an autoref coercion diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index ea624b9ed30..abd899e8db4 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -21,7 +21,8 @@ //! `late_lint_methods!` invocation in `lib.rs`. use crate::{ - types::CItemKind, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext, + types::{transparent_newtype_field, CItemKind}, + EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext, }; use rustc_ast::attr::{self, HasAttrs}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; @@ -40,6 +41,7 @@ use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind}; use rustc_hir::{HirId, HirIdSet, Node}; use rustc_index::vec::Idx; use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::{GenericArgKind, Subst}; use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt}; use rustc_session::lint::FutureIncompatibleInfo; @@ -60,6 +62,23 @@ use tracing::{debug, trace}; pub use rustc_session::lint::builtin::*; declare_lint! { + /// The `while_true` lint detects `while true { }`. + /// + /// ### Example + /// + /// ```rust,no_run + /// while true { + /// + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `while true` should be replaced with `loop`. A `loop` expression is + /// the preferred way to write an infinite loop because it more directly + /// expresses the intent of the loop. WHILE_TRUE, Warn, "suggest using `loop { }` instead of `while true { }`" @@ -101,6 +120,24 @@ impl EarlyLintPass for WhileTrue { } declare_lint! { + /// The `box_pointers` lints use of the Box type. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(box_pointers)] + /// struct Foo { + /// x: Box<isize>, + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is mostly historical, and not particularly useful. `Box<T>` + /// used to be built into the language, and the only way to do heap + /// allocation. Today's Rust can call into other allocators, etc. BOX_POINTERS, Allow, "use of owned (Box type) heap memory" @@ -155,6 +192,36 @@ impl<'tcx> LateLintPass<'tcx> for BoxPointers { } declare_lint! { + /// The `non_shorthand_field_patterns` lint detects using `Struct { x: x }` + /// instead of `Struct { x }` in a pattern. + /// + /// ### Example + /// + /// ```rust + /// struct Point { + /// x: i32, + /// y: i32, + /// } + /// + /// + /// fn main() { + /// let p = Point { + /// x: 5, + /// y: 5, + /// }; + /// + /// match p { + /// Point { x: x, y: y } => (), + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The preferred style is to avoid the repetition of specifying both the + /// field name and the binding name if both identifiers are the same. NON_SHORTHAND_FIELD_PATTERNS, Warn, "using `Struct { x: x }` instead of `Struct { x }` in a pattern" @@ -215,6 +282,25 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { } declare_lint! { + /// The `unsafe_code` lint catches usage of `unsafe` code. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unsafe_code)] + /// fn main() { + /// unsafe { + /// + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is intended to restrict the usage of `unsafe`, which can be + /// difficult to use correctly. UNSAFE_CODE, Allow, "usage of `unsafe` code" @@ -302,6 +388,25 @@ impl EarlyLintPass for UnsafeCode { } declare_lint! { + /// The `missing_docs` lint detects missing documentation for public items. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(missing_docs)] + /// pub fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is intended to ensure that a library is well-documented. + /// Items without documentation can be difficult for users to understand + /// how to use properly. + /// + /// This lint is "allow" by default because it can be noisy, and not all + /// projects may want to enforce everything to be documented. pub MISSING_DOCS, Allow, "detects missing documentation for public members", @@ -508,6 +613,19 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { ); } + fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'_>) { + let def_id = cx.tcx.hir().local_def_id(foreign_item.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + self.check_missing_docs_attrs( + cx, + Some(foreign_item.hir_id), + &foreign_item.attrs, + foreign_item.span, + article, + desc, + ); + } + fn check_struct_field(&mut self, cx: &LateContext<'_>, sf: &hir::StructField<'_>) { if !sf.is_positional() { self.check_missing_docs_attrs( @@ -527,6 +645,34 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } declare_lint! { + /// The `missing_copy_implementations` lint detects potentially-forgotten + /// implementations of [`Copy`]. + /// + /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(missing_copy_implementations)] + /// pub struct Foo { + /// pub field: i32 + /// } + /// # fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Historically (before 1.0), types were automatically marked as `Copy` + /// if possible. This was changed so that it required an explicit opt-in + /// by implementing the `Copy` trait. As part of this change, a lint was + /// added to alert if a copyable type was not marked `Copy`. + /// + /// This lint is "allow" by default because this code isn't bad; it is + /// common to write newtypes like this specifically so that a `Copy` type + /// is no longer `Copy`. `Copy` types can result in unintended copies of + /// large data which can impact performance. pub MISSING_COPY_IMPLEMENTATIONS, Allow, "detects potentially-forgotten implementations of `Copy`" @@ -583,6 +729,32 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { } declare_lint! { + /// The `missing_debug_implementations` lint detects missing + /// implementations of [`fmt::Debug`]. + /// + /// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(missing_debug_implementations)] + /// pub struct Foo; + /// # fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Having a `Debug` implementation on all types can assist with + /// debugging, as it provides a convenient way to format and display a + /// value. Using the `#[derive(Debug)]` attribute will automatically + /// generate a typical implementation, or a custom implementation can be + /// added by manually implementing the `Debug` trait. + /// + /// This lint is "allow" by default because adding `Debug` to all types can + /// have a negative impact on compile time and code size. It also requires + /// boilerplate to be added to every type, which can be an impediment. MISSING_DEBUG_IMPLEMENTATIONS, Allow, "detects missing implementations of Debug" @@ -639,6 +811,45 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { } declare_lint! { + /// The `anonymous_parameters` lint detects anonymous parameters in trait + /// definitions. + /// + /// ### Example + /// + /// ```rust,edition2015,compile_fail + /// #![deny(anonymous_parameters)] + /// // edition 2015 + /// pub trait Foo { + /// fn foo(usize); + /// } + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This syntax is mostly a historical accident, and can be worked around + /// quite easily by adding an `_` pattern or a descriptive identifier: + /// + /// ```rust + /// trait Foo { + /// fn foo(_: usize); + /// } + /// ``` + /// + /// This syntax is now a hard error in the 2018 edition. In the 2015 + /// edition, this lint is "allow" by default, because the old code is + /// still valid, and warning for all old code can be noisy. This lint + /// enables the [`cargo fix`] tool with the `--edition` flag to + /// automatically transition old code from the 2015 edition to 2018. The + /// tool will switch this lint to "warn" and will automatically apply the + /// suggested fix from the compiler (which is to add `_` to each + /// parameter). This provides a completely automated way to update old + /// code for a new edition. See [issue #41686] for more details. + /// + /// [issue #41686]: https://github.com/rust-lang/rust/issues/41686 + /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html pub ANONYMOUS_PARAMETERS, Allow, "detects anonymous parameters", @@ -764,7 +975,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & continue; } - let span = sugared_span.take().unwrap_or_else(|| attr.span); + let span = sugared_span.take().unwrap_or(attr.span); if attr.is_doc_comment() || cx.sess().check_name(attr, sym::doc) { cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| { @@ -805,12 +1016,54 @@ impl EarlyLintPass for UnusedDocComment { } declare_lint! { + /// The `no_mangle_const_items` lint detects any `const` items with the + /// [`no_mangle` attribute]. + /// + /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[no_mangle] + /// const FOO: i32 = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Constants do not have their symbols exported, and therefore, this + /// probably means you meant to use a [`static`], not a [`const`]. + /// + /// [`static`]: https://doc.rust-lang.org/reference/items/static-items.html + /// [`const`]: https://doc.rust-lang.org/reference/items/constant-items.html NO_MANGLE_CONST_ITEMS, Deny, "const items will not have their symbols exported" } declare_lint! { + /// The `no_mangle_generic_items` lint detects generic items that must be + /// mangled. + /// + /// ### Example + /// + /// ```rust + /// #[no_mangle] + /// fn foo<T>(t: T) { + /// + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// An function with generics must have its symbol mangled to accommodate + /// the generic parameter. The [`no_mangle` attribute] has no effect in + /// this situation, and should be removed. + /// + /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute NO_MANGLE_GENERIC_ITEMS, Warn, "generic items must be mangled" @@ -881,6 +1134,27 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { } declare_lint! { + /// The `mutable_transmutes` lint catches transmuting from `&T` to `&mut + /// T` because it is [undefined behavior]. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// unsafe { + /// let y = std::mem::transmute::<&i32, &mut i32>(&5); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Certain assumptions are made about aliasing of data, and this transmute + /// violates those assumptions. Consider using [`UnsafeCell`] instead. + /// + /// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html MUTABLE_TRANSMUTES, Deny, "mutating transmuted &mut T from &T may cause undefined behavior" @@ -892,7 +1166,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { use rustc_target::spec::abi::Abi::RustIntrinsic; if let Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) = - get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (&ty1.kind, &ty2.kind)) + get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind())) { if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \ @@ -930,6 +1204,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { } declare_lint! { + /// The `unstable_features` is deprecated and should no longer be used. UNSTABLE_FEATURES, Allow, "enabling unstable features (deprecated. do not use)" @@ -955,6 +1230,32 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { } declare_lint! { + /// The `unreachable_pub` lint triggers for `pub` items not reachable from + /// the crate root. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unreachable_pub)] + /// mod foo { + /// pub mod bar { + /// + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A bare `pub` visibility may be misleading if the item is not actually + /// publicly exported from the crate. The `pub(crate)` visibility is + /// recommended to be used instead, which more clearly expresses the intent + /// that the item is only visible within its own crate. + /// + /// This lint is "allow" by default because it will trigger for a large + /// amount existing Rust code, and has some false-positives. Eventually it + /// is desired for this to become warn-by-default. pub UNREACHABLE_PUB, Allow, "`pub` items not reachable from crate root" @@ -1034,6 +1335,21 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { } declare_lint! { + /// The `type_alias_bounds` lint detects bounds in type aliases. + /// + /// ### Example + /// + /// ```rust + /// type SendVec<T: Send> = Vec<T>; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The trait bounds in a type alias are currently ignored, and should not + /// be included to avoid confusion. This was previously allowed + /// unintentionally; this may become a hard error in the future. TYPE_ALIAS_BOUNDS, Warn, "bounds in type aliases are not enforced" @@ -1171,21 +1487,19 @@ declare_lint_pass!( UnusedBrokenConst => [] ); -fn check_const(cx: &LateContext<'_>, body_id: hir::BodyId) { - let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); - // trigger the query once for all constants since that will already report the errors - // FIXME: Use ensure here - let _ = cx.tcx.const_eval_poly(def_id); -} - impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { match it.kind { hir::ItemKind::Const(_, body_id) => { - check_const(cx, body_id); + let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); + // trigger the query once for all constants since that will already report the errors + // FIXME: Use ensure here + let _ = cx.tcx.const_eval_poly(def_id); } hir::ItemKind::Static(_, _, body_id) => { - check_const(cx, body_id); + let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); + // FIXME: Use ensure here + let _ = cx.tcx.eval_static_initializer(def_id); } _ => {} } @@ -1193,6 +1507,35 @@ impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst { } declare_lint! { + /// The `trivial_bounds` lint detects trait bounds that don't depend on + /// any type parameters. + /// + /// ### Example + /// + /// ```rust + /// #![feature(trivial_bounds)] + /// pub struct A where i32: Copy; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Usually you would not write a trait bound that you know is always + /// true, or never true. However, when using macros, the macro may not + /// know whether or not the constraint would hold or not at the time when + /// generating the code. Currently, the compiler does not alert you if the + /// constraint is always true, and generates an error if it is never true. + /// The `trivial_bounds` feature changes this to be a warning in both + /// cases, giving macros more freedom and flexibility to generate code, + /// while still providing a signal when writing non-macro code that + /// something is amiss. + /// + /// See [RFC 2056] for more details. This feature is currently only + /// available on the nightly channel, see [tracking issue #48214]. + /// + /// [RFC 2056]: https://github.com/rust-lang/rfcs/blob/master/text/2056-allow-trivial-where-clause-constraints.md + /// [tracking issue #48214]: https://github.com/rust-lang/rust/issues/48214 TRIVIAL_BOUNDS, Warn, "these bounds don't depend on an type parameters" @@ -1227,7 +1570,8 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { ClosureKind(..) | Subtype(..) | ConstEvaluatable(..) | - ConstEquate(..) => continue, + ConstEquate(..) | + TypeWellFormedFromEnv(..) => continue, }; if predicate.is_global() { cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| { @@ -1268,6 +1612,29 @@ declare_lint_pass!( ); declare_lint! { + /// The `ellipsis_inclusive_range_patterns` lint detects the [`...` range + /// pattern], which is deprecated. + /// + /// [`...` range pattern]: https://doc.rust-lang.org/reference/patterns.html#range-patterns + /// + /// ### Example + /// + /// ```rust + /// let x = 123; + /// match x { + /// 0...100 => {} + /// _ => {} + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The `...` range pattern syntax was changed to `..=` to avoid potential + /// confusion with the [`..` range expression]. Use the new form instead. + /// + /// [`..` range expression]: https://doc.rust-lang.org/reference/expressions/range-expr.html pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, Warn, "`...` range patterns are deprecated" @@ -1354,6 +1721,38 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } declare_lint! { + /// The `unnameable_test_items` lint detects [`#[test]`][test] functions + /// that are not able to be run by the test harness because they are in a + /// position where they are not nameable. + /// + /// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute + /// + /// ### Example + /// + /// ```rust,test + /// fn main() { + /// #[test] + /// fn foo() { + /// // This test will not fail because it does not run. + /// assert_eq!(1, 2); + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In order for the test harness to run a test, the test function must be + /// located in a position where it can be accessed from the crate root. + /// This generally means it must be defined in a module, and not anywhere + /// else such as inside another function. The compiler previously allowed + /// this without an error, so a lint was added as an alert that a test is + /// not being used. Whether or not this should be allowed has not yet been + /// decided, see [RFC 2471] and [issue #36629]. + /// + /// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443 + /// [issue #36629]: https://github.com/rust-lang/rust/issues/36629 UNNAMEABLE_TEST_ITEMS, Warn, "detects an item that cannot be named being marked as `#[test_case]`", @@ -1399,6 +1798,41 @@ impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems { } declare_lint! { + /// The `keyword_idents` lint detects edition keywords being used as an + /// identifier. + /// + /// ### Example + /// + /// ```rust,edition2015,compile_fail + /// #![deny(keyword_idents)] + /// // edition 2015 + /// fn dyn() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust [editions] allow the language to evolve without breaking + /// backwards compatibility. This lint catches code that uses new keywords + /// that are added to the language that are used as identifiers (such as a + /// variable name, function name, etc.). If you switch the compiler to a + /// new edition without updating the code, then it will fail to compile if + /// you are using a new keyword as an identifier. + /// + /// You can manually change the identifiers to a non-keyword, or use a + /// [raw identifier], for example `r#dyn`, to transition to a new edition. + /// + /// This lint solves the problem automatically. It is "allow" by default + /// because the code is perfectly valid in older editions. The [`cargo + /// fix`] tool with the `--edition` flag will switch this lint to "warn" + /// and automatically apply the suggested fix from the compiler (which is + /// to use a raw identifier). This provides a completely automated way to + /// update old code for a new edition. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html + /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html pub KEYWORD_IDENTS, Allow, "detects edition keywords being used as an identifier", @@ -1563,9 +1997,9 @@ impl ExplicitOutlivesRequirements { .filter_map(|(i, bound)| { if let hir::GenericBound::Outlives(lifetime) = bound { let is_inferred = match tcx.named_region(lifetime.hir_id) { - Some(Region::Static) if infer_static => inferred_outlives - .iter() - .any(|r| if let ty::ReStatic = r { true } else { false }), + Some(Region::Static) if infer_static => { + inferred_outlives.iter().any(|r| matches!(r, ty::ReStatic)) + } Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| { if let ty::ReEarlyBound(ebr) = r { ebr.index == index } else { false } }), @@ -1657,9 +2091,10 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { let mut lint_spans = Vec::new(); for param in hir_generics.params { - let has_lifetime_bounds = param.bounds.iter().any(|bound| { - if let hir::GenericBound::Outlives(_) = bound { true } else { false } - }); + let has_lifetime_bounds = param + .bounds + .iter() + .any(|bound| matches!(bound, hir::GenericBound::Outlives(_))); if !has_lifetime_bounds { continue; } @@ -1800,6 +2235,26 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { } declare_lint! { + /// The `incomplete_features` lint detects unstable features enabled with + /// the [`feature` attribute] that may function improperly in some or all + /// cases. + /// + /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/ + /// + /// ### Example + /// + /// ```rust + /// #![feature(generic_associated_types)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Although it is encouraged for people to experiment with unstable + /// features, some of them are known to be incomplete or faulty. This lint + /// is a signal that the feature has not yet been finished, and you may + /// experience problems with it. pub INCOMPLETE_FEATURES, Warn, "incomplete features that may function improperly in some or all cases" @@ -1840,6 +2295,36 @@ impl EarlyLintPass for IncompleteFeatures { } declare_lint! { + /// The `invalid_value` lint detects creating a value that is not valid, + /// such as a NULL reference. + /// + /// ### Example + /// + /// ```rust,no_run + /// # #![allow(unused)] + /// unsafe { + /// let x: &'static i32 = std::mem::zeroed(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In some situations the compiler can detect that the code is creating + /// an invalid value, which should be avoided. + /// + /// In particular, this lint will check for improper use of + /// [`mem::zeroed`], [`mem::uninitialized`], [`mem::transmute`], and + /// [`MaybeUninit::assume_init`] that can cause [undefined behavior]. The + /// lint should provide extra information to indicate what the problem is + /// and a possible solution. + /// + /// [`mem::zeroed`]: https://doc.rust-lang.org/std/mem/fn.zeroed.html + /// [`mem::uninitialized`]: https://doc.rust-lang.org/std/mem/fn.uninitialized.html + /// [`mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html + /// [`MaybeUninit::assume_init`]: https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#method.assume_init + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html pub INVALID_VALUE, Warn, "an invalid value is being created (such as a NULL reference)" @@ -1878,13 +2363,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { /// Determine if this expression is a "dangerous initialization". fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<InitKind> { - // `transmute` is inside an anonymous module (the `extern` block?); - // `Invalid` represents the empty string and matches that. - // FIXME(#66075): use diagnostic items. Somehow, that does not seem to work - // on intrinsics right now. - const TRANSMUTE_PATH: &[Symbol] = - &[sym::core, sym::intrinsics, kw::Invalid, sym::transmute]; - if let hir::ExprKind::Call(ref path_expr, ref args) = expr.kind { // Find calls to `mem::{uninitialized,zeroed}` methods. if let hir::ExprKind::Path(ref qpath) = path_expr.kind { @@ -1894,7 +2372,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { return Some(InitKind::Zeroed); } else if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, def_id) { return Some(InitKind::Uninit); - } else if cx.match_def_path(def_id, TRANSMUTE_PATH) { + } else if cx.tcx.is_diagnostic_item(sym::transmute, def_id) { if is_zero(&args[0]) { return Some(InitKind::Zeroed); } @@ -1939,13 +2417,13 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { init: InitKind, ) -> Option<InitError> { use rustc_middle::ty::TyKind::*; - match ty.kind { + match ty.kind() { // Primitive types that don't like 0 as a value. Ref(..) => Some(("references must be non-null".to_string(), None)), Adt(..) if ty.is_box() => Some(("`Box` must be non-null".to_string(), None)), FnPtr(..) => Some(("function pointers must be non-null".to_string(), None)), Never => Some(("the `!` type has no valid value".to_string(), None)), - RawPtr(tm) if matches!(tm.ty.kind, Dynamic(..)) => + RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) => // raw ptr to dyn Trait { Some(("the vtable of a wide raw pointer must be non-null".to_string(), None)) @@ -2040,7 +2518,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { // using zeroed or uninitialized memory. // We are extremely conservative with what we warn about. let conjured_ty = cx.typeck_results().expr_ty(expr); - if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) { + if let Some((msg, span)) = + with_no_trimmed_paths(|| ty_find_init_error(cx.tcx, conjured_ty, init)) + { cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| { let mut err = lint.build(&format!( "the type `{}` does not permit {}", @@ -2069,6 +2549,40 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { } declare_lint! { + /// The `clashing_extern_declarations` lint detects when an `extern fn` + /// has been declared with the same name but different types. + /// + /// ### Example + /// + /// ```rust + /// mod m { + /// extern "C" { + /// fn foo(); + /// } + /// } + /// + /// extern "C" { + /// fn foo(_: u32); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Because two symbols of the same name cannot be resolved to two + /// different functions at link time, and one function cannot possibly + /// have two types, a clashing extern declaration is almost certainly a + /// mistake. Check to make sure that the `extern` definitions are correct + /// and equivalent, and possibly consider unifying them in one location. + /// + /// This lint does not run between crates because a project may have + /// dependencies which both rely on the same extern function, but declare + /// it in a different (but valid) way. For example, they may both declare + /// an opaque type for one or more of the arguments (which would end up + /// distinct types), or use types that are valid conversions in the + /// language the `extern fn` is defined in. In these cases, the compiler + /// can't say that the clashing declaration is incorrect. pub CLASHING_EXTERN_DECLARATIONS, Warn, "detects when an extern fn has been declared with the same name but different types" @@ -2170,7 +2684,7 @@ impl ClashingExternDeclarations { let non_transparent_ty = |ty: Ty<'tcx>| -> Ty<'tcx> { let mut ty = ty; loop { - if let ty::Adt(def, substs) = ty.kind { + if let ty::Adt(def, substs) = *ty.kind() { let is_transparent = def.subst(tcx, substs).repr.transparent(); let is_non_null = crate::types::nonnull_optimization_guaranteed(tcx, &def); debug!( @@ -2180,8 +2694,7 @@ impl ClashingExternDeclarations { if is_transparent && !is_non_null { debug_assert!(def.variants.len() == 1); let v = &def.variants[VariantIdx::new(0)]; - ty = v - .transparent_newtype_field(tcx) + ty = transparent_newtype_field(tcx, v) .expect( "single-variant transparent structure with zero-sized field", ) @@ -2209,8 +2722,8 @@ impl ClashingExternDeclarations { } else { // Do a full, depth-first comparison between the two. use rustc_middle::ty::TyKind::*; - let a_kind = &a.kind; - let b_kind = &b.kind; + let a_kind = a.kind(); + let b_kind = b.kind(); let compare_layouts = |a, b| -> Result<bool, LayoutError<'tcx>> { debug!("compare_layouts({:?}, {:?})", a, b); @@ -2332,7 +2845,7 @@ impl ClashingExternDeclarations { if is_primitive_or_pointer(other_kind) => { let (primitive, adt) = - if is_primitive_or_pointer(&a.kind) { (a, b) } else { (b, a) }; + if is_primitive_or_pointer(a.kind()) { (a, b) } else { (b, a) }; if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) { ty == primitive } else { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index a6784ffffcd..0265fc323b3 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -31,6 +31,7 @@ use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability; use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; use rustc_session::lint::{add_elided_lifetime_in_path_suggestion, BuiltinLintDiagnostics}; use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; @@ -719,6 +720,10 @@ impl<'tcx> LateContext<'tcx> { /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`; /// inherent `impl` blocks are matched with the name of the type. /// + /// Instead of using this method, it is often preferable to instead use + /// `rustc_diagnostic_item` or a `lang_item`. This is less prone to errors + /// as paths get invalidated if the target definition moves. + /// /// # Examples /// /// ```rust,ignore (no context or def id available) @@ -789,16 +794,18 @@ impl<'tcx> LateContext<'tcx> { trait_ref: Option<ty::TraitRef<'tcx>>, ) -> Result<Self::Path, Self::Error> { if trait_ref.is_none() { - if let ty::Adt(def, substs) = self_ty.kind { + if let ty::Adt(def, substs) = self_ty.kind() { return self.print_def_path(def.did, substs); } } // This shouldn't ever be needed, but just in case: - Ok(vec![match trait_ref { - Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)), - None => Symbol::intern(&format!("<{}>", self_ty)), - }]) + with_no_trimmed_paths(|| { + Ok(vec![match trait_ref { + Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)), + None => Symbol::intern(&format!("<{}>", self_ty)), + }]) + }) } fn path_append_impl( @@ -812,12 +819,16 @@ impl<'tcx> LateContext<'tcx> { // This shouldn't ever be needed, but just in case: path.push(match trait_ref { - Some(trait_ref) => Symbol::intern(&format!( - "<impl {} for {}>", - trait_ref.print_only_trait_path(), - self_ty - )), - None => Symbol::intern(&format!("<impl {}>", self_ty)), + Some(trait_ref) => with_no_trimmed_paths(|| { + Symbol::intern(&format!( + "<impl {} for {}>", + trait_ref.print_only_trait_path(), + self_ty + )) + }), + None => { + with_no_trimmed_paths(|| Symbol::intern(&format!("<impl {}>", self_ty))) + } }); Ok(path) diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 100e555f299..c2d98b8e4ad 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -5,7 +5,9 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext} use rustc_ast::{Item, ItemKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind}; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -177,11 +179,31 @@ fn lint_ty_kind_usage(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> bool { 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 { - let did = path.res.opt_def_id()?; - if cx.tcx.is_diagnostic_item(sym::Ty, did) { - return Some(format!("Ty{}", gen_args(path.segments.last().unwrap()))); - } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) { - return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap()))); + match path.res { + Res::Def(_, did) => { + if cx.tcx.is_diagnostic_item(sym::Ty, did) { + return Some(format!("Ty{}", gen_args(path.segments.last().unwrap()))); + } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) { + return Some(format!("TyCtxt{}", 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 cx.tcx.is_diagnostic_item(sym::Ty, 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!("Ty<{}>", substs[0])); + } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, adt.did) { + return Some(format!("TyCtxt<{}>", substs[0])); + } + } + } + _ => (), } } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 0a14b16e274..7f7472d9283 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -25,8 +25,9 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![cfg_attr(test, feature(test))] +#![feature(array_windows)] #![feature(bool_to_option)] #![feature(box_syntax)] #![feature(crate_visibility_modifier)] diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 2f0b2a8d680..a1c7e47e749 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -4,6 +4,32 @@ use rustc_data_structures::fx::FxHashMap; use rustc_span::symbol::Symbol; declare_lint! { + /// The `non_ascii_idents` lint detects non-ASCII identifiers. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # #![allow(unused)] + /// #![feature(non_ascii_idents)] + /// #![deny(non_ascii_idents)] + /// fn main() { + /// let föö = 1; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Currently on stable Rust, identifiers must contain ASCII characters. + /// The [`non_ascii_idents`] nightly-only feature allows identifiers to + /// contain non-ASCII characters. This lint allows projects that wish to + /// retain the limit of only using ASCII characters to switch this lint to + /// "forbid" (for example to ease collaboration or for security reasons). + /// See [RFC 2457] for more details. + /// + /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html + /// [RFC 2457]: https://github.com/rust-lang/rfcs/blob/master/text/2457-non-ascii-idents.md pub NON_ASCII_IDENTS, Allow, "detects non-ASCII identifiers", @@ -11,6 +37,37 @@ declare_lint! { } declare_lint! { + /// The `uncommon_codepoints` lint detects uncommon Unicode codepoints in + /// identifiers. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// #![feature(non_ascii_idents)] + /// const µ: f64 = 0.000001; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// With the [`non_ascii_idents`] nightly-only feature enabled, + /// identifiers are allowed to use non-ASCII characters. This lint warns + /// about using characters which are not commonly used, and may cause + /// visual confusion. + /// + /// This lint is triggered by identifiers that contain a codepoint that is + /// not part of the set of "Allowed" codepoints as described by [Unicode® + /// Technical Standard #39 Unicode Security Mechanisms Section 3.1 General + /// Security Profile for Identifiers][TR39Allowed]. + /// + /// Note that the set of uncommon codepoints may change over time. Beware + /// that if you "forbid" this lint that existing code may fail in the + /// future. + /// + /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html + /// [TR39Allowed]: https://www.unicode.org/reports/tr39/#General_Security_Profile pub UNCOMMON_CODEPOINTS, Warn, "detects uncommon Unicode codepoints in identifiers", @@ -18,6 +75,43 @@ declare_lint! { } declare_lint! { + /// The `confusable_idents` lint detects visually confusable pairs between + /// identifiers. + /// + /// ### Example + /// + /// ```rust + /// #![feature(non_ascii_idents)] + /// + /// // Latin Capital Letter E With Caron + /// pub const Ě: i32 = 1; + /// // Latin Capital Letter E With Breve + /// pub const Ĕ: i32 = 2; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// With the [`non_ascii_idents`] nightly-only feature enabled, + /// identifiers are allowed to use non-ASCII characters. This lint warns + /// when different identifiers may appear visually similar, which can + /// cause confusion. + /// + /// The confusable detection algorithm is based on [Unicode® Technical + /// Standard #39 Unicode Security Mechanisms Section 4 Confusable + /// Detection][TR39Confusable]. For every distinct identifier X execute + /// the function `skeleton(X)`. If there exist two distinct identifiers X + /// and Y in the same crate where `skeleton(X) = skeleton(Y)` report it. + /// The compiler uses the same mechanism to check if an identifier is too + /// similar to a keyword. + /// + /// Note that the set of confusable characters may change over time. + /// Beware that if you "forbid" this lint that existing code may fail in + /// the future. + /// + /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html + /// [TR39Confusable]: https://www.unicode.org/reports/tr39/#Confusable_Detection pub CONFUSABLE_IDENTS, Warn, "detects visually confusable pairs between identifiers", @@ -25,6 +119,41 @@ declare_lint! { } declare_lint! { + /// The `mixed_script_confusables` lint detects visually confusable + /// characters in identifiers between different [scripts]. + /// + /// [scripts]: https://en.wikipedia.org/wiki/Script_(Unicode) + /// + /// ### Example + /// + /// ```rust + /// #![feature(non_ascii_idents)] + /// + /// // The Japanese katakana character エ can be confused with the Han character 工. + /// const エ: &'static str = "アイウ"; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// With the [`non_ascii_idents`] nightly-only feature enabled, + /// identifiers are allowed to use non-ASCII characters. This lint warns + /// when characters between different scripts may appear visually similar, + /// which can cause confusion. + /// + /// If the crate contains other identifiers in the same script that have + /// non-confusable characters, then this lint will *not* be issued. For + /// example, if the example given above has another identifier with + /// katakana characters (such as `let カタカナ = 123;`), then this indicates + /// that you are intentionally using katakana, and it will not warn about + /// it. + /// + /// Note that the set of confusable characters may change over time. + /// Beware that if you "forbid" this lint that existing code may fail in + /// the future. + /// + /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html pub MIXED_SCRIPT_CONFUSABLES, Warn, "detects Unicode scripts whose mixed script confusables codepoints are solely used", @@ -212,7 +341,8 @@ impl EarlyLintPass for NonAsciiIdents { } } - ch_list.sort(); + // We sort primitive chars here and can use unstable sort + ch_list.sort_unstable(); ch_list.dedup(); lint_reports.insert((sp, ch_list), augment_script_set); } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index f23e8c5e208..b3125f55d4d 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -31,6 +31,24 @@ pub fn method_context(cx: &LateContext<'_>, id: hir::HirId) -> MethodLateContext } declare_lint! { + /// The `non_camel_case_types` lint detects types, variants, traits and + /// type parameters that don't have camel case names. + /// + /// ### Example + /// + /// ```rust + /// struct my_struct; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The preferred style for these identifiers is to use "camel case", such + /// as `MyStruct`, where the first letter should not be lowercase, and + /// should not use underscores between letters. Underscores are allowed at + /// the beginning and end of the identifier, as well as between + /// non-letters (such as `X86_64`). pub NON_CAMEL_CASE_TYPES, Warn, "types, variants, traits and type parameters should have camel case names" @@ -52,9 +70,9 @@ fn is_camel_case(name: &str) -> bool { // ones (some scripts don't have a concept of upper/lowercase) !name.chars().next().unwrap().is_lowercase() && !name.contains("__") - && !name.chars().collect::<Vec<_>>().windows(2).any(|pair| { + && !name.chars().collect::<Vec<_>>().array_windows().any(|&[fst, snd]| { // contains a capitalisable character followed by, or preceded by, an underscore - char_has_case(pair[0]) && pair[1] == '_' || char_has_case(pair[1]) && pair[0] == '_' + char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_' }) } @@ -161,6 +179,22 @@ impl EarlyLintPass for NonCamelCaseTypes { } declare_lint! { + /// The `non_snake_case` lint detects variables, methods, functions, + /// lifetime parameters and modules that don't have snake case names. + /// + /// ### Example + /// + /// ```rust + /// let MY_VALUE = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The preferred style for these identifiers is to use "snake case", + /// where all the characters are in lowercase, with words separated with a + /// single underscore, such as `my_value`. pub NON_SNAKE_CASE, Warn, "variables, methods, functions, lifetime parameters and modules should have snake case names" @@ -379,6 +413,21 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } declare_lint! { + /// The `non_upper_case_globals` lint detects static items that don't have + /// uppercase identifiers. + /// + /// ### Example + /// + /// ```rust + /// static max_points: i32 = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The preferred style is for static item names to use all uppercase + /// letters such as `MAX_POINTS`. pub NON_UPPER_CASE_GLOBALS, Warn, "static constants should have uppercase identifiers" diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index d4aa4968f25..a31deb87ff0 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -4,6 +4,21 @@ use rustc_errors::Applicability; use rustc_span::Span; declare_lint! { + /// The `redundant_semicolons` lint detects unnecessary trailing + /// semicolons. + /// + /// ### Example + /// + /// ```rust + /// let _ = 123;; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Extra semicolons are not needed, and may be removed to avoid confusion + /// and visual clutter. pub REDUNDANT_SEMICOLONS, Warn, "detects unnecessary trailing semicolons" diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 35c462c24c8..6aa28d04ae1 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -23,18 +23,82 @@ use std::cmp; use tracing::debug; declare_lint! { + /// The `unused_comparisons` lint detects comparisons made useless by + /// limits of the types involved. + /// + /// ### Example + /// + /// ```rust + /// fn foo(x: u8) { + /// x >= 0; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A useless comparison may indicate a mistake, and should be fixed or + /// removed. UNUSED_COMPARISONS, Warn, "comparisons made useless by limits of the types involved" } declare_lint! { + /// The `overflowing_literals` lint detects literal out of range for its + /// type. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// let x: u8 = 1000; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to use a literal that overflows the type where + /// it is used. Either use a literal that is within range, or change the + /// type to be within the range of the literal. OVERFLOWING_LITERALS, Deny, "literal out of range for its type" } declare_lint! { + /// The `variant_size_differences` lint detects enums with widely varying + /// variant sizes. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(variant_size_differences)] + /// enum En { + /// V0(u8), + /// VBig([u8; 1024]), + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It can be a mistake to add a variant to an enum that is much larger + /// than the other variants, bloating the overall size required for all + /// variants. This can impact performance and memory usage. This is + /// triggered if one variant is more than 3 times larger than the + /// second-largest variant. + /// + /// Consider placing the large variant's contents on the heap (for example + /// via [`Box`]) to keep the overall size of the enum itself down. + /// + /// This lint is "allow" by default because it can be noisy, and may not be + /// an actual problem. Decisions about this should be guided with + /// profiling and benchmarking. + /// + /// [`Box`]: https://doc.rust-lang.org/std/boxed/index.html VARIANT_SIZE_DIFFERENCES, Allow, "detects enums with widely varying variant sizes" @@ -217,7 +281,7 @@ fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static } } } - match t.kind { + match t.kind() { ty::Int(i) => find_fit!(i, val, negative, I8 => [U8] => [I16, I32, I64, I128], I16 => [U16] => [I32, I64, I128], @@ -303,7 +367,7 @@ fn lint_uint_literal<'tcx>( if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) { match par_e.kind { hir::ExprKind::Cast(..) => { - if let ty::Char = cx.typeck_results().expr_ty(par_e).kind { + if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { cx.struct_span_lint(OVERFLOWING_LITERALS, par_e.span, |lint| { lint.build("only `u8` can be cast into `char`") .span_suggestion( @@ -354,7 +418,7 @@ fn lint_literal<'tcx>( e: &'tcx hir::Expr<'tcx>, lit: &hir::Lit, ) { - match cx.typeck_results().node_type(e.hir_id).kind { + match *cx.typeck_results().node_type(e.hir_id).kind() { ty::Int(t) => { match lit.node { ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { @@ -450,7 +514,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { // Normalize the binop so that the literal is always on the RHS in // the comparison let norm_binop = if swap { rev_binop(binop) } else { binop }; - match cx.typeck_results().node_type(expr.hir_id).kind { + match *cx.typeck_results().node_type(expr.hir_id).kind() { ty::Int(int_ty) => { let (min, max) = int_ty_range(int_ty); let lit_val: i128 = match lit.kind { @@ -495,6 +559,27 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } declare_lint! { + /// The `improper_ctypes` lint detects incorrect use of types in foreign + /// modules. + /// + /// ### Example + /// + /// ```rust + /// extern "C" { + /// static STATIC: String; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler has several checks to verify that types used in `extern` + /// blocks are safe and follow certain rules to ensure proper + /// compatibility with the foreign interfaces. This lint is issued when it + /// detects a probable mistake in a definition. The lint usually should + /// provide a description of the issue, along with possibly a hint on how + /// to resolve it. IMPROPER_CTYPES, Warn, "proper use of libc types in foreign modules" @@ -503,6 +588,27 @@ declare_lint! { declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]); declare_lint! { + /// The `improper_ctypes_definitions` lint detects incorrect use of + /// [`extern` function] definitions. + /// + /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// pub extern "C" fn str_type(p: &str) { } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// There are many parameter and return types that may be specified in an + /// `extern` function that are not compatible with the given ABI. This + /// lint is an alert that these types should not be used. The lint usually + /// should provide a description of the issue, along with possibly a hint + /// on how to resolve it. IMPROPER_CTYPES_DEFINITIONS, Warn, "proper use of libc types in foreign item definitions" @@ -533,10 +639,30 @@ crate fn nonnull_optimization_guaranteed<'tcx>(tcx: TyCtxt<'tcx>, def: &ty::AdtD .any(|a| tcx.sess.check_name(a, sym::rustc_nonnull_optimization_guaranteed)) } +/// `repr(transparent)` structs can have a single non-ZST field, this function returns that +/// field. +pub fn transparent_newtype_field<'a, 'tcx>( + tcx: TyCtxt<'tcx>, + variant: &'a ty::VariantDef, +) -> Option<&'a ty::FieldDef> { + let param_env = tcx.param_env(variant.def_id); + for field in &variant.fields { + let field_ty = tcx.type_of(field.did); + let is_zst = + tcx.layout_of(param_env.and(field_ty)).map(|layout| layout.is_zst()).unwrap_or(false); + + if !is_zst { + return Some(field); + } + } + + None +} + /// Is type known to be non-null? crate fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool { let tcx = cx.tcx; - match ty.kind { + match ty.kind() { ty::FnPtr(_) => true, ty::Ref(..) => true, ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true, @@ -548,7 +674,7 @@ crate fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: C } for variant in &def.variants { - if let Some(field) = variant.transparent_newtype_field(tcx) { + if let Some(field) = transparent_newtype_field(cx.tcx, variant) { if ty_is_known_nonnull(cx, field.ty(tcx, substs), mode) { return true; } @@ -565,11 +691,11 @@ crate fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: C /// If the type passed in was not scalar, returns None. fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { let tcx = cx.tcx; - Some(match ty.kind { + Some(match *ty.kind() { ty::Adt(field_def, field_substs) => { let inner_field_ty = { let first_non_zst_ty = - field_def.variants.iter().filter_map(|v| v.transparent_newtype_field(tcx)); + field_def.variants.iter().filter_map(|v| transparent_newtype_field(cx.tcx, v)); debug_assert_eq!( first_non_zst_ty.clone().count(), 1, @@ -607,7 +733,7 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t } /// Check if this enum can be safely exported based on the "nullable pointer optimization". If it -/// can, return the the type that `ty` can be safely converted to, otherwise return `None`. +/// can, return the type that `ty` can be safely converted to, otherwise return `None`. /// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`, /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. /// FIXME: This duplicates code in codegen. @@ -617,7 +743,7 @@ crate fn repr_nullable_ptr<'tcx>( ckind: CItemKind, ) -> Option<Ty<'tcx>> { debug!("is_repr_nullable_ptr(cx, ty = {:?})", ty); - if let ty::Adt(ty_def, substs) = ty.kind { + if let ty::Adt(ty_def, substs) = ty.kind() { if ty_def.variants.len() != 2 { return None; } @@ -667,7 +793,7 @@ crate fn repr_nullable_ptr<'tcx>( impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Check if the type is array and emit an unsafe type lint. fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { - if let ty::Array(..) = ty.kind { + if let ty::Array(..) = ty.kind() { self.emit_ffi_unsafe_type_lint( ty, sp, @@ -710,7 +836,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if def.repr.transparent() { // Can assume that only one field is not a ZST, so only check // that field's type for FFI-safety. - if let Some(field) = variant.transparent_newtype_field(self.cx.tcx) { + if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) { self.check_field_type_for_ffi(cache, field, substs) } else { bug!("malformed transparent type"); @@ -755,7 +881,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiSafe; } - match ty.kind { + match ty.kind() { ty::Adt(def, _) if def.is_box() && matches!(self.mode, CItemKind::Definition) => { FfiSafe } @@ -994,7 +1120,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { diag.help(help); } diag.note(note); - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) { diag.span_note(sp, "the type is defined here"); } @@ -1011,7 +1137,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - match ty.kind { + match ty.kind() { ty::Opaque(..) => { self.ty = Some(ty); true diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index c793e81ebe3..1e8c30071e7 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -20,6 +20,29 @@ use rustc_span::{BytePos, Span, DUMMY_SP}; use tracing::debug; declare_lint! { + /// The `unused_must_use` lint detects unused result of a type flagged as + /// `#[must_use]`. + /// + /// ### Example + /// + /// ```rust + /// fn returns_result() -> Result<(), ()> { + /// Ok(()) + /// } + /// + /// fn main() { + /// returns_result(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The `#[must_use]` attribute is an indicator that it is a mistake to + /// ignore the value. See [the reference] for more details. + /// + /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute pub UNUSED_MUST_USE, Warn, "unused result of a type flagged as `#[must_use]`", @@ -27,6 +50,39 @@ declare_lint! { } declare_lint! { + /// The `unused_results` lint checks for the unused result of an + /// expression in a statement. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_results)] + /// fn foo<T>() -> T { panic!() } + /// + /// fn main() { + /// foo::<usize>(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Ignoring the return value of a function may indicate a mistake. In + /// cases were it is almost certain that the result should be used, it is + /// recommended to annotate the function with the [`must_use` attribute]. + /// Failure to use such a return value will trigger the [`unused_must_use` + /// lint] which is warn-by-default. The `unused_results` lint is + /// essentially the same, but triggers for *all* return values. + /// + /// This lint is "allow" by default because it can be noisy, and may not be + /// an actual problem. For example, calling the `remove` method of a `Vec` + /// or `HashMap` returns the previous value, which you may not care about. + /// Using this lint would require explicitly ignoring or discarding such + /// values. + /// + /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use pub UNUSED_RESULTS, Allow, "unused result of an expression in a statement" @@ -135,7 +191,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let plural_suffix = pluralize!(plural_len); - match ty.kind { + match *ty.kind() { ty::Adt(..) if ty.is_box() => { let boxed_ty = ty.boxed_ty(); let descr_pre = &format!("{}boxed ", descr_pre); @@ -265,6 +321,21 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } declare_lint! { + /// The `path_statements` lint detects path statements with no effect. + /// + /// ### Example + /// + /// ```rust + /// let x = 42; + /// + /// x; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to have a statement that has no effect. pub PATH_STATEMENTS, Warn, "path statements with no effect" @@ -635,6 +706,21 @@ trait UnusedDelimLint { } declare_lint! { + /// The `unused_parens` lint detects `if`, `match`, `while` and `return` + /// with parentheses; they do not need them. + /// + /// ### Examples + /// + /// ```rust + /// if(true) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The parenthesis are not needed, and should be removed. This is the + /// preferred style for writing these expressions. pub(super) UNUSED_PARENS, Warn, "`if`, `match`, `while` and `return` do not need parentheses" @@ -808,6 +894,23 @@ impl EarlyLintPass for UnusedParens { } declare_lint! { + /// The `unused_braces` lint detects unnecessary braces around an + /// expression. + /// + /// ### Example + /// + /// ```rust + /// if { true } { + /// // ... + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The braces are not needed, and should be removed. This is the + /// preferred style for writing these expressions. pub(super) UNUSED_BRACES, Warn, "unnecessary braces around an expression" @@ -929,6 +1032,30 @@ impl EarlyLintPass for UnusedBraces { } declare_lint! { + /// The `unused_import_braces` lint catches unnecessary braces around an + /// imported item. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_import_braces)] + /// use test::{A}; + /// + /// pub mod test { + /// pub struct A; + /// } + /// # fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// If there is only a single item, then remove the braces (`use test::A;` + /// for example). + /// + /// This lint is "allow" by default because it is only enforcing a + /// stylistic choice. UNUSED_IMPORT_BRACES, Allow, "unnecessary braces around an imported item" @@ -978,6 +1105,25 @@ impl EarlyLintPass for UnusedImportBraces { } declare_lint! { + /// The `unused_allocation` lint detects unnecessary allocations that can + /// be eliminated. + /// + /// ### Example + /// + /// ```rust + /// #![feature(box_syntax)] + /// fn main() { + /// let a = (box [1,2,3]).len(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When a `box` expression is immediately coerced to a reference, then + /// the allocation is unnecessary, and a reference (using `&` or `&mut`) + /// should be used instead to avoid the allocation. pub(super) UNUSED_ALLOCATION, Warn, "detects unnecessary allocations that can be eliminated" diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml new file mode 100644 index 00000000000..e29af053289 --- /dev/null +++ b/compiler/rustc_llvm/Cargo.toml @@ -0,0 +1,16 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_llvm" +version = "0.0.0" +edition = "2018" + +[features] +static-libstdcpp = [] +emscripten = [] + +[dependencies] +libc = "0.2.73" + +[build-dependencies] +build_helper = { path = "../../src/build_helper" } +cc = "1.0.60" diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs new file mode 100644 index 00000000000..7f1e5cf336a --- /dev/null +++ b/compiler/rustc_llvm/build.rs @@ -0,0 +1,322 @@ +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use build_helper::{output, tracked_env_var_os}; + +fn detect_llvm_link() -> (&'static str, &'static str) { + // Force the link mode we want, preferring static by default, but + // possibly overridden by `configure --enable-llvm-link-shared`. + if tracked_env_var_os("LLVM_LINK_SHARED").is_some() { + ("dylib", "--link-shared") + } else { + ("static", "--link-static") + } +} + +fn main() { + if tracked_env_var_os("RUST_CHECK").is_some() { + // If we're just running `check`, there's no need for LLVM to be built. + return; + } + + build_helper::restore_library_path(); + + let target = env::var("TARGET").expect("TARGET was not set"); + let llvm_config = + tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| { + if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) { + let to_test = dir + .parent() + .unwrap() + .parent() + .unwrap() + .join(&target) + .join("llvm/bin/llvm-config"); + if Command::new(&to_test).output().is_ok() { + return Some(to_test); + } + } + None + }); + + if let Some(llvm_config) = &llvm_config { + println!("cargo:rerun-if-changed={}", llvm_config.display()); + } + let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config")); + + // Test whether we're cross-compiling LLVM. This is a pretty rare case + // currently where we're producing an LLVM for a different platform than + // what this build script is currently running on. + // + // In that case, there's no guarantee that we can actually run the target, + // so the build system works around this by giving us the LLVM_CONFIG for + // the host platform. This only really works if the host LLVM and target + // LLVM are compiled the same way, but for us that's typically the case. + // + // We *want* detect this cross compiling situation by asking llvm-config + // what its host-target is. If that's not the TARGET, then we're cross + // compiling. Unfortunately `llvm-config` seems either be buggy, or we're + // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will + // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This + // tricks us into thinking we're doing a cross build when we aren't, so + // havoc ensues. + // + // In any case, if we're cross compiling, this generally just means that we + // can't trust all the output of llvm-config because it might be targeted + // for the host rather than the target. As a result a bunch of blocks below + // are gated on `if !is_crossed` + let target = env::var("TARGET").expect("TARGET was not set"); + let host = env::var("HOST").expect("HOST was not set"); + let is_crossed = target != host; + + let mut optional_components = vec![ + "x86", + "arm", + "aarch64", + "amdgpu", + "avr", + "mips", + "powerpc", + "systemz", + "jsbackend", + "webassembly", + "msp430", + "sparc", + "nvptx", + "hexagon", + ]; + + let mut version_cmd = Command::new(&llvm_config); + version_cmd.arg("--version"); + let version_output = output(&mut version_cmd); + let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::<u32>().ok()); + let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { + (major, minor) + } else { + (6, 0) + }; + + if major > 6 { + optional_components.push("riscv"); + } + + let required_components = &[ + "ipo", + "bitreader", + "bitwriter", + "linker", + "asmparser", + "lto", + "coverage", + "instrumentation", + ]; + + let components = output(Command::new(&llvm_config).arg("--components")); + let mut components = components.split_whitespace().collect::<Vec<_>>(); + components.retain(|c| optional_components.contains(c) || required_components.contains(c)); + + for component in required_components { + if !components.contains(component) { + panic!("require llvm component {} but wasn't found", component); + } + } + + for component in components.iter() { + println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); + } + + if major >= 9 { + println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser"); + } + + // Link in our own LLVM shims, compiled with the same flags as LLVM + let mut cmd = Command::new(&llvm_config); + cmd.arg("--cxxflags"); + let cxxflags = output(&mut cmd); + let mut cfg = cc::Build::new(); + cfg.warnings(false); + for flag in cxxflags.split_whitespace() { + // Ignore flags like `-m64` when we're doing a cross build + if is_crossed && flag.starts_with("-m") { + continue; + } + + if flag.starts_with("-flto") { + continue; + } + + // -Wdate-time is not supported by the netbsd cross compiler + if is_crossed && target.contains("netbsd") && flag.contains("date-time") { + continue; + } + + // Include path contains host directory, replace it with target + if is_crossed && flag.starts_with("-I") { + cfg.flag(&flag.replace(&host, &target)); + continue; + } + + cfg.flag(flag); + } + + for component in &components { + let mut flag = String::from("LLVM_COMPONENT_"); + flag.push_str(&component.to_uppercase()); + cfg.define(&flag, None); + } + + if tracked_env_var_os("LLVM_RUSTLLVM").is_some() { + cfg.define("LLVM_RUSTLLVM", None); + } + + if tracked_env_var_os("LLVM_NDEBUG").is_some() { + cfg.define("NDEBUG", None); + cfg.debug(false); + } + + build_helper::rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper")); + cfg.file("llvm-wrapper/PassWrapper.cpp") + .file("llvm-wrapper/RustWrapper.cpp") + .file("llvm-wrapper/ArchiveWrapper.cpp") + .file("llvm-wrapper/CoverageMappingWrapper.cpp") + .file("llvm-wrapper/Linker.cpp") + .cpp(true) + .cpp_link_stdlib(None) // we handle this below + .compile("llvm-wrapper"); + + let (llvm_kind, llvm_link_arg) = detect_llvm_link(); + + // Link in all LLVM libraries, if we're using the "wrong" llvm-config then + // we don't pick up system libs because unfortunately they're for the host + // of llvm-config, not the target that we're attempting to link. + let mut cmd = Command::new(&llvm_config); + cmd.arg(llvm_link_arg).arg("--libs"); + + if !is_crossed { + cmd.arg("--system-libs"); + } else if target.contains("windows-gnu") { + println!("cargo:rustc-link-lib=shell32"); + println!("cargo:rustc-link-lib=uuid"); + } else if target.contains("netbsd") || target.contains("haiku") { + println!("cargo:rustc-link-lib=z"); + } + cmd.args(&components); + + for lib in output(&mut cmd).split_whitespace() { + let name = if lib.starts_with("-l") { + &lib[2..] + } else if lib.starts_with('-') { + &lib[1..] + } else if Path::new(lib).exists() { + // On MSVC llvm-config will print the full name to libraries, but + // we're only interested in the name part + let name = Path::new(lib).file_name().unwrap().to_str().unwrap(); + name.trim_end_matches(".lib") + } else if lib.ends_with(".lib") { + // Some MSVC libraries just come up with `.lib` tacked on, so chop + // that off + lib.trim_end_matches(".lib") + } else { + continue; + }; + + // Don't need or want this library, but LLVM's CMake build system + // doesn't provide a way to disable it, so filter it here even though we + // may or may not have built it. We don't reference anything from this + // library and it otherwise may just pull in extra dependencies on + // libedit which we don't want + if name == "LLVMLineEditor" { + continue; + } + + let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" }; + println!("cargo:rustc-link-lib={}={}", kind, name); + } + + // LLVM ldflags + // + // If we're a cross-compile of LLVM then unfortunately we can't trust these + // ldflags (largely where all the LLVM libs are located). Currently just + // hack around this by replacing the host triple with the target and pray + // that those -L directories are the same! + let mut cmd = Command::new(&llvm_config); + cmd.arg(llvm_link_arg).arg("--ldflags"); + for lib in output(&mut cmd).split_whitespace() { + if is_crossed { + if lib.starts_with("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target)); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target)); + } + } else if lib.starts_with("-LIBPATH:") { + println!("cargo:rustc-link-search=native={}", &lib[9..]); + } else if lib.starts_with("-l") { + println!("cargo:rustc-link-lib={}", &lib[2..]); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", &lib[2..]); + } + } + + // Some LLVM linker flags (-L and -l) may be needed even when linking + // rustc_llvm, for example when using static libc++, we may need to + // manually specify the library search path and -ldl -lpthread as link + // dependencies. + let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS"); + if let Some(s) = llvm_linker_flags { + for lib in s.into_string().unwrap().split_whitespace() { + if lib.starts_with("-l") { + println!("cargo:rustc-link-lib={}", &lib[2..]); + } else if lib.starts_with("-L") { + println!("cargo:rustc-link-search=native={}", &lib[2..]); + } + } + } + + let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP"); + let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX"); + + let stdcppname = if target.contains("openbsd") { + if target.contains("sparc64") { "estdc++" } else { "c++" } + } else if target.contains("freebsd") { + "c++" + } else if target.contains("darwin") { + "c++" + } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() { + // NetBSD uses a separate library when relocation is required + "stdc++_pic" + } else if llvm_use_libcxx.is_some() { + "c++" + } else { + "stdc++" + }; + + // RISC-V requires libatomic for sub-word atomic operations + if target.starts_with("riscv") { + println!("cargo:rustc-link-lib=atomic"); + } + + // C++ runtime library + if !target.contains("msvc") { + if let Some(s) = llvm_static_stdcpp { + assert!(!cxxflags.contains("stdlib=libc++")); + 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); + } else { + println!("cargo:rustc-link-lib=static={}", stdcppname); + } + } else if cxxflags.contains("stdlib=libc++") { + println!("cargo:rustc-link-lib=c++"); + } else { + println!("cargo:rustc-link-lib={}", stdcppname); + } + } + + // 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"); + } +} diff --git a/compiler/rustc_llvm/llvm-wrapper/.editorconfig b/compiler/rustc_llvm/llvm-wrapper/.editorconfig new file mode 100644 index 00000000000..865cd45f708 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/.editorconfig @@ -0,0 +1,6 @@ +[*.{h,cpp}] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp new file mode 100644 index 00000000000..2797fe8df4a --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp @@ -0,0 +1,226 @@ +#include "LLVMWrapper.h" + +#include "llvm/Object/Archive.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::object; + +struct RustArchiveMember { + const char *Filename; + const char *Name; + Archive::Child Child; + + RustArchiveMember() + : Filename(nullptr), Name(nullptr), + Child(nullptr, nullptr, nullptr) + { + } + ~RustArchiveMember() {} +}; + +struct RustArchiveIterator { + bool First; + Archive::child_iterator Cur; + Archive::child_iterator End; + std::unique_ptr<Error> Err; + + RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End, + std::unique_ptr<Error> Err) + : First(true), + Cur(Cur), + End(End), + Err(std::move(Err)) {} +}; + +enum class LLVMRustArchiveKind { + GNU, + BSD, + DARWIN, + COFF, +}; + +static Archive::Kind fromRust(LLVMRustArchiveKind Kind) { + switch (Kind) { + case LLVMRustArchiveKind::GNU: + return Archive::K_GNU; + case LLVMRustArchiveKind::BSD: + return Archive::K_BSD; + case LLVMRustArchiveKind::DARWIN: + return Archive::K_DARWIN; + case LLVMRustArchiveKind::COFF: + return Archive::K_COFF; + default: + report_fatal_error("Bad ArchiveKind."); + } +} + +typedef OwningBinary<Archive> *LLVMRustArchiveRef; +typedef RustArchiveMember *LLVMRustArchiveMemberRef; +typedef Archive::Child *LLVMRustArchiveChildRef; +typedef Archive::Child const *LLVMRustArchiveChildConstRef; +typedef RustArchiveIterator *LLVMRustArchiveIteratorRef; + +extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr = + MemoryBuffer::getFile(Path, -1, false); + if (!BufOr) { + LLVMRustSetLastError(BufOr.getError().message().c_str()); + return nullptr; + } + + Expected<std::unique_ptr<Archive>> ArchiveOr = + Archive::create(BufOr.get()->getMemBufferRef()); + + if (!ArchiveOr) { + LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str()); + return nullptr; + } + + OwningBinary<Archive> *Ret = new OwningBinary<Archive>( + std::move(ArchiveOr.get()), std::move(BufOr.get())); + + return Ret; +} + +extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) { + delete RustArchive; +} + +extern "C" LLVMRustArchiveIteratorRef +LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) { + Archive *Archive = RustArchive->getBinary(); +#if LLVM_VERSION_GE(10, 0) + std::unique_ptr<Error> Err = std::make_unique<Error>(Error::success()); +#else + std::unique_ptr<Error> Err = llvm::make_unique<Error>(Error::success()); +#endif + auto Cur = Archive->child_begin(*Err); + if (*Err) { + LLVMRustSetLastError(toString(std::move(*Err)).c_str()); + return nullptr; + } + auto End = Archive->child_end(); + return new RustArchiveIterator(Cur, End, std::move(Err)); +} + +extern "C" LLVMRustArchiveChildConstRef +LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) { + if (RAI->Cur == RAI->End) + return nullptr; + + // Advancing the iterator validates the next child, and this can + // uncover an error. LLVM requires that we check all Errors, + // so we only advance the iterator if we actually need to fetch + // the next child. + // This means we must not advance the iterator in the *first* call, + // but instead advance it *before* fetching the child in all later calls. + if (!RAI->First) { + ++RAI->Cur; + if (*RAI->Err) { + LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str()); + return nullptr; + } + } else { + RAI->First = false; + } + + if (RAI->Cur == RAI->End) + return nullptr; + + const Archive::Child &Child = *RAI->Cur.operator->(); + Archive::Child *Ret = new Archive::Child(Child); + + return Ret; +} + +extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) { + delete Child; +} + +extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) { + delete RAI; +} + +extern "C" const char * +LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) { + Expected<StringRef> NameOrErr = Child->getName(); + if (!NameOrErr) { + // rustc_codegen_llvm currently doesn't use this error string, but it might be + // useful in the future, and in the mean time this tells LLVM that the + // error was not ignored and that it shouldn't abort the process. + LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str()); + return nullptr; + } + StringRef Name = NameOrErr.get(); + *Size = Name.size(); + return Name.data(); +} + +extern "C" const char *LLVMRustArchiveChildData(LLVMRustArchiveChildRef Child, + size_t *Size) { + StringRef Buf; + Expected<StringRef> BufOrErr = Child->getBuffer(); + if (!BufOrErr) { + LLVMRustSetLastError(toString(BufOrErr.takeError()).c_str()); + return nullptr; + } + Buf = BufOrErr.get(); + *Size = Buf.size(); + return Buf.data(); +} + +extern "C" LLVMRustArchiveMemberRef +LLVMRustArchiveMemberNew(char *Filename, char *Name, + LLVMRustArchiveChildRef Child) { + RustArchiveMember *Member = new RustArchiveMember; + Member->Filename = Filename; + Member->Name = Name; + if (Child) + Member->Child = *Child; + return Member; +} + +extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) { + delete Member; +} + +extern "C" LLVMRustResult +LLVMRustWriteArchive(char *Dst, size_t NumMembers, + const LLVMRustArchiveMemberRef *NewMembers, + bool WriteSymbtab, LLVMRustArchiveKind RustKind) { + + std::vector<NewArchiveMember> Members; + auto Kind = fromRust(RustKind); + + for (size_t I = 0; I < NumMembers; I++) { + auto Member = NewMembers[I]; + assert(Member->Name); + if (Member->Filename) { + Expected<NewArchiveMember> MOrErr = + NewArchiveMember::getFile(Member->Filename, true); + if (!MOrErr) { + LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); + return LLVMRustResult::Failure; + } + MOrErr->MemberName = sys::path::filename(MOrErr->MemberName); + Members.push_back(std::move(*MOrErr)); + } else { + Expected<NewArchiveMember> MOrErr = + NewArchiveMember::getOldMember(Member->Child, true); + if (!MOrErr) { + LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); + return LLVMRustResult::Failure; + } + Members.push_back(std::move(*MOrErr)); + } + } + + auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false); + if (!Result) + return LLVMRustResult::Success; + LLVMRustSetLastError(toString(std::move(Result)).c_str()); + + return LLVMRustResult::Failure; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp new file mode 100644 index 00000000000..2b1143a4ecf --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -0,0 +1,70 @@ +#include "LLVMWrapper.h" +#include "llvm/ProfileData/Coverage/CoverageMapping.h" +#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/LEB128.h" + +#include <iostream> + +using namespace llvm; + +extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( + const char* const Filenames[], + size_t FilenamesLen, + RustStringRef BufferOut) { + // LLVM 11's CoverageFilenamesSectionWriter uses its new `Version4` format, + // so we're manually writing the `Version3` format ourselves. + RawRustStringOstream OS(BufferOut); + encodeULEB128(FilenamesLen, OS); + for (size_t i = 0; i < FilenamesLen; i++) { + StringRef Filename(Filenames[i]); + encodeULEB128(Filename.size(), OS); + OS << Filename; + } +} + +extern "C" void LLVMRustCoverageWriteMappingToBuffer( + const unsigned *VirtualFileMappingIDs, + unsigned NumVirtualFileMappingIDs, + const coverage::CounterExpression *Expressions, + unsigned NumExpressions, + coverage::CounterMappingRegion *MappingRegions, + unsigned NumMappingRegions, + RustStringRef BufferOut) { + auto CoverageMappingWriter = coverage::CoverageMappingWriter( + makeArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs), + makeArrayRef(Expressions, NumExpressions), + makeMutableArrayRef(MappingRegions, NumMappingRegions)); + RawRustStringOstream OS(BufferOut); + CoverageMappingWriter.write(OS); +} + +extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName) { + StringRef FuncNameRef(FuncName); + return wrap(createPGOFuncNameVar(*cast<Function>(unwrap(F)), FuncNameRef)); +} + +extern "C" uint64_t LLVMRustCoverageComputeHash(const char *Name) { + StringRef NameRef(Name); + return IndexedInstrProf::ComputeHash(NameRef); +} + +extern "C" void LLVMRustCoverageWriteSectionNameToString(LLVMModuleRef M, + RustStringRef Str) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + auto name = getInstrProfSectionName(IPSK_covmap, + TargetTriple.getObjectFormat()); + RawRustStringOstream OS(Str); + OS << name; +} + +extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) { + auto name = getCoverageMappingVarName(); + RawRustStringOstream OS(Str); + OS << name; +} + +extern "C" uint32_t LLVMRustCoverageMappingVersion() { + return coverage::CovMapVersion::Version3; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h new file mode 100644 index 00000000000..57b8664d3b6 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -0,0 +1,115 @@ +#include "llvm-c/BitReader.h" +#include "llvm-c/Core.h" +#include "llvm-c/Object.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Analysis/Lint.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/FormattedStream.h" +#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" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Vectorize.h" + +#define LLVM_VERSION_GE(major, minor) \ + (LLVM_VERSION_MAJOR > (major) || \ + LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor)) + +#define LLVM_VERSION_EQ(major, minor) \ + (LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR == (minor)) + +#define LLVM_VERSION_LE(major, minor) \ + (LLVM_VERSION_MAJOR < (major) || \ + LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR <= (minor)) + +#define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor))) + +#include "llvm/IR/LegacyPassManager.h" + +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/Bitcode/BitcodeWriter.h" + +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/IRPrintingPasses.h" +#include "llvm/Linker/Linker.h" + +extern "C" void LLVMRustSetLastError(const char *); + +enum class LLVMRustResult { Success, Failure }; + +enum LLVMRustAttribute { + AlwaysInline = 0, + ByVal = 1, + Cold = 2, + InlineHint = 3, + MinSize = 4, + Naked = 5, + NoAlias = 6, + NoCapture = 7, + NoInline = 8, + NonNull = 9, + NoRedZone = 10, + NoReturn = 11, + NoUnwind = 12, + OptimizeForSize = 13, + ReadOnly = 14, + SExt = 15, + StructRet = 16, + UWTable = 17, + ZExt = 18, + InReg = 19, + SanitizeThread = 20, + SanitizeAddress = 21, + SanitizeMemory = 22, + NonLazyBind = 23, + OptimizeNone = 24, + ReturnsTwice = 25, + ReadNone = 26, + InaccessibleMemOnly = 27, +}; + +typedef struct OpaqueRustString *RustStringRef; +typedef struct LLVMOpaqueTwine *LLVMTwineRef; +typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef; + +extern "C" void LLVMRustStringWriteImpl(RustStringRef Str, const char *Ptr, + size_t Size); + +class RawRustStringOstream : public llvm::raw_ostream { + RustStringRef Str; + uint64_t Pos; + + void write_impl(const char *Ptr, size_t Size) override { + LLVMRustStringWriteImpl(Str, Ptr, Size); + Pos += Size; + } + + uint64_t current_pos() const override { return Pos; } + +public: + explicit RawRustStringOstream(RustStringRef Str) : Str(Str), Pos(0) {} + + ~RawRustStringOstream() { + // LLVM requires this. + flush(); + } +}; diff --git a/compiler/rustc_llvm/llvm-wrapper/Linker.cpp b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp new file mode 100644 index 00000000000..8766e96f086 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp @@ -0,0 +1,48 @@ +#include "llvm/Linker/Linker.h" + +#include "LLVMWrapper.h" + +using namespace llvm; + +struct RustLinker { + Linker L; + LLVMContext &Ctx; + + RustLinker(Module &M) : + L(M), + Ctx(M.getContext()) + {} +}; + +extern "C" RustLinker* +LLVMRustLinkerNew(LLVMModuleRef DstRef) { + Module *Dst = unwrap(DstRef); + + return new RustLinker(*Dst); +} + +extern "C" void +LLVMRustLinkerFree(RustLinker *L) { + delete L; +} + +extern "C" bool +LLVMRustLinkerAdd(RustLinker *L, char *BC, size_t Len) { + std::unique_ptr<MemoryBuffer> Buf = + MemoryBuffer::getMemBufferCopy(StringRef(BC, Len)); + + Expected<std::unique_ptr<Module>> SrcOrError = + llvm::getLazyBitcodeModule(Buf->getMemBufferRef(), L->Ctx); + if (!SrcOrError) { + LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str()); + return false; + } + + auto Src = std::move(*SrcOrError); + + if (L->L.linkInModule(std::move(Src))) { + LLVMRustSetLastError(""); + return false; + } + return true; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp new file mode 100644 index 00000000000..7b1c3f9ba2c --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -0,0 +1,1655 @@ +#include <stdio.h> + +#include <vector> +#include <set> + +#include "LLVMWrapper.h" + +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/InitializePasses.h" +#include "llvm/IR/AutoUpgrade.h" +#include "llvm/IR/AssemblyAnnotationWriter.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/IRObjectFile.h" +#include "llvm/Passes/PassBuilder.h" +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Passes/StandardInstrumentations.h" +#endif +#include "llvm/Support/CBindingWrapping.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/IPO/AlwaysInliner.h" +#include "llvm/Transforms/IPO/FunctionImport.h" +#include "llvm/Transforms/Utils/FunctionImportUtils.h" +#include "llvm/LTO/LTO.h" +#include "llvm-c/Transforms/PassManagerBuilder.h" + +#include "llvm/Transforms/Instrumentation.h" +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Transforms/Instrumentation/AddressSanitizer.h" +#include "llvm/Support/TimeProfiler.h" +#endif +#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" +#include "llvm/Transforms/Instrumentation/MemorySanitizer.h" +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Transforms/Utils/CanonicalizeAliases.h" +#endif +#include "llvm/Transforms/Utils/NameAnonGlobals.h" + +using namespace llvm; + +typedef struct LLVMOpaquePass *LLVMPassRef; +typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef; + +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(); + initializeCore(Registry); + initializeCodeGen(Registry); + initializeScalarOpts(Registry); + initializeVectorization(Registry); + initializeIPO(Registry); + initializeAnalysis(Registry); + initializeTransformUtils(Registry); + initializeInstCombine(Registry); + initializeInstrumentation(Registry); + initializeTarget(Registry); +} + +extern "C" void LLVMTimeTraceProfilerInitialize() { +#if LLVM_VERSION_GE(10, 0) + timeTraceProfilerInitialize( + /* TimeTraceGranularity */ 0, + /* ProcName */ "rustc"); +#elif LLVM_VERSION_GE(9, 0) + timeTraceProfilerInitialize(); +#endif +} + +extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) { +#if LLVM_VERSION_GE(9, 0) + StringRef FN(FileName); + std::error_code EC; + raw_fd_ostream OS(FN, EC, sys::fs::CD_CreateAlways); + + timeTraceProfilerWrite(OS); + timeTraceProfilerCleanup(); +#endif +} + +enum class LLVMRustPassKind { + Other, + Function, + Module, +}; + +static LLVMRustPassKind toRust(PassKind Kind) { + switch (Kind) { + case PT_Function: + return LLVMRustPassKind::Function; + case PT_Module: + return LLVMRustPassKind::Module; + default: + return LLVMRustPassKind::Other; + } +} + +extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) { + StringRef SR(PassName); + PassRegistry *PR = PassRegistry::getPassRegistry(); + + const PassInfo *PI = PR->getPassInfo(SR); + if (PI) { + return wrap(PI->createPass()); + } + return nullptr; +} + +extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) { + const bool CompileKernel = false; + const bool UseAfterScope = true; + + return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover, UseAfterScope)); +} + +extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) { + const bool CompileKernel = false; + +#if LLVM_VERSION_GE(9, 0) + return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover)); +#else + return wrap(createAddressSanitizerModulePass(CompileKernel, Recover)); +#endif +} + +extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) { +#if LLVM_VERSION_GE(9, 0) + const bool CompileKernel = false; + + return wrap(createMemorySanitizerLegacyPassPass( + MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel})); +#else + return wrap(createMemorySanitizerLegacyPassPass(TrackOrigins, Recover)); +#endif +} + +extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() { + return wrap(createThreadSanitizerLegacyPassPass()); +} + +extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) { + assert(RustPass); + Pass *Pass = unwrap(RustPass); + return toRust(Pass->getPassKind()); +} + +extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) { + assert(RustPass); + Pass *Pass = unwrap(RustPass); + PassManagerBase *PMB = unwrap(PMR); + PMB->add(Pass); +} + +extern "C" +void LLVMRustPassManagerBuilderPopulateThinLTOPassManager( + LLVMPassManagerBuilderRef PMBR, + LLVMPassManagerRef PMR +) { + unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR)); +} + +extern "C" +void LLVMRustAddLastExtensionPasses( + LLVMPassManagerBuilderRef PMBR, LLVMPassRef *Passes, size_t NumPasses) { + auto AddExtensionPasses = [Passes, NumPasses]( + const PassManagerBuilder &Builder, PassManagerBase &PM) { + for (size_t I = 0; I < NumPasses; I++) { + PM.add(unwrap(Passes[I])); + } + }; + // Add the passes to both of the pre-finalization extension points, + // so they are run for optimized and non-optimized builds. + unwrap(PMBR)->addExtension(PassManagerBuilder::EP_OptimizerLast, + AddExtensionPasses); + unwrap(PMBR)->addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, + AddExtensionPasses); +} + +#ifdef LLVM_COMPONENT_X86 +#define SUBTARGET_X86 SUBTARGET(X86) +#else +#define SUBTARGET_X86 +#endif + +#ifdef LLVM_COMPONENT_ARM +#define SUBTARGET_ARM SUBTARGET(ARM) +#else +#define SUBTARGET_ARM +#endif + +#ifdef LLVM_COMPONENT_AARCH64 +#define SUBTARGET_AARCH64 SUBTARGET(AArch64) +#else +#define SUBTARGET_AARCH64 +#endif + +#ifdef LLVM_COMPONENT_AVR +#define SUBTARGET_AVR SUBTARGET(AVR) +#else +#define SUBTARGET_AVR +#endif + +#ifdef LLVM_COMPONENT_MIPS +#define SUBTARGET_MIPS SUBTARGET(Mips) +#else +#define SUBTARGET_MIPS +#endif + +#ifdef LLVM_COMPONENT_POWERPC +#define SUBTARGET_PPC SUBTARGET(PPC) +#else +#define SUBTARGET_PPC +#endif + +#ifdef LLVM_COMPONENT_SYSTEMZ +#define SUBTARGET_SYSTEMZ SUBTARGET(SystemZ) +#else +#define SUBTARGET_SYSTEMZ +#endif + +#ifdef LLVM_COMPONENT_MSP430 +#define SUBTARGET_MSP430 SUBTARGET(MSP430) +#else +#define SUBTARGET_MSP430 +#endif + +#ifdef LLVM_COMPONENT_RISCV +#define SUBTARGET_RISCV SUBTARGET(RISCV) +#else +#define SUBTARGET_RISCV +#endif + +#ifdef LLVM_COMPONENT_SPARC +#define SUBTARGET_SPARC SUBTARGET(Sparc) +#else +#define SUBTARGET_SPARC +#endif + +#ifdef LLVM_COMPONENT_HEXAGON +#define SUBTARGET_HEXAGON SUBTARGET(Hexagon) +#else +#define SUBTARGET_HEXAGON +#endif + +#define GEN_SUBTARGETS \ + SUBTARGET_X86 \ + SUBTARGET_ARM \ + SUBTARGET_AARCH64 \ + SUBTARGET_AVR \ + SUBTARGET_MIPS \ + SUBTARGET_PPC \ + SUBTARGET_SYSTEMZ \ + SUBTARGET_MSP430 \ + SUBTARGET_SPARC \ + SUBTARGET_HEXAGON \ + SUBTARGET_RISCV \ + +#define SUBTARGET(x) \ + namespace llvm { \ + extern const SubtargetFeatureKV x##FeatureKV[]; \ + extern const SubtargetFeatureKV x##SubTypeKV[]; \ + } + +GEN_SUBTARGETS +#undef SUBTARGET + +extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM, + const char *Feature) { + TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + return MCInfo->checkFeatures(std::string("+") + Feature); +} + +enum class LLVMRustCodeModel { + Tiny, + Small, + Kernel, + Medium, + Large, + None, +}; + +static Optional<CodeModel::Model> fromRust(LLVMRustCodeModel Model) { + switch (Model) { + case LLVMRustCodeModel::Tiny: + return CodeModel::Tiny; + case LLVMRustCodeModel::Small: + return CodeModel::Small; + case LLVMRustCodeModel::Kernel: + return CodeModel::Kernel; + case LLVMRustCodeModel::Medium: + return CodeModel::Medium; + case LLVMRustCodeModel::Large: + return CodeModel::Large; + case LLVMRustCodeModel::None: + return None; + default: + report_fatal_error("Bad CodeModel."); + } +} + +enum class LLVMRustCodeGenOptLevel { + None, + Less, + Default, + Aggressive, +}; + +static CodeGenOpt::Level fromRust(LLVMRustCodeGenOptLevel Level) { + switch (Level) { + case LLVMRustCodeGenOptLevel::None: + return CodeGenOpt::None; + case LLVMRustCodeGenOptLevel::Less: + return CodeGenOpt::Less; + case LLVMRustCodeGenOptLevel::Default: + return CodeGenOpt::Default; + case LLVMRustCodeGenOptLevel::Aggressive: + return CodeGenOpt::Aggressive; + default: + report_fatal_error("Bad CodeGenOptLevel."); + } +} + +enum class LLVMRustPassBuilderOptLevel { + O0, + O1, + O2, + O3, + Os, + Oz, +}; + +static PassBuilder::OptimizationLevel fromRust(LLVMRustPassBuilderOptLevel Level) { + switch (Level) { + case LLVMRustPassBuilderOptLevel::O0: + return PassBuilder::OptimizationLevel::O0; + case LLVMRustPassBuilderOptLevel::O1: + return PassBuilder::OptimizationLevel::O1; + case LLVMRustPassBuilderOptLevel::O2: + return PassBuilder::OptimizationLevel::O2; + case LLVMRustPassBuilderOptLevel::O3: + return PassBuilder::OptimizationLevel::O3; + case LLVMRustPassBuilderOptLevel::Os: + return PassBuilder::OptimizationLevel::Os; + case LLVMRustPassBuilderOptLevel::Oz: + return PassBuilder::OptimizationLevel::Oz; + default: + report_fatal_error("Bad PassBuilderOptLevel."); + } +} + +enum class LLVMRustRelocModel { + Static, + PIC, + DynamicNoPic, + ROPI, + RWPI, + ROPIRWPI, +}; + +static Reloc::Model fromRust(LLVMRustRelocModel RustReloc) { + switch (RustReloc) { + case LLVMRustRelocModel::Static: + return Reloc::Static; + case LLVMRustRelocModel::PIC: + return Reloc::PIC_; + case LLVMRustRelocModel::DynamicNoPic: + return Reloc::DynamicNoPIC; + case LLVMRustRelocModel::ROPI: + return Reloc::ROPI; + case LLVMRustRelocModel::RWPI: + return Reloc::RWPI; + case LLVMRustRelocModel::ROPIRWPI: + return Reloc::ROPI_RWPI; + } + report_fatal_error("Bad RelocModel."); +} + +#ifdef LLVM_RUSTLLVM +/// getLongestEntryLength - Return the length of the longest entry in the table. +template<typename KV> +static size_t getLongestEntryLength(ArrayRef<KV> Table) { + size_t MaxLen = 0; + for (auto &I : Table) + MaxLen = std::max(MaxLen, std::strlen(I.Key)); + return MaxLen; +} + +extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) { + const TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + const Triple::ArchType HostArch = Triple(sys::getProcessTriple()).getArch(); + const Triple::ArchType TargetArch = Target->getTargetTriple().getArch(); + const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getCPUTable(); + unsigned MaxCPULen = getLongestEntryLength(CPUTable); + + printf("Available CPUs for this target:\n"); + if (HostArch == TargetArch) { + const StringRef HostCPU = sys::getHostCPUName(); + printf(" %-*s - Select the CPU of the current host (currently %.*s).\n", + MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data()); + } + for (auto &CPU : CPUTable) + printf(" %-*s\n", MaxCPULen, CPU.Key); + printf("\n"); +} + +extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { + const TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable(); + unsigned MaxFeatLen = getLongestEntryLength(FeatTable); + + printf("Available features for this target:\n"); + for (auto &Feature : FeatTable) + printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); + printf("\nRust-specific features:\n"); + printf(" %-*s - %s.\n", + MaxFeatLen, + "crt-static", + "Enables libraries with C Run-time Libraries(CRT) to be statically linked" + ); + printf("\n"); + + printf("Use +feature to enable a feature, or -feature to disable it.\n" + "For example, rustc -C -target-cpu=mycpu -C " + "target-feature=+feature1,-feature2\n\n"); +} + +#else + +extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef) { + printf("Target CPU help is not supported by this LLVM version.\n\n"); +} + +extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef) { + printf("Target features help is not supported by this LLVM version.\n\n"); +} +#endif + +extern "C" const char* LLVMRustGetHostCPUName(size_t *len) { + StringRef Name = sys::getHostCPUName(); + *len = Name.size(); + return Name.data(); +} + +extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( + const char *TripleStr, const char *CPU, const char *Feature, + const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocModel RustReloc, + LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat, + bool FunctionSections, + bool DataSections, + bool TrapUnreachable, + bool Singlethread, + bool AsmComments, + bool EmitStackSizeSection, + bool RelaxELFRelocations, + bool UseInitArray) { + + auto OptLevel = fromRust(RustOptLevel); + auto RM = fromRust(RustReloc); + auto CM = fromRust(RustCM); + + std::string Error; + Triple Trip(Triple::normalize(TripleStr)); + const llvm::Target *TheTarget = + TargetRegistry::lookupTarget(Trip.getTriple(), Error); + if (TheTarget == nullptr) { + LLVMRustSetLastError(Error.c_str()); + return nullptr; + } + + TargetOptions Options; + + Options.FloatABIType = FloatABI::Default; + if (UseSoftFloat) { + Options.FloatABIType = FloatABI::Soft; + } + Options.DataSections = DataSections; + Options.FunctionSections = FunctionSections; + Options.MCOptions.AsmVerbose = AsmComments; + Options.MCOptions.PreserveAsmComments = AsmComments; + Options.MCOptions.ABIName = ABIStr; + Options.RelaxELFRelocations = RelaxELFRelocations; + Options.UseInitArray = UseInitArray; + + if (TrapUnreachable) { + // Tell LLVM to codegen `unreachable` into an explicit trap instruction. + // This limits the extent of possible undefined behavior in some cases, as + // it prevents control flow from "falling through" into whatever code + // happens to be laid out next in memory. + Options.TrapUnreachable = true; + } + + if (Singlethread) { + Options.ThreadModel = ThreadModel::Single; + } + + Options.EmitStackSizeSection = EmitStackSizeSection; + + TargetMachine *TM = TheTarget->createTargetMachine( + Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel); + return wrap(TM); +} + +extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { + delete unwrap(TM); +} + +extern "C" void LLVMRustConfigurePassManagerBuilder( + LLVMPassManagerBuilderRef PMBR, LLVMRustCodeGenOptLevel OptLevel, + bool MergeFunctions, bool SLPVectorize, bool LoopVectorize, bool PrepareForThinLTO, + const char* PGOGenPath, const char* PGOUsePath) { + unwrap(PMBR)->MergeFunctions = MergeFunctions; + unwrap(PMBR)->SLPVectorize = SLPVectorize; + unwrap(PMBR)->OptLevel = fromRust(OptLevel); + unwrap(PMBR)->LoopVectorize = LoopVectorize; + unwrap(PMBR)->PrepareForThinLTO = PrepareForThinLTO; + + if (PGOGenPath) { + assert(!PGOUsePath); + unwrap(PMBR)->EnablePGOInstrGen = true; + unwrap(PMBR)->PGOInstrGen = PGOGenPath; + } + if (PGOUsePath) { + assert(!PGOGenPath); + unwrap(PMBR)->PGOInstrUse = PGOUsePath; + } +} + +// Unfortunately, the LLVM C API doesn't provide a way to set the `LibraryInfo` +// field of a PassManagerBuilder, we expose our own method of doing so. +extern "C" void LLVMRustAddBuilderLibraryInfo(LLVMPassManagerBuilderRef PMBR, + LLVMModuleRef M, + bool DisableSimplifyLibCalls) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + TargetLibraryInfoImpl *TLI = new TargetLibraryInfoImpl(TargetTriple); + if (DisableSimplifyLibCalls) + TLI->disableAllFunctions(); + unwrap(PMBR)->LibraryInfo = TLI; +} + +// Unfortunately, the LLVM C API doesn't provide a way to create the +// TargetLibraryInfo pass, so we use this method to do so. +extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M, + bool DisableSimplifyLibCalls) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + TargetLibraryInfoImpl TLII(TargetTriple); + if (DisableSimplifyLibCalls) + TLII.disableAllFunctions(); + unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII)); +} + +// Unfortunately, the LLVM C API doesn't provide an easy way of iterating over +// all the functions in a module, so we do that manually here. You'll find +// similar code in clang's BackendUtil.cpp file. +extern "C" void LLVMRustRunFunctionPassManager(LLVMPassManagerRef PMR, + LLVMModuleRef M) { + llvm::legacy::FunctionPassManager *P = + unwrap<llvm::legacy::FunctionPassManager>(PMR); + P->doInitialization(); + + // Upgrade all calls to old intrinsics first. + for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E;) + UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove + + for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E; + ++I) + if (!I->isDeclaration()) + P->run(*I); + + P->doFinalization(); +} + +extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) { + // Initializing the command-line options more than once is not allowed. So, + // check if they've already been initialized. (This could happen if we're + // being called from rustpkg, for example). If the arguments change, then + // that's just kinda unfortunate. + static bool Initialized = false; + if (Initialized) + return; + Initialized = true; + cl::ParseCommandLineOptions(Argc, Argv); +} + +enum class LLVMRustFileType { + AssemblyFile, + ObjectFile, +}; + +#if LLVM_VERSION_GE(10, 0) +static CodeGenFileType fromRust(LLVMRustFileType Type) { + switch (Type) { + case LLVMRustFileType::AssemblyFile: + return CGFT_AssemblyFile; + case LLVMRustFileType::ObjectFile: + return CGFT_ObjectFile; + default: + report_fatal_error("Bad FileType."); + } +} +#else +static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) { + switch (Type) { + case LLVMRustFileType::AssemblyFile: + return TargetMachine::CGFT_AssemblyFile; + case LLVMRustFileType::ObjectFile: + return TargetMachine::CGFT_ObjectFile; + default: + report_fatal_error("Bad FileType."); + } +} +#endif + +extern "C" LLVMRustResult +LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, + LLVMModuleRef M, const char *Path, + LLVMRustFileType RustFileType) { + llvm::legacy::PassManager *PM = unwrap<llvm::legacy::PassManager>(PMR); + auto FileType = fromRust(RustFileType); + + std::string ErrorInfo; + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + ErrorInfo = EC.message(); + if (ErrorInfo != "") { + LLVMRustSetLastError(ErrorInfo.c_str()); + return LLVMRustResult::Failure; + } + + buffer_ostream BOS(OS); + unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false); + PM->run(*unwrap(M)); + + // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output + // stream (OS), so the only real safe place to delete this is here? Don't we + // wish this was written in Rust? + LLVMDisposePassManager(PMR); + return LLVMRustResult::Success; +} + +extern "C" typedef void (*LLVMRustSelfProfileBeforePassCallback)(void*, // LlvmSelfProfiler + const char*, // pass name + const char*); // IR name +extern "C" typedef void (*LLVMRustSelfProfileAfterPassCallback)(void*); // LlvmSelfProfiler + +#if LLVM_VERSION_GE(9, 0) + +std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) { + if (any_isa<const Module *>(WrappedIr)) + return any_cast<const Module *>(WrappedIr)->getName().str(); + if (any_isa<const Function *>(WrappedIr)) + return any_cast<const Function *>(WrappedIr)->getName().str(); + if (any_isa<const Loop *>(WrappedIr)) + return any_cast<const Loop *>(WrappedIr)->getName().str(); + if (any_isa<const LazyCallGraph::SCC *>(WrappedIr)) + return any_cast<const LazyCallGraph::SCC *>(WrappedIr)->getName(); + return "<UNKNOWN>"; +} + + +void LLVMSelfProfileInitializeCallbacks( + PassInstrumentationCallbacks& PIC, void* LlvmSelfProfiler, + LLVMRustSelfProfileBeforePassCallback BeforePassCallback, + LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { + 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); + }); + + PIC.registerBeforeAnalysisCallback([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()); + }); + + PIC.registerAfterAnalysisCallback( + [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) { + AfterPassCallback(LlvmSelfProfiler); + }); +} +#endif + +enum class LLVMRustOptStage { + PreLinkNoLTO, + PreLinkThinLTO, + PreLinkFatLTO, + ThinLTO, + FatLTO, +}; + +struct LLVMRustSanitizerOptions { + bool SanitizeAddress; + bool SanitizeAddressRecover; + bool SanitizeMemory; + bool SanitizeMemoryRecover; + int SanitizeMemoryTrackOrigins; + bool SanitizeThread; +}; + +extern "C" void +LLVMRustOptimizeWithNewPassManager( + LLVMModuleRef ModuleRef, + LLVMTargetMachineRef TMRef, + LLVMRustPassBuilderOptLevel OptLevelRust, + LLVMRustOptStage OptStage, + bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers, + bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, + bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, + LLVMRustSanitizerOptions *SanitizerOptions, + const char *PGOGenPath, const char *PGOUsePath, + void* LlvmSelfProfiler, + LLVMRustSelfProfileBeforePassCallback BeforePassCallback, + LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { +#if LLVM_VERSION_GE(9, 0) + Module *TheModule = unwrap(ModuleRef); + TargetMachine *TM = unwrap(TMRef); + PassBuilder::OptimizationLevel OptLevel = fromRust(OptLevelRust); + + // FIXME: MergeFunctions is not supported by NewPM yet. + (void) MergeFunctions; + + PipelineTuningOptions PTO; + PTO.LoopUnrolling = UnrollLoops; + PTO.LoopInterleaving = UnrollLoops; + PTO.LoopVectorization = LoopVectorize; + PTO.SLPVectorization = SLPVectorize; + + PassInstrumentationCallbacks PIC; + StandardInstrumentations SI; + SI.registerCallbacks(PIC); + + if (LlvmSelfProfiler){ + LLVMSelfProfileInitializeCallbacks(PIC,LlvmSelfProfiler,BeforePassCallback,AfterPassCallback); + } + + Optional<PGOOptions> PGOOpt; + if (PGOGenPath) { + assert(!PGOUsePath); + PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr); + } else if (PGOUsePath) { + assert(!PGOGenPath); + PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse); + } + + PassBuilder PB(TM, PTO, PGOOpt, &PIC); + + // FIXME: We may want to expose this as an option. + bool DebugPassManager = false; + LoopAnalysisManager LAM(DebugPassManager); + FunctionAnalysisManager FAM(DebugPassManager); + CGSCCAnalysisManager CGAM(DebugPassManager); + ModuleAnalysisManager MAM(DebugPassManager); + + FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); }); + + Triple TargetTriple(TheModule->getTargetTriple()); + std::unique_ptr<TargetLibraryInfoImpl> TLII(new TargetLibraryInfoImpl(TargetTriple)); + if (DisableSimplifyLibCalls) + TLII->disableAllFunctions(); + FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); + + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + // We manually collect pipeline callbacks so we can apply them at O0, where the + // PassBuilder does not create a pipeline. + std::vector<std::function<void(ModulePassManager &)>> PipelineStartEPCallbacks; +#if LLVM_VERSION_GE(11, 0) + std::vector<std::function<void(ModulePassManager &, PassBuilder::OptimizationLevel)>> + OptimizerLastEPCallbacks; +#else + std::vector<std::function<void(FunctionPassManager &, PassBuilder::OptimizationLevel)>> + OptimizerLastEPCallbacks; +#endif + + if (VerifyIR) { + PipelineStartEPCallbacks.push_back([VerifyIR](ModulePassManager &MPM) { + MPM.addPass(VerifierPass()); + }); + } + + if (SanitizerOptions) { + if (SanitizerOptions->SanitizeMemory) { + MemorySanitizerOptions Options( + SanitizerOptions->SanitizeMemoryTrackOrigins, + SanitizerOptions->SanitizeMemoryRecover, + /*CompileKernel=*/false); +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [Options](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(MemorySanitizerPass(Options)); + MPM.addPass(createModuleToFunctionPassAdaptor(MemorySanitizerPass(Options))); + } + ); +#else +#if LLVM_VERSION_GE(10, 0) + PipelineStartEPCallbacks.push_back([Options](ModulePassManager &MPM) { + MPM.addPass(MemorySanitizerPass(Options)); + }); +#endif + OptimizerLastEPCallbacks.push_back( + [Options](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(MemorySanitizerPass(Options)); + } + ); +#endif + } + + if (SanitizerOptions->SanitizeThread) { +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(ThreadSanitizerPass()); + MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); + } + ); +#else +#if LLVM_VERSION_GE(10, 0) + PipelineStartEPCallbacks.push_back([](ModulePassManager &MPM) { + MPM.addPass(ThreadSanitizerPass()); + }); +#endif + OptimizerLastEPCallbacks.push_back( + [](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(ThreadSanitizerPass()); + } + ); +#endif + } + + if (SanitizerOptions->SanitizeAddress) { +#if LLVM_VERSION_GE(11, 0) + OptimizerLastEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>()); + MPM.addPass(ModuleAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); + MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, + /*UseAfterScope=*/true))); + } + ); +#else + PipelineStartEPCallbacks.push_back([&](ModulePassManager &MPM) { + MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>()); + }); + OptimizerLastEPCallbacks.push_back( + [SanitizerOptions](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(AddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, + /*UseAfterScope=*/true)); + } + ); + PipelineStartEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM) { + MPM.addPass(ModuleAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); + } + ); +#endif + } + } + + ModulePassManager MPM(DebugPassManager); + if (!NoPrepopulatePasses) { + if (OptLevel == PassBuilder::OptimizationLevel::O0) { + for (const auto &C : PipelineStartEPCallbacks) + C(MPM); + +#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 LLVM_VERSION_GE(10, 0) + if (PGOOpt) { + PB.addPGOInstrPassesForO0( + MPM, DebugPassManager, PGOOpt->Action == PGOOptions::IRInstr, + /*IsCS=*/false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile); + } +#endif + } else { + for (const auto &C : PipelineStartEPCallbacks) + PB.registerPipelineStartEPCallback(C); + if (OptStage != LLVMRustOptStage::PreLinkThinLTO) { + for (const auto &C : OptimizerLastEPCallbacks) + PB.registerOptimizerLastEPCallback(C); + } + + switch (OptStage) { + case LLVMRustOptStage::PreLinkNoLTO: + MPM = PB.buildPerModuleDefaultPipeline(OptLevel, DebugPassManager); + break; + case LLVMRustOptStage::PreLinkThinLTO: + MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); +#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: + MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); + break; + case LLVMRustOptStage::ThinLTO: + // FIXME: Does it make sense to pass the ModuleSummaryIndex? + // It only seems to be needed for C++ specific optimizations. + MPM = PB.buildThinLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); + break; + case LLVMRustOptStage::FatLTO: + MPM = PB.buildLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); + break; + } + } + } + + if (UseThinLTOBuffers) { + MPM.addPass(CanonicalizeAliasesPass()); + MPM.addPass(NameAnonGlobalPass()); + } + + // Upgrade all calls to old intrinsics first. + for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;) + UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove + + MPM.run(*TheModule, MAM); +#else + // The new pass manager has been available for a long time, + // but we don't bother supporting it on old LLVM versions. + report_fatal_error("New pass manager only supported since LLVM 9"); +#endif +} + +// Callback to demangle function name +// Parameters: +// * name to be demangled +// * name len +// * output buffer +// * output buffer len +// Returns len of demangled string, or 0 if demangle failed. +typedef size_t (*DemangleFn)(const char*, size_t, char*, size_t); + + +namespace { + +class RustAssemblyAnnotationWriter : public AssemblyAnnotationWriter { + DemangleFn Demangle; + std::vector<char> Buf; + +public: + RustAssemblyAnnotationWriter(DemangleFn Demangle) : Demangle(Demangle) {} + + // Return empty string if demangle failed + // or if name does not need to be demangled + StringRef CallDemangle(StringRef name) { + if (!Demangle) { + return StringRef(); + } + + if (Buf.size() < name.size() * 2) { + // Semangled name usually shorter than mangled, + // but allocate twice as much memory just in case + Buf.resize(name.size() * 2); + } + + auto R = Demangle(name.data(), name.size(), Buf.data(), Buf.size()); + if (!R) { + // Demangle failed. + return StringRef(); + } + + auto Demangled = StringRef(Buf.data(), R); + if (Demangled == name) { + // Do not print anything if demangled name is equal to mangled. + return StringRef(); + } + + return Demangled; + } + + void emitFunctionAnnot(const Function *F, + formatted_raw_ostream &OS) override { + StringRef Demangled = CallDemangle(F->getName()); + if (Demangled.empty()) { + return; + } + + OS << "; " << Demangled << "\n"; + } + + void emitInstructionAnnot(const Instruction *I, + formatted_raw_ostream &OS) override { + const char *Name; + const Value *Value; + if (const CallInst *CI = dyn_cast<CallInst>(I)) { + Name = "call"; + Value = CI->getCalledOperand(); + } else if (const InvokeInst* II = dyn_cast<InvokeInst>(I)) { + Name = "invoke"; + Value = II->getCalledOperand(); + } else { + // Could demangle more operations, e. g. + // `store %place, @function`. + return; + } + + if (!Value->hasName()) { + return; + } + + StringRef Demangled = CallDemangle(Value->getName()); + if (Demangled.empty()) { + return; + } + + OS << "; " << Name << " " << Demangled << "\n"; + } +}; + +} // namespace + +extern "C" LLVMRustResult +LLVMRustPrintModule(LLVMModuleRef M, const char *Path, DemangleFn Demangle) { + std::string ErrorInfo; + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + ErrorInfo = EC.message(); + if (ErrorInfo != "") { + LLVMRustSetLastError(ErrorInfo.c_str()); + return LLVMRustResult::Failure; + } + + RustAssemblyAnnotationWriter AAW(Demangle); + formatted_raw_ostream FOS(OS); + unwrap(M)->print(FOS, &AAW); + + return LLVMRustResult::Success; +} + +extern "C" void LLVMRustPrintPasses() { + LLVMInitializePasses(); + struct MyListener : PassRegistrationListener { + void passEnumerate(const PassInfo *Info) { + StringRef PassArg = Info->getPassArgument(); + StringRef PassName = Info->getPassName(); + if (!PassArg.empty()) { + // These unsigned->signed casts could theoretically overflow, but + // realistically never will (and even if, the result is implementation + // defined rather plain UB). + printf("%15.*s - %.*s\n", (int)PassArg.size(), PassArg.data(), + (int)PassName.size(), PassName.data()); + } + } + } Listener; + + PassRegistry *PR = PassRegistry::getPassRegistry(); + PR->enumerateWith(&Listener); +} + +extern "C" void LLVMRustAddAlwaysInlinePass(LLVMPassManagerBuilderRef PMBR, + bool AddLifetimes) { + unwrap(PMBR)->Inliner = llvm::createAlwaysInlinerLegacyPass(AddLifetimes); +} + +extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols, + size_t Len) { + llvm::legacy::PassManager passes; + + auto PreserveFunctions = [=](const GlobalValue &GV) { + for (size_t I = 0; I < Len; I++) { + if (GV.getName() == Symbols[I]) { + return true; + } + } + return false; + }; + + passes.add(llvm::createInternalizePass(PreserveFunctions)); + + passes.run(*unwrap(M)); +} + +extern "C" void LLVMRustMarkAllFunctionsNounwind(LLVMModuleRef M) { + for (Module::iterator GV = unwrap(M)->begin(), E = unwrap(M)->end(); GV != E; + ++GV) { + GV->setDoesNotThrow(); + Function *F = dyn_cast<Function>(GV); + if (F == nullptr) + continue; + + for (Function::iterator B = F->begin(), BE = F->end(); B != BE; ++B) { + for (BasicBlock::iterator I = B->begin(), IE = B->end(); I != IE; ++I) { + if (isa<InvokeInst>(I)) { + InvokeInst *CI = cast<InvokeInst>(I); + CI->setDoesNotThrow(); + } + } + } + } +} + +extern "C" void +LLVMRustSetDataLayoutFromTargetMachine(LLVMModuleRef Module, + LLVMTargetMachineRef TMR) { + TargetMachine *Target = unwrap(TMR); + unwrap(Module)->setDataLayout(Target->createDataLayout()); +} + +extern "C" void LLVMRustSetModulePICLevel(LLVMModuleRef M) { + unwrap(M)->setPICLevel(PICLevel::Level::BigPIC); +} + +extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) { + unwrap(M)->setPIELevel(PIELevel::Level::Large); +} + +// Here you'll find an implementation of ThinLTO as used by the Rust compiler +// right now. This ThinLTO support is only enabled on "recent ish" versions of +// LLVM, and otherwise it's just blanket rejected from other compilers. +// +// Most of this implementation is straight copied from LLVM. At the time of +// this writing it wasn't *quite* suitable to reuse more code from upstream +// for our purposes, but we should strive to upstream this support once it's +// ready to go! I figure we may want a bit of testing locally first before +// sending this upstream to LLVM. I hear though they're quite eager to receive +// feedback like this! +// +// If you're reading this code and wondering "what in the world" or you're +// working "good lord by LLVM upgrade is *still* failing due to these bindings" +// then fear not! (ok maybe fear a little). All code here is mostly based +// on `lib/LTO/ThinLTOCodeGenerator.cpp` in LLVM. +// +// You'll find that the general layout here roughly corresponds to the `run` +// method in that file as well as `ProcessThinLTOModule`. Functions are +// specifically commented below as well, but if you're updating this code +// or otherwise trying to understand it, the LLVM source will be useful in +// interpreting the mysteries within. +// +// Otherwise I'll apologize in advance, it probably requires a relatively +// significant investment on your part to "truly understand" what's going on +// here. Not saying I do myself, but it took me awhile staring at LLVM's source +// and various online resources about ThinLTO to make heads or tails of all +// this. + +// This is a shared data structure which *must* be threadsafe to share +// read-only amongst threads. This also corresponds basically to the arguments +// of the `ProcessThinLTOModule` function in the LLVM source. +struct LLVMRustThinLTOData { + // The combined index that is the global analysis over all modules we're + // performing ThinLTO for. This is mostly managed by LLVM. + ModuleSummaryIndex Index; + + // All modules we may look at, stored as in-memory serialized versions. This + // is later used when inlining to ensure we can extract any module to inline + // from. + StringMap<MemoryBufferRef> ModuleMap; + + // A set that we manage of everything we *don't* want internalized. Note that + // this includes all transitive references right now as well, but it may not + // always! + DenseSet<GlobalValue::GUID> GUIDPreservedSymbols; + + // Not 100% sure what these are, but they impact what's internalized and + // what's inlined across modules, I believe. + StringMap<FunctionImporter::ImportMapTy> ImportLists; + StringMap<FunctionImporter::ExportSetTy> ExportLists; + StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries; + + LLVMRustThinLTOData() : Index(/* HaveGVs = */ false) {} +}; + +// Just an argument to the `LLVMRustCreateThinLTOData` function below. +struct LLVMRustThinLTOModule { + const char *identifier; + const char *data; + size_t len; +}; + +// This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`, not sure what it +// does. +static const GlobalValueSummary * +getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) { + auto StrongDefForLinker = llvm::find_if( + GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) { + auto Linkage = Summary->linkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage) && + !GlobalValue::isWeakForLinker(Linkage); + }); + if (StrongDefForLinker != GVSummaryList.end()) + return StrongDefForLinker->get(); + + auto FirstDefForLinker = llvm::find_if( + GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) { + auto Linkage = Summary->linkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage); + }); + if (FirstDefForLinker == GVSummaryList.end()) + return nullptr; + return FirstDefForLinker->get(); +} + +// The main entry point for creating the global ThinLTO analysis. The structure +// here is basically the same as before threads are spawned in the `run` +// function of `lib/LTO/ThinLTOCodeGenerator.cpp`. +extern "C" LLVMRustThinLTOData* +LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, + int num_modules, + const char **preserved_symbols, + int num_symbols) { +#if LLVM_VERSION_GE(10, 0) + auto Ret = std::make_unique<LLVMRustThinLTOData>(); +#else + auto Ret = llvm::make_unique<LLVMRustThinLTOData>(); +#endif + + // Load each module's summary and merge it into one combined index + for (int i = 0; i < num_modules; i++) { + auto module = &modules[i]; + StringRef buffer(module->data, module->len); + MemoryBufferRef mem_buffer(buffer, module->identifier); + + Ret->ModuleMap[module->identifier] = mem_buffer; + + if (Error Err = readModuleSummaryIndex(mem_buffer, Ret->Index, i)) { + LLVMRustSetLastError(toString(std::move(Err)).c_str()); + return nullptr; + } + } + + // Collect for each module the list of function it defines (GUID -> Summary) + Ret->Index.collectDefinedGVSummariesPerModule(Ret->ModuleToDefinedGVSummaries); + + // Convert the preserved symbols set from string to GUID, this is then needed + // for internalization. + for (int i = 0; i < num_symbols; i++) { + auto GUID = GlobalValue::getGUID(preserved_symbols[i]); + Ret->GUIDPreservedSymbols.insert(GUID); + } + + // Collect the import/export lists for all modules from the call-graph in the + // combined index + // + // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` + auto deadIsPrevailing = [&](GlobalValue::GUID G) { + return PrevailingType::Unknown; + }; + // We don't have a complete picture in our use of ThinLTO, just our immediate + // crate, so we need `ImportEnabled = false` to limit internalization. + // Otherwise, we sometimes lose `static` values -- see #60184. + computeDeadSymbolsWithConstProp(Ret->Index, Ret->GUIDPreservedSymbols, + deadIsPrevailing, /* ImportEnabled = */ false); + ComputeCrossModuleImport( + Ret->Index, + Ret->ModuleToDefinedGVSummaries, + Ret->ImportLists, + Ret->ExportLists + ); + + // Resolve LinkOnce/Weak symbols, this has to be computed early be cause it + // impacts the caching. + // + // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` with some of this + // being lifted from `lib/LTO/LTO.cpp` as well + StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR; + DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy; + for (auto &I : Ret->Index) { + if (I.second.SummaryList.size() > 1) + PrevailingCopy[I.first] = getFirstDefinitionForLinker(I.second.SummaryList); + } + auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { + const auto &Prevailing = PrevailingCopy.find(GUID); + if (Prevailing == PrevailingCopy.end()) + return true; + return Prevailing->second == S; + }; + auto recordNewLinkage = [&](StringRef ModuleIdentifier, + GlobalValue::GUID GUID, + GlobalValue::LinkageTypes NewLinkage) { + ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; + }; +#if LLVM_VERSION_GE(9, 0) + thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage, + Ret->GUIDPreservedSymbols); +#else + thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage); +#endif + + // Here we calculate an `ExportedGUIDs` set for use in the `isExported` + // callback below. This callback below will dictate the linkage for all + // summaries in the index, and we basically just only want to ensure that dead + // symbols are internalized. Otherwise everything that's already external + // linkage will stay as external, and internal will stay as internal. + std::set<GlobalValue::GUID> ExportedGUIDs; + for (auto &List : Ret->Index) { + for (auto &GVS: List.second.SummaryList) { + if (GlobalValue::isLocalLinkage(GVS->linkage())) + continue; + auto GUID = GVS->getOriginalName(); + if (GVS->flags().Live) + ExportedGUIDs.insert(GUID); + } + } +#if LLVM_VERSION_GE(10, 0) + auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) { + const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); + return (ExportList != Ret->ExportLists.end() && + ExportList->second.count(VI)) || + ExportedGUIDs.count(VI.getGUID()); + }; + thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported, isPrevailing); +#else + auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { + const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); + return (ExportList != Ret->ExportLists.end() && + ExportList->second.count(GUID)) || + ExportedGUIDs.count(GUID); + }; + thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported); +#endif + + return Ret.release(); +} + +extern "C" void +LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) { + delete Data; +} + +// Below are the various passes that happen *per module* when doing ThinLTO. +// +// In other words, these are the functions that are all run concurrently +// with one another, one per module. The passes here correspond to the analysis +// passes in `lib/LTO/ThinLTOCodeGenerator.cpp`, currently found in the +// `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 + // conservatively do this for -fpic. + bool ClearDSOLocalOnDeclarations = + TM.getTargetTriple().isOSBinFormatELF() && + TM.getRelocationModel() != Reloc::Static && + Mod.getPIELevel() == PIELevel::Default; + return ClearDSOLocalOnDeclarations; +} +#endif + +extern "C" bool +LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M, + LLVMTargetMachineRef TM) { + 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"); + return false; + } + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); + thinLTOResolvePrevailingInModule(Mod, DefinedGlobals); + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); + thinLTOInternalizeModule(Mod, DefinedGlobals); + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, + LLVMTargetMachineRef TM) { + Module &Mod = *unwrap(M); + TargetMachine &Target = *unwrap(TM); + + const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier()); + auto Loader = [&](StringRef Identifier) { + const auto &Memory = Data->ModuleMap.lookup(Identifier); + auto &Context = Mod.getContext(); + auto MOrErr = getLazyBitcodeModule(Memory, Context, true, true); + + if (!MOrErr) + return MOrErr; + + // The rest of this closure is a workaround for + // https://bugs.llvm.org/show_bug.cgi?id=38184 where during ThinLTO imports + // we accidentally import wasm custom sections into different modules, + // duplicating them by in the final output artifact. + // + // The issue is worked around here by manually removing the + // `wasm.custom_sections` named metadata node from any imported module. This + // we know isn't used by any optimization pass so there's no need for it to + // be imported. + // + // Note that the metadata is currently lazily loaded, so we materialize it + // here before looking up if there's metadata inside. The `FunctionImporter` + // will immediately materialize metadata anyway after an import, so this + // shouldn't be a perf hit. + if (Error Err = (*MOrErr)->materializeMetadata()) { + Expected<std::unique_ptr<Module>> Ret(std::move(Err)); + return Ret; + } + + auto *WasmCustomSections = (*MOrErr)->getNamedMetadata("wasm.custom_sections"); + if (WasmCustomSections) + WasmCustomSections->eraseFromParent(); + + 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()); + return false; + } + return true; +} + +extern "C" typedef void (*LLVMRustModuleNameCallback)(void*, // payload + const char*, // importing module name + const char*); // imported module name + +// Calls `module_name_callback` for each module import done by ThinLTO. +// The callback is provided with regular null-terminated C strings. +extern "C" void +LLVMRustGetThinLTOModuleImports(const LLVMRustThinLTOData *data, + LLVMRustModuleNameCallback module_name_callback, + void* callback_payload) { + for (const auto& importing_module : data->ImportLists) { + const std::string importing_module_id = importing_module.getKey().str(); + const auto& imports = importing_module.getValue(); + for (const auto& imported_module : imports) { + const std::string imported_module_id = imported_module.getKey().str(); + module_name_callback(callback_payload, + importing_module_id.c_str(), + imported_module_id.c_str()); + } + } +} + +// This struct and various functions are sort of a hack right now, but the +// problem is that we've got in-memory LLVM modules after we generate and +// optimize all codegen-units for one compilation in rustc. To be compatible +// with the LTO support above we need to serialize the modules plus their +// ThinLTO summary into memory. +// +// This structure is basically an owned version of a serialize module, with +// a ThinLTO summary attached. +struct LLVMRustThinLTOBuffer { + std::string data; +}; + +extern "C" LLVMRustThinLTOBuffer* +LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { +#if LLVM_VERSION_GE(10, 0) + auto Ret = std::make_unique<LLVMRustThinLTOBuffer>(); +#else + auto Ret = llvm::make_unique<LLVMRustThinLTOBuffer>(); +#endif + { + raw_string_ostream OS(Ret->data); + { + legacy::PassManager PM; + PM.add(createWriteThinLTOBitcodePass(OS)); + PM.run(*unwrap(M)); + } + } + return Ret.release(); +} + +extern "C" void +LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) { + delete Buffer; +} + +extern "C" const void* +LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->data.data(); +} + +extern "C" size_t +LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->data.length(); +} + +// This is what we used to parse upstream bitcode for actual ThinLTO +// processing. We'll call this once per module optimized through ThinLTO, and +// it'll be called concurrently on many threads. +extern "C" LLVMModuleRef +LLVMRustParseBitcodeForLTO(LLVMContextRef Context, + const char *data, + size_t len, + const char *identifier) { + StringRef Data(data, len); + MemoryBufferRef Buffer(Data, identifier); + unwrap(Context)->enableDebugTypeODRUniquing(); + Expected<std::unique_ptr<Module>> SrcOrError = + parseBitcodeFile(Buffer, *unwrap(Context)); + if (!SrcOrError) { + LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str()); + return nullptr; + } + return wrap(std::move(*SrcOrError).release()); +} + +// Find the bitcode section in the object file data and return it as a slice. +// Fail if the bitcode section is present but empty. +// +// On success, the return value is the pointer to the start of the slice and +// `out_len` is filled with the (non-zero) length. On failure, the return value +// is `nullptr` and `out_len` is set to zero. +extern "C" const char* +LLVMRustGetBitcodeSliceFromObjectData(const char *data, + size_t len, + size_t *out_len) { + *out_len = 0; + + StringRef Data(data, len); + MemoryBufferRef Buffer(Data, ""); // The id is unused. + + Expected<MemoryBufferRef> BitcodeOrError = + object::IRObjectFile::findBitcodeInMemBuffer(Buffer); + if (!BitcodeOrError) { + LLVMRustSetLastError(toString(BitcodeOrError.takeError()).c_str()); + return nullptr; + } + + *out_len = BitcodeOrError->getBufferSize(); + return BitcodeOrError->getBufferStart(); +} + +// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See +// the comment in `back/lto.rs` for why this exists. +extern "C" void +LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod, + DICompileUnit **A, + DICompileUnit **B) { + Module *M = unwrap(Mod); + DICompileUnit **Cur = A; + DICompileUnit **Next = B; + for (DICompileUnit *CU : M->debug_compile_units()) { + *Cur = CU; + Cur = Next; + Next = nullptr; + if (Cur == nullptr) + break; + } +} + +// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See +// the comment in `back/lto.rs` for why this exists. +extern "C" void +LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) { + Module *M = unwrap(Mod); + + // If the original source module didn't have a `DICompileUnit` then try to + // merge all the existing compile units. If there aren't actually any though + // then there's not much for us to do so return. + if (Unit == nullptr) { + for (DICompileUnit *CU : M->debug_compile_units()) { + Unit = CU; + break; + } + if (Unit == nullptr) + return; + } + + // Use LLVM's built-in `DebugInfoFinder` to find a bunch of debuginfo and + // process it recursively. Note that we used to specifically iterate over + // instructions to ensure we feed everything into it, but `processModule` + // started doing this the same way in LLVM 7 (commit d769eb36ab2b8). + DebugInfoFinder Finder; + Finder.processModule(*M); + + // After we've found all our debuginfo, rewrite all subprograms to point to + // the same `DICompileUnit`. + for (auto &F : Finder.subprograms()) { + F->replaceUnit(Unit); + } + + // Erase any other references to other `DICompileUnit` instances, the verifier + // will later ensure that we don't actually have any other stale references to + // worry about. + auto *MD = M->getNamedMetadata("llvm.dbg.cu"); + MD->clearOperands(); + MD->addOperand(Unit); +} diff --git a/compiler/rustc_llvm/llvm-wrapper/README b/compiler/rustc_llvm/llvm-wrapper/README new file mode 100644 index 00000000000..e1c6dd07d2b --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/README @@ -0,0 +1,16 @@ +This directory currently contains some LLVM support code. This will generally +be sent upstream to LLVM in time; for now it lives here. + +NOTE: the LLVM C++ ABI is subject to between-version breakage and must *never* +be exposed to Rust. To allow for easy auditing of that, all Rust-exposed types +must be typedef-ed as "LLVMXyz", or "LLVMRustXyz" if they were defined here. + +Functions that return a failure status and leave the error in +the LLVM last error should return an LLVMRustResult rather than an +int or anything to avoid confusion. + +When translating enums, add a single `Other` variant as the first +one to allow for new variants to be added. It should abort when used +as an input. + +All other types must not be typedef-ed as such. diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp new file mode 100644 index 00000000000..e85a9b76380 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -0,0 +1,1721 @@ +#include "LLVMWrapper.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/Support/Signals.h" +#include "llvm/ADT/Optional.h" + +#include <iostream> + +//===----------------------------------------------------------------------=== +// +// This file defines alternate interfaces to core functions that are more +// readily callable by Rust's FFI. +// +//===----------------------------------------------------------------------=== + +using namespace llvm; +using namespace llvm::sys; +using namespace llvm::object; + +// LLVMAtomicOrdering is already an enum - don't create another +// one. +static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) { + switch (Ordering) { + case LLVMAtomicOrderingNotAtomic: + return AtomicOrdering::NotAtomic; + case LLVMAtomicOrderingUnordered: + return AtomicOrdering::Unordered; + case LLVMAtomicOrderingMonotonic: + return AtomicOrdering::Monotonic; + case LLVMAtomicOrderingAcquire: + return AtomicOrdering::Acquire; + case LLVMAtomicOrderingRelease: + return AtomicOrdering::Release; + case LLVMAtomicOrderingAcquireRelease: + return AtomicOrdering::AcquireRelease; + case LLVMAtomicOrderingSequentiallyConsistent: + return AtomicOrdering::SequentiallyConsistent; + } + + report_fatal_error("Invalid LLVMAtomicOrdering value!"); +} + +static LLVM_THREAD_LOCAL char *LastError; + +// Custom error handler for fatal LLVM errors. +// +// Notably it exits the process with code 101, unlike LLVM's default of 1. +static void FatalErrorHandler(void *UserData, + const std::string& Reason, + bool GenCrashDiag) { + // Do the same thing that the default error handler does. + std::cerr << "LLVM ERROR: " << Reason << std::endl; + + // Since this error handler exits the process, we have to run any cleanup that + // LLVM would run after handling the error. This might change with an LLVM + // upgrade. + sys::RunInterruptHandlers(); + + exit(101); +} + +extern "C" void LLVMRustInstallFatalErrorHandler() { + install_fatal_error_handler(FatalErrorHandler); +} + +extern "C" LLVMMemoryBufferRef +LLVMRustCreateMemoryBufferWithContentsOfFile(const char *Path) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr = + MemoryBuffer::getFile(Path, -1, false); + if (!BufOr) { + LLVMRustSetLastError(BufOr.getError().message().c_str()); + return nullptr; + } + return wrap(BufOr.get().release()); +} + +extern "C" char *LLVMRustGetLastError(void) { + char *Ret = LastError; + LastError = nullptr; + return Ret; +} + +extern "C" unsigned int LLVMRustGetInstructionCount(LLVMModuleRef M) { + return unwrap(M)->getInstructionCount(); +} + +extern "C" void LLVMRustSetLastError(const char *Err) { + free((void *)LastError); + LastError = strdup(Err); +} + +extern "C" LLVMContextRef LLVMRustContextCreate(bool shouldDiscardNames) { + auto ctx = new LLVMContext(); + ctx->setDiscardValueNames(shouldDiscardNames); + return wrap(ctx); +} + +extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M, + const char *Triple) { + unwrap(M)->setTargetTriple(Triple::normalize(Triple)); +} + +extern "C" void LLVMRustPrintPassTimings() { + raw_fd_ostream OS(2, false); // stderr. + TimerGroup::printAll(OS); +} + +extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, + size_t NameLen) { + return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen))); +} + +extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M, + const char *Name, + size_t NameLen, + LLVMTypeRef FunctionTy) { + return wrap(unwrap(M) + ->getOrInsertFunction(StringRef(Name, NameLen), + unwrap<FunctionType>(FunctionTy)) +#if LLVM_VERSION_GE(9, 0) + .getCallee() +#endif + ); +} + +extern "C" LLVMValueRef +LLVMRustGetOrInsertGlobal(LLVMModuleRef M, const char *Name, size_t NameLen, LLVMTypeRef Ty) { + StringRef NameRef(Name, NameLen); + return wrap(unwrap(M)->getOrInsertGlobal(NameRef, unwrap(Ty))); +} + +extern "C" LLVMValueRef +LLVMRustInsertPrivateGlobal(LLVMModuleRef M, LLVMTypeRef Ty) { + return wrap(new GlobalVariable(*unwrap(M), + unwrap(Ty), + false, + GlobalValue::PrivateLinkage, + nullptr)); +} + +extern "C" LLVMTypeRef LLVMRustMetadataTypeInContext(LLVMContextRef C) { + return wrap(Type::getMetadataTy(*unwrap(C))); +} + +static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { + switch (Kind) { + case AlwaysInline: + return Attribute::AlwaysInline; + case ByVal: + return Attribute::ByVal; + case Cold: + return Attribute::Cold; + case InlineHint: + return Attribute::InlineHint; + case MinSize: + return Attribute::MinSize; + case Naked: + return Attribute::Naked; + case NoAlias: + return Attribute::NoAlias; + case NoCapture: + return Attribute::NoCapture; + case NoInline: + return Attribute::NoInline; + case NonNull: + return Attribute::NonNull; + case NoRedZone: + return Attribute::NoRedZone; + case NoReturn: + return Attribute::NoReturn; + case NoUnwind: + return Attribute::NoUnwind; + case OptimizeForSize: + return Attribute::OptimizeForSize; + case ReadOnly: + return Attribute::ReadOnly; + case SExt: + return Attribute::SExt; + case StructRet: + return Attribute::StructRet; + case UWTable: + return Attribute::UWTable; + case ZExt: + return Attribute::ZExt; + case InReg: + return Attribute::InReg; + case SanitizeThread: + return Attribute::SanitizeThread; + case SanitizeAddress: + return Attribute::SanitizeAddress; + case SanitizeMemory: + return Attribute::SanitizeMemory; + case NonLazyBind: + return Attribute::NonLazyBind; + case OptimizeNone: + return Attribute::OptimizeNone; + case ReturnsTwice: + return Attribute::ReturnsTwice; + case ReadNone: + return Attribute::ReadNone; + case InaccessibleMemOnly: + return Attribute::InaccessibleMemOnly; + } + report_fatal_error("bad AttributeKind"); +} + +extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index, + LLVMRustAttribute RustAttr) { + CallBase *Call = unwrap<CallBase>(Instr); + Attribute Attr = Attribute::get(Call->getContext(), fromRust(RustAttr)); + Call->addAttribute(Index, Attr); +} + +extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint32_t Bytes) { + CallBase *Call = unwrap<CallBase>(Instr); + AttrBuilder B; + B.addAlignmentAttr(Bytes); + Call->setAttributes(Call->getAttributes().addAttributes( + Call->getContext(), Index, B)); +} + +extern "C" void LLVMRustAddDereferenceableCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint64_t Bytes) { + CallBase *Call = unwrap<CallBase>(Instr); + AttrBuilder B; + B.addDereferenceableAttr(Bytes); + Call->setAttributes(Call->getAttributes().addAttributes( + Call->getContext(), Index, B)); +} + +extern "C" void LLVMRustAddDereferenceableOrNullCallSiteAttr(LLVMValueRef Instr, + unsigned Index, + uint64_t Bytes) { + CallBase *Call = unwrap<CallBase>(Instr); + AttrBuilder B; + B.addDereferenceableOrNullAttr(Bytes); + Call->setAttributes(Call->getAttributes().addAttributes( + Call->getContext(), Index, B)); +} + +extern "C" void LLVMRustAddByValCallSiteAttr(LLVMValueRef Instr, unsigned Index, + LLVMTypeRef Ty) { + CallBase *Call = unwrap<CallBase>(Instr); +#if LLVM_VERSION_GE(9, 0) + Attribute Attr = Attribute::getWithByValType(Call->getContext(), unwrap(Ty)); +#else + Attribute Attr = Attribute::get(Call->getContext(), Attribute::ByVal); +#endif + Call->addAttribute(Index, Attr); +} + +extern "C" void LLVMRustAddFunctionAttribute(LLVMValueRef Fn, unsigned Index, + LLVMRustAttribute RustAttr) { + Function *A = unwrap<Function>(Fn); + Attribute Attr = Attribute::get(A->getContext(), fromRust(RustAttr)); + AttrBuilder B(Attr); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddAlignmentAttr(LLVMValueRef Fn, + unsigned Index, + uint32_t Bytes) { + Function *A = unwrap<Function>(Fn); + AttrBuilder B; + B.addAlignmentAttr(Bytes); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddDereferenceableAttr(LLVMValueRef Fn, unsigned Index, + uint64_t Bytes) { + Function *A = unwrap<Function>(Fn); + AttrBuilder B; + B.addDereferenceableAttr(Bytes); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddDereferenceableOrNullAttr(LLVMValueRef Fn, + unsigned Index, + uint64_t Bytes) { + Function *A = unwrap<Function>(Fn); + AttrBuilder B; + B.addDereferenceableOrNullAttr(Bytes); + A->addAttributes(Index, B); +} + +extern "C" void LLVMRustAddByValAttr(LLVMValueRef Fn, unsigned Index, + LLVMTypeRef Ty) { + Function *F = unwrap<Function>(Fn); +#if LLVM_VERSION_GE(9, 0) + Attribute Attr = Attribute::getWithByValType(F->getContext(), unwrap(Ty)); +#else + Attribute Attr = Attribute::get(F->getContext(), Attribute::ByVal); +#endif + F->addAttribute(Index, Attr); +} + +extern "C" void LLVMRustAddFunctionAttrStringValue(LLVMValueRef Fn, + unsigned Index, + const char *Name, + const char *Value) { + Function *F = unwrap<Function>(Fn); + AttrBuilder B; + B.addAttribute(Name, Value); + F->addAttributes(Index, B); +} + +extern "C" void LLVMRustRemoveFunctionAttributes(LLVMValueRef Fn, + unsigned Index, + LLVMRustAttribute RustAttr) { + Function *F = unwrap<Function>(Fn); + Attribute Attr = Attribute::get(F->getContext(), fromRust(RustAttr)); + AttrBuilder B(Attr); + auto PAL = F->getAttributes(); + auto PALNew = PAL.removeAttributes(F->getContext(), Index, B); + F->setAttributes(PALNew); +} + +// enable fpmath flag UnsafeAlgebra +extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef V) { + if (auto I = dyn_cast<Instruction>(unwrap<Value>(V))) { + I->setFast(true); + } +} + +extern "C" LLVMValueRef +LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMValueRef Source, const char *Name, + LLVMAtomicOrdering Order) { + Value *Ptr = unwrap(Source); + Type *Ty = Ptr->getType()->getPointerElementType(); + LoadInst *LI = unwrap(B)->CreateLoad(Ty, Ptr, Name); + LI->setAtomic(fromRust(Order)); + return wrap(LI); +} + +extern "C" LLVMValueRef LLVMRustBuildAtomicStore(LLVMBuilderRef B, + LLVMValueRef V, + LLVMValueRef Target, + LLVMAtomicOrdering Order) { + StoreInst *SI = unwrap(B)->CreateStore(unwrap(V), unwrap(Target)); + SI->setAtomic(fromRust(Order)); + return wrap(SI); +} + +// FIXME: Use the C-API LLVMBuildAtomicCmpXchg and LLVMSetWeak +// once we raise our minimum support to LLVM 10. +extern "C" LLVMValueRef +LLVMRustBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Target, + LLVMValueRef Old, LLVMValueRef Source, + LLVMAtomicOrdering Order, + LLVMAtomicOrdering FailureOrder, LLVMBool Weak) { + AtomicCmpXchgInst *ACXI = unwrap(B)->CreateAtomicCmpXchg( + unwrap(Target), unwrap(Old), unwrap(Source), fromRust(Order), + fromRust(FailureOrder)); + ACXI->setWeak(Weak); + return wrap(ACXI); +} + +enum class LLVMRustSynchronizationScope { + SingleThread, + CrossThread, +}; + +static SyncScope::ID fromRust(LLVMRustSynchronizationScope Scope) { + switch (Scope) { + case LLVMRustSynchronizationScope::SingleThread: + return SyncScope::SingleThread; + case LLVMRustSynchronizationScope::CrossThread: + return SyncScope::System; + default: + report_fatal_error("bad SynchronizationScope."); + } +} + +extern "C" LLVMValueRef +LLVMRustBuildAtomicFence(LLVMBuilderRef B, LLVMAtomicOrdering Order, + LLVMRustSynchronizationScope Scope) { + return wrap(unwrap(B)->CreateFence(fromRust(Order), fromRust(Scope))); +} + +enum class LLVMRustAsmDialect { + Att, + Intel, +}; + +static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) { + switch (Dialect) { + case LLVMRustAsmDialect::Att: + return InlineAsm::AD_ATT; + case LLVMRustAsmDialect::Intel: + return InlineAsm::AD_Intel; + default: + report_fatal_error("bad AsmDialect."); + } +} + +extern "C" LLVMValueRef +LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, + char *Constraints, size_t ConstraintsLen, + LLVMBool HasSideEffects, LLVMBool IsAlignStack, + LLVMRustAsmDialect Dialect) { + return wrap(InlineAsm::get(unwrap<FunctionType>(Ty), + StringRef(AsmString, AsmStringLen), + StringRef(Constraints, ConstraintsLen), + HasSideEffects, IsAlignStack, fromRust(Dialect))); +} + +extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, + size_t ConstraintsLen) { + return InlineAsm::Verify(unwrap<FunctionType>(Ty), + StringRef(Constraints, ConstraintsLen)); +} + +extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, + size_t AsmLen) { + unwrap(M)->appendModuleInlineAsm(StringRef(Asm, AsmLen)); +} + +typedef DIBuilder *LLVMRustDIBuilderRef; + +template <typename DIT> DIT *unwrapDIPtr(LLVMMetadataRef Ref) { + return (DIT *)(Ref ? unwrap<MDNode>(Ref) : nullptr); +} + +#define DIDescriptor DIScope +#define DIArray DINodeArray +#define unwrapDI unwrapDIPtr + +// These values **must** match debuginfo::DIFlags! They also *happen* +// to match LLVM, but that isn't required as we do giant sets of +// matching below. The value shouldn't be directly passed to LLVM. +enum class LLVMRustDIFlags : uint32_t { + FlagZero = 0, + FlagPrivate = 1, + FlagProtected = 2, + FlagPublic = 3, + FlagFwdDecl = (1 << 2), + FlagAppleBlock = (1 << 3), + FlagBlockByrefStruct = (1 << 4), + FlagVirtual = (1 << 5), + FlagArtificial = (1 << 6), + FlagExplicit = (1 << 7), + FlagPrototyped = (1 << 8), + FlagObjcClassComplete = (1 << 9), + FlagObjectPointer = (1 << 10), + FlagVector = (1 << 11), + FlagStaticMember = (1 << 12), + FlagLValueReference = (1 << 13), + FlagRValueReference = (1 << 14), + FlagExternalTypeRef = (1 << 15), + FlagIntroducedVirtual = (1 << 18), + FlagBitField = (1 << 19), + FlagNoReturn = (1 << 20), + // Do not add values that are not supported by the minimum LLVM + // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def +}; + +inline LLVMRustDIFlags operator&(LLVMRustDIFlags A, LLVMRustDIFlags B) { + return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(A) & + static_cast<uint32_t>(B)); +} + +inline LLVMRustDIFlags operator|(LLVMRustDIFlags A, LLVMRustDIFlags B) { + return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(A) | + static_cast<uint32_t>(B)); +} + +inline LLVMRustDIFlags &operator|=(LLVMRustDIFlags &A, LLVMRustDIFlags B) { + return A = A | B; +} + +inline bool isSet(LLVMRustDIFlags F) { return F != LLVMRustDIFlags::FlagZero; } + +inline LLVMRustDIFlags visibility(LLVMRustDIFlags F) { + return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(F) & 0x3); +} + +static DINode::DIFlags fromRust(LLVMRustDIFlags Flags) { + DINode::DIFlags Result = DINode::DIFlags::FlagZero; + + switch (visibility(Flags)) { + case LLVMRustDIFlags::FlagPrivate: + Result |= DINode::DIFlags::FlagPrivate; + break; + case LLVMRustDIFlags::FlagProtected: + Result |= DINode::DIFlags::FlagProtected; + break; + case LLVMRustDIFlags::FlagPublic: + Result |= DINode::DIFlags::FlagPublic; + break; + default: + // The rest are handled below + break; + } + + if (isSet(Flags & LLVMRustDIFlags::FlagFwdDecl)) { + Result |= DINode::DIFlags::FlagFwdDecl; + } + if (isSet(Flags & LLVMRustDIFlags::FlagAppleBlock)) { + Result |= DINode::DIFlags::FlagAppleBlock; + } +#if LLVM_VERSION_LT(10, 0) + if (isSet(Flags & LLVMRustDIFlags::FlagBlockByrefStruct)) { + Result |= DINode::DIFlags::FlagBlockByrefStruct; + } +#endif + if (isSet(Flags & LLVMRustDIFlags::FlagVirtual)) { + Result |= DINode::DIFlags::FlagVirtual; + } + if (isSet(Flags & LLVMRustDIFlags::FlagArtificial)) { + Result |= DINode::DIFlags::FlagArtificial; + } + if (isSet(Flags & LLVMRustDIFlags::FlagExplicit)) { + Result |= DINode::DIFlags::FlagExplicit; + } + if (isSet(Flags & LLVMRustDIFlags::FlagPrototyped)) { + Result |= DINode::DIFlags::FlagPrototyped; + } + if (isSet(Flags & LLVMRustDIFlags::FlagObjcClassComplete)) { + Result |= DINode::DIFlags::FlagObjcClassComplete; + } + if (isSet(Flags & LLVMRustDIFlags::FlagObjectPointer)) { + Result |= DINode::DIFlags::FlagObjectPointer; + } + if (isSet(Flags & LLVMRustDIFlags::FlagVector)) { + Result |= DINode::DIFlags::FlagVector; + } + if (isSet(Flags & LLVMRustDIFlags::FlagStaticMember)) { + Result |= DINode::DIFlags::FlagStaticMember; + } + if (isSet(Flags & LLVMRustDIFlags::FlagLValueReference)) { + Result |= DINode::DIFlags::FlagLValueReference; + } + if (isSet(Flags & LLVMRustDIFlags::FlagRValueReference)) { + Result |= DINode::DIFlags::FlagRValueReference; + } + if (isSet(Flags & LLVMRustDIFlags::FlagIntroducedVirtual)) { + Result |= DINode::DIFlags::FlagIntroducedVirtual; + } + if (isSet(Flags & LLVMRustDIFlags::FlagBitField)) { + Result |= DINode::DIFlags::FlagBitField; + } + if (isSet(Flags & LLVMRustDIFlags::FlagNoReturn)) { + Result |= DINode::DIFlags::FlagNoReturn; + } + + return Result; +} + +// These values **must** match debuginfo::DISPFlags! They also *happen* +// to match LLVM, but that isn't required as we do giant sets of +// matching below. The value shouldn't be directly passed to LLVM. +enum class LLVMRustDISPFlags : uint32_t { + SPFlagZero = 0, + SPFlagVirtual = 1, + SPFlagPureVirtual = 2, + SPFlagLocalToUnit = (1 << 2), + SPFlagDefinition = (1 << 3), + SPFlagOptimized = (1 << 4), + SPFlagMainSubprogram = (1 << 5), + // Do not add values that are not supported by the minimum LLVM + // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def + // (In LLVM < 8, createFunction supported these as separate bool arguments.) +}; + +inline LLVMRustDISPFlags operator&(LLVMRustDISPFlags A, LLVMRustDISPFlags B) { + return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(A) & + static_cast<uint32_t>(B)); +} + +inline LLVMRustDISPFlags operator|(LLVMRustDISPFlags A, LLVMRustDISPFlags B) { + return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(A) | + static_cast<uint32_t>(B)); +} + +inline LLVMRustDISPFlags &operator|=(LLVMRustDISPFlags &A, LLVMRustDISPFlags B) { + return A = A | B; +} + +inline bool isSet(LLVMRustDISPFlags F) { return F != LLVMRustDISPFlags::SPFlagZero; } + +inline LLVMRustDISPFlags virtuality(LLVMRustDISPFlags F) { + return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(F) & 0x3); +} + +static DISubprogram::DISPFlags fromRust(LLVMRustDISPFlags SPFlags) { + DISubprogram::DISPFlags Result = DISubprogram::DISPFlags::SPFlagZero; + + switch (virtuality(SPFlags)) { + case LLVMRustDISPFlags::SPFlagVirtual: + Result |= DISubprogram::DISPFlags::SPFlagVirtual; + break; + case LLVMRustDISPFlags::SPFlagPureVirtual: + Result |= DISubprogram::DISPFlags::SPFlagPureVirtual; + break; + default: + // The rest are handled below + break; + } + + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagLocalToUnit)) { + Result |= DISubprogram::DISPFlags::SPFlagLocalToUnit; + } + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagDefinition)) { + Result |= DISubprogram::DISPFlags::SPFlagDefinition; + } + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagOptimized)) { + Result |= DISubprogram::DISPFlags::SPFlagOptimized; + } +#if LLVM_VERSION_GE(9, 0) + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) { + Result |= DISubprogram::DISPFlags::SPFlagMainSubprogram; + } +#endif + + return Result; +} + +enum class LLVMRustDebugEmissionKind { + NoDebug, + FullDebug, + LineTablesOnly, +}; + +static DICompileUnit::DebugEmissionKind fromRust(LLVMRustDebugEmissionKind Kind) { + switch (Kind) { + case LLVMRustDebugEmissionKind::NoDebug: + return DICompileUnit::DebugEmissionKind::NoDebug; + case LLVMRustDebugEmissionKind::FullDebug: + return DICompileUnit::DebugEmissionKind::FullDebug; + case LLVMRustDebugEmissionKind::LineTablesOnly: + return DICompileUnit::DebugEmissionKind::LineTablesOnly; + default: + report_fatal_error("bad DebugEmissionKind."); + } +} + +enum class LLVMRustChecksumKind { + None, + MD5, + SHA1, +}; + +static Optional<DIFile::ChecksumKind> fromRust(LLVMRustChecksumKind Kind) { + switch (Kind) { + case LLVMRustChecksumKind::None: + return None; + case LLVMRustChecksumKind::MD5: + return DIFile::ChecksumKind::CSK_MD5; + case LLVMRustChecksumKind::SHA1: + return DIFile::ChecksumKind::CSK_SHA1; + default: + report_fatal_error("bad ChecksumKind."); + } +} + +extern "C" uint32_t LLVMRustDebugMetadataVersion() { + return DEBUG_METADATA_VERSION; +} + +extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; } + +extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; } + +extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *Name, + uint32_t Value) { + unwrap(M)->addModuleFlag(Module::Warning, Name, Value); +} + +extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef C, LLVMMetadataRef MD) { + return wrap(MetadataAsValue::get(*unwrap(C), unwrap(MD))); +} + +extern "C" LLVMRustDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) { + return new DIBuilder(*unwrap(M)); +} + +extern "C" void LLVMRustDIBuilderDispose(LLVMRustDIBuilderRef Builder) { + delete Builder; +} + +extern "C" void LLVMRustDIBuilderFinalize(LLVMRustDIBuilderRef Builder) { + Builder->finalize(); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( + LLVMRustDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, + const char *Producer, size_t ProducerLen, bool isOptimized, + const char *Flags, unsigned RuntimeVer, + const char *SplitName, size_t SplitNameLen, + LLVMRustDebugEmissionKind Kind) { + auto *File = unwrapDI<DIFile>(FileRef); + + return wrap(Builder->createCompileUnit(Lang, File, StringRef(Producer, ProducerLen), + isOptimized, Flags, RuntimeVer, + StringRef(SplitName, SplitNameLen), + fromRust(Kind))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile( + LLVMRustDIBuilderRef Builder, + const char *Filename, size_t FilenameLen, + const char *Directory, size_t DirectoryLen, LLVMRustChecksumKind CSKind, + const char *Checksum, size_t ChecksumLen) { + Optional<DIFile::ChecksumKind> llvmCSKind = fromRust(CSKind); + Optional<DIFile::ChecksumInfo<StringRef>> CSInfo{}; + if (llvmCSKind) + CSInfo.emplace(*llvmCSKind, StringRef{Checksum, ChecksumLen}); + return wrap(Builder->createFile(StringRef(Filename, FilenameLen), + StringRef(Directory, DirectoryLen), + CSInfo)); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef ParameterTypes) { + return wrap(Builder->createSubroutineType( + DITypeRefArray(unwrap<MDTuple>(ParameterTypes)))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + const char *LinkageName, size_t LinkageNameLen, + LLVMMetadataRef File, unsigned LineNo, + LLVMMetadataRef Ty, unsigned ScopeLine, LLVMRustDIFlags Flags, + LLVMRustDISPFlags SPFlags, LLVMValueRef Fn, LLVMMetadataRef TParam, + LLVMMetadataRef Decl) { + DITemplateParameterArray TParams = + DITemplateParameterArray(unwrap<MDTuple>(TParam)); + DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags); + DINode::DIFlags llvmFlags = fromRust(Flags); +#if LLVM_VERSION_LT(9, 0) + if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) + llvmFlags |= DINode::DIFlags::FlagMainSubprogram; +#endif + DISubprogram *Sub = Builder->createFunction( + unwrapDI<DIScope>(Scope), + StringRef(Name, NameLen), + StringRef(LinkageName, LinkageNameLen), + unwrapDI<DIFile>(File), LineNo, + unwrapDI<DISubroutineType>(Ty), ScopeLine, llvmFlags, + llvmSPFlags, TParams, unwrapDIPtr<DISubprogram>(Decl)); + unwrap<Function>(Fn)->setSubprogram(Sub); + return wrap(Sub); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType( + LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, + uint64_t SizeInBits, unsigned Encoding) { + return wrap(Builder->createBasicType(StringRef(Name, NameLen), SizeInBits, Encoding)); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTypedef( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Type, const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Scope) { + return wrap(Builder->createTypedef( + unwrap<DIType>(Type), StringRef(Name, NameLen), unwrap<DIFile>(File), + LineNo, unwrap<DIScope>(Scope))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef PointeeTy, + uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace, + const char *Name, size_t NameLen) { + return wrap(Builder->createPointerType(unwrapDI<DIType>(PointeeTy), + SizeInBits, AlignInBits, + AddressSpace, + StringRef(Name, NameLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMRustDIFlags Flags, + LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, + unsigned RunTimeLang, LLVMMetadataRef VTableHolder, + const char *UniqueId, size_t UniqueIdLen) { + return wrap(Builder->createStructType( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNumber, + SizeInBits, AlignInBits, fromRust(Flags), unwrapDI<DIType>(DerivedFrom), + DINodeArray(unwrapDI<MDTuple>(Elements)), RunTimeLang, + unwrapDI<DIType>(VTableHolder), StringRef(UniqueId, UniqueIdLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Discriminator, + LLVMMetadataRef Elements, const char *UniqueId, size_t UniqueIdLen) { + return wrap(Builder->createVariantPart( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNumber, + SizeInBits, AlignInBits, fromRust(Flags), unwrapDI<DIDerivedType>(Discriminator), + DINodeArray(unwrapDI<MDTuple>(Elements)), StringRef(UniqueId, UniqueIdLen))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, + uint32_t AlignInBits, uint64_t OffsetInBits, LLVMRustDIFlags Flags, + LLVMMetadataRef Ty) { + return wrap(Builder->createMemberType(unwrapDI<DIDescriptor>(Scope), + StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNo, + SizeInBits, AlignInBits, OffsetInBits, + fromRust(Flags), unwrapDI<DIType>(Ty))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, + uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant, + LLVMRustDIFlags Flags, LLVMMetadataRef Ty) { + llvm::ConstantInt* D = nullptr; + if (Discriminant) { + D = unwrap<llvm::ConstantInt>(Discriminant); + } + return wrap(Builder->createVariantMemberType(unwrapDI<DIDescriptor>(Scope), + StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNo, + SizeInBits, AlignInBits, OffsetInBits, D, + fromRust(Flags), unwrapDI<DIType>(Ty))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlock( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + LLVMMetadataRef File, unsigned Line, unsigned Col) { + return wrap(Builder->createLexicalBlock(unwrapDI<DIDescriptor>(Scope), + unwrapDI<DIFile>(File), Line, Col)); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderCreateLexicalBlockFile(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef Scope, + LLVMMetadataRef File) { + return wrap(Builder->createLexicalBlockFile(unwrapDI<DIDescriptor>(Scope), + unwrapDI<DIFile>(File))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Context, + const char *Name, size_t NameLen, + const char *LinkageName, size_t LinkageNameLen, + LLVMMetadataRef File, unsigned LineNo, + LLVMMetadataRef Ty, bool IsLocalToUnit, LLVMValueRef V, + LLVMMetadataRef Decl = nullptr, uint32_t AlignInBits = 0) { + llvm::GlobalVariable *InitVal = cast<llvm::GlobalVariable>(unwrap(V)); + + llvm::DIExpression *InitExpr = nullptr; + if (llvm::ConstantInt *IntVal = llvm::dyn_cast<llvm::ConstantInt>(InitVal)) { + InitExpr = Builder->createConstantValueExpression( + IntVal->getValue().getSExtValue()); + } else if (llvm::ConstantFP *FPVal = + llvm::dyn_cast<llvm::ConstantFP>(InitVal)) { + InitExpr = Builder->createConstantValueExpression( + FPVal->getValueAPF().bitcastToAPInt().getZExtValue()); + } + + llvm::DIGlobalVariableExpression *VarExpr = Builder->createGlobalVariableExpression( + unwrapDI<DIDescriptor>(Context), StringRef(Name, NameLen), + StringRef(LinkageName, LinkageNameLen), + unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), IsLocalToUnit, +#if LLVM_VERSION_GE(10, 0) + /* isDefined */ true, +#endif + InitExpr, unwrapDIPtr<MDNode>(Decl), + /* templateParams */ nullptr, + AlignInBits); + + InitVal->setMetadata("dbg", VarExpr); + + return wrap(VarExpr); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( + LLVMRustDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, + LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags, + unsigned ArgNo, uint32_t AlignInBits) { + if (Tag == 0x100) { // DW_TAG_auto_variable + return wrap(Builder->createAutoVariable( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNo, + unwrapDI<DIType>(Ty), AlwaysPreserve, fromRust(Flags), AlignInBits)); + } else { + return wrap(Builder->createParameterVariable( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ArgNo, + unwrapDI<DIFile>(File), LineNo, + unwrapDI<DIType>(Ty), AlwaysPreserve, fromRust(Flags))); + } +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderCreateArrayType(LLVMRustDIBuilderRef Builder, uint64_t Size, + uint32_t AlignInBits, LLVMMetadataRef Ty, + LLVMMetadataRef Subscripts) { + return wrap( + Builder->createArrayType(Size, AlignInBits, unwrapDI<DIType>(Ty), + DINodeArray(unwrapDI<MDTuple>(Subscripts)))); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderGetOrCreateSubrange(LLVMRustDIBuilderRef Builder, int64_t Lo, + int64_t Count) { + return wrap(Builder->getOrCreateSubrange(Lo, Count)); +} + +extern "C" LLVMMetadataRef +LLVMRustDIBuilderGetOrCreateArray(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef *Ptr, unsigned Count) { + Metadata **DataValue = unwrap(Ptr); + return wrap( + Builder->getOrCreateArray(ArrayRef<Metadata *>(DataValue, Count)).get()); +} + +extern "C" LLVMValueRef LLVMRustDIBuilderInsertDeclareAtEnd( + LLVMRustDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo, + int64_t *AddrOps, unsigned AddrOpsCount, LLVMValueRef DL, + LLVMBasicBlockRef InsertAtEnd) { + return wrap(Builder->insertDeclare( + unwrap(V), unwrap<DILocalVariable>(VarInfo), + Builder->createExpression(llvm::ArrayRef<int64_t>(AddrOps, AddrOpsCount)), + DebugLoc(cast<MDNode>(unwrap<MetadataAsValue>(DL)->getMetadata())), + unwrap(InsertAtEnd))); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator( + LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, + int64_t Value, bool IsUnsigned) { + return wrap(Builder->createEnumerator(StringRef(Name, NameLen), Value, IsUnsigned)); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMMetadataRef Elements, + LLVMMetadataRef ClassTy, bool IsScoped) { + return wrap(Builder->createEnumerationType( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), + unwrapDI<DIFile>(File), LineNumber, + SizeInBits, AlignInBits, DINodeArray(unwrapDI<MDTuple>(Elements)), + unwrapDI<DIType>(ClassTy), "", IsScoped)); +} + +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, + uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Elements, + unsigned RunTimeLang, const char *UniqueId, size_t UniqueIdLen) { + return wrap(Builder->createUnionType( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIFile>(File), + LineNumber, SizeInBits, AlignInBits, fromRust(Flags), + DINodeArray(unwrapDI<MDTuple>(Elements)), RunTimeLang, + StringRef(UniqueId, UniqueIdLen))); +} + +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( + LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, + const char *Name, size_t NameLen, bool ExportSymbols) { + return wrap(Builder->createNameSpace( + unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ExportSymbols + )); +} + +extern "C" void +LLVMRustDICompositeTypeReplaceArrays(LLVMRustDIBuilderRef Builder, + LLVMMetadataRef CompositeTy, + LLVMMetadataRef Elements, + LLVMMetadataRef Params) { + DICompositeType *Tmp = unwrapDI<DICompositeType>(CompositeTy); + Builder->replaceArrays(Tmp, DINodeArray(unwrap<MDTuple>(Elements)), + DINodeArray(unwrap<MDTuple>(Params))); +} + +extern "C" LLVMValueRef +LLVMRustDIBuilderCreateDebugLocation(LLVMContextRef ContextRef, unsigned Line, + unsigned Column, LLVMMetadataRef Scope, + LLVMMetadataRef InlinedAt) { + LLVMContext &Context = *unwrap(ContextRef); + + DebugLoc debug_loc = DebugLoc::get(Line, Column, unwrapDIPtr<MDNode>(Scope), + unwrapDIPtr<MDNode>(InlinedAt)); + + return wrap(MetadataAsValue::get(Context, debug_loc.getAsMDNode())); +} + +extern "C" int64_t LLVMRustDIBuilderCreateOpDeref() { + return dwarf::DW_OP_deref; +} + +extern "C" int64_t LLVMRustDIBuilderCreateOpPlusUconst() { + return dwarf::DW_OP_plus_uconst; +} + +extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) { + RawRustStringOstream OS(Str); + unwrap<llvm::Type>(Ty)->print(OS); +} + +extern "C" void LLVMRustWriteValueToString(LLVMValueRef V, + RustStringRef Str) { + RawRustStringOstream OS(Str); + if (!V) { + OS << "(null)"; + } else { + OS << "("; + unwrap<llvm::Value>(V)->getType()->print(OS); + OS << ":"; + unwrap<llvm::Value>(V)->print(OS); + OS << ")"; + } +} + +// Note that the two following functions look quite similar to the +// LLVMGetSectionName function. Sadly, it appears that this function only +// returns a char* pointer, which isn't guaranteed to be null-terminated. The +// function provided by LLVM doesn't return the length, so we've created our own +// function which returns the length as well as the data pointer. +// +// For an example of this not returning a null terminated string, see +// lib/Object/COFFObjectFile.cpp in the getSectionName function. One of the +// branches explicitly creates a StringRef without a null terminator, and then +// that's returned. + +inline section_iterator *unwrap(LLVMSectionIteratorRef SI) { + return reinterpret_cast<section_iterator *>(SI); +} + +extern "C" size_t LLVMRustGetSectionName(LLVMSectionIteratorRef SI, + const char **Ptr) { +#if LLVM_VERSION_GE(10, 0) + auto NameOrErr = (*unwrap(SI))->getName(); + if (!NameOrErr) + report_fatal_error(NameOrErr.takeError()); + *Ptr = NameOrErr->data(); + return NameOrErr->size(); +#else + StringRef Ret; + if (std::error_code EC = (*unwrap(SI))->getName(Ret)) + report_fatal_error(EC.message()); + *Ptr = Ret.data(); + return Ret.size(); +#endif +} + +// LLVMArrayType function does not support 64-bit ElementCount +extern "C" LLVMTypeRef LLVMRustArrayType(LLVMTypeRef ElementTy, + uint64_t ElementCount) { + return wrap(ArrayType::get(unwrap(ElementTy), ElementCount)); +} + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Twine, LLVMTwineRef) + +extern "C" void LLVMRustWriteTwineToString(LLVMTwineRef T, RustStringRef Str) { + RawRustStringOstream OS(Str); + unwrap(T)->print(OS); +} + +extern "C" void LLVMRustUnpackOptimizationDiagnostic( + LLVMDiagnosticInfoRef DI, RustStringRef PassNameOut, + LLVMValueRef *FunctionOut, unsigned* Line, unsigned* Column, + RustStringRef FilenameOut, RustStringRef MessageOut) { + // Undefined to call this not on an optimization diagnostic! + llvm::DiagnosticInfoOptimizationBase *Opt = + static_cast<llvm::DiagnosticInfoOptimizationBase *>(unwrap(DI)); + + RawRustStringOstream PassNameOS(PassNameOut); + PassNameOS << Opt->getPassName(); + *FunctionOut = wrap(&Opt->getFunction()); + + RawRustStringOstream FilenameOS(FilenameOut); + DiagnosticLocation loc = Opt->getLocation(); + if (loc.isValid()) { + *Line = loc.getLine(); + *Column = loc.getColumn(); + FilenameOS << loc.getAbsolutePath(); + } + + RawRustStringOstream MessageOS(MessageOut); + MessageOS << Opt->getMsg(); +} + +enum class LLVMRustDiagnosticLevel { + Error, + Warning, + Note, + Remark, +}; + +extern "C" void +LLVMRustUnpackInlineAsmDiagnostic(LLVMDiagnosticInfoRef DI, + LLVMRustDiagnosticLevel *LevelOut, + unsigned *CookieOut, + LLVMTwineRef *MessageOut, + LLVMValueRef *InstructionOut) { + // Undefined to call this not on an inline assembly diagnostic! + llvm::DiagnosticInfoInlineAsm *IA = + static_cast<llvm::DiagnosticInfoInlineAsm *>(unwrap(DI)); + + *CookieOut = IA->getLocCookie(); + *MessageOut = wrap(&IA->getMsgStr()); + *InstructionOut = wrap(IA->getInstruction()); + + switch (IA->getSeverity()) { + case DS_Error: + *LevelOut = LLVMRustDiagnosticLevel::Error; + break; + case DS_Warning: + *LevelOut = LLVMRustDiagnosticLevel::Warning; + break; + case DS_Note: + *LevelOut = LLVMRustDiagnosticLevel::Note; + break; + case DS_Remark: + *LevelOut = LLVMRustDiagnosticLevel::Remark; + break; + default: + report_fatal_error("Invalid LLVMRustDiagnosticLevel value!"); + } +} + +extern "C" void LLVMRustWriteDiagnosticInfoToString(LLVMDiagnosticInfoRef DI, + RustStringRef Str) { + RawRustStringOstream OS(Str); + DiagnosticPrinterRawOStream DP(OS); + unwrap(DI)->print(DP); +} + +enum class LLVMRustDiagnosticKind { + Other, + InlineAsm, + StackSize, + DebugMetadataVersion, + SampleProfile, + OptimizationRemark, + OptimizationRemarkMissed, + OptimizationRemarkAnalysis, + OptimizationRemarkAnalysisFPCommute, + OptimizationRemarkAnalysisAliasing, + OptimizationRemarkOther, + OptimizationFailure, + PGOProfile, + Linker, +}; + +static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) { + switch (Kind) { + case DK_InlineAsm: + return LLVMRustDiagnosticKind::InlineAsm; + case DK_StackSize: + return LLVMRustDiagnosticKind::StackSize; + case DK_DebugMetadataVersion: + return LLVMRustDiagnosticKind::DebugMetadataVersion; + case DK_SampleProfile: + return LLVMRustDiagnosticKind::SampleProfile; + case DK_OptimizationRemark: + return LLVMRustDiagnosticKind::OptimizationRemark; + case DK_OptimizationRemarkMissed: + return LLVMRustDiagnosticKind::OptimizationRemarkMissed; + case DK_OptimizationRemarkAnalysis: + return LLVMRustDiagnosticKind::OptimizationRemarkAnalysis; + case DK_OptimizationRemarkAnalysisFPCommute: + return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisFPCommute; + case DK_OptimizationRemarkAnalysisAliasing: + return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisAliasing; + case DK_PGOProfile: + return LLVMRustDiagnosticKind::PGOProfile; + case DK_Linker: + return LLVMRustDiagnosticKind::Linker; + default: + return (Kind >= DK_FirstRemark && Kind <= DK_LastRemark) + ? LLVMRustDiagnosticKind::OptimizationRemarkOther + : LLVMRustDiagnosticKind::Other; + } +} + +extern "C" LLVMRustDiagnosticKind +LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) { + return toRust((DiagnosticKind)unwrap(DI)->getKind()); +} + +// This is kept distinct from LLVMGetTypeKind, because when +// a new type kind is added, the Rust-side enum must be +// updated or UB will result. +extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) { + switch (unwrap(Ty)->getTypeID()) { + case Type::VoidTyID: + return LLVMVoidTypeKind; + case Type::HalfTyID: + return LLVMHalfTypeKind; + case Type::FloatTyID: + return LLVMFloatTypeKind; + case Type::DoubleTyID: + return LLVMDoubleTypeKind; + case Type::X86_FP80TyID: + return LLVMX86_FP80TypeKind; + case Type::FP128TyID: + return LLVMFP128TypeKind; + case Type::PPC_FP128TyID: + return LLVMPPC_FP128TypeKind; + case Type::LabelTyID: + return LLVMLabelTypeKind; + case Type::MetadataTyID: + return LLVMMetadataTypeKind; + case Type::IntegerTyID: + return LLVMIntegerTypeKind; + case Type::FunctionTyID: + return LLVMFunctionTypeKind; + case Type::StructTyID: + return LLVMStructTypeKind; + case Type::ArrayTyID: + 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 + } + report_fatal_error("Unhandled TypeID."); +} + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef) + +extern "C" void LLVMRustSetInlineAsmDiagnosticHandler( + LLVMContextRef C, LLVMContext::InlineAsmDiagHandlerTy H, void *CX) { + unwrap(C)->setInlineAsmDiagnosticHandler(H, CX); +} + +extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, + RustStringRef MessageOut, + RustStringRef BufferOut, + LLVMRustDiagnosticLevel* LevelOut, + unsigned* LocOut, + unsigned* RangesOut, + size_t* NumRanges) { + SMDiagnostic& D = *unwrap(DRef); + RawRustStringOstream MessageOS(MessageOut); + MessageOS << D.getMessage(); + + switch (D.getKind()) { + case SourceMgr::DK_Error: + *LevelOut = LLVMRustDiagnosticLevel::Error; + break; + case SourceMgr::DK_Warning: + *LevelOut = LLVMRustDiagnosticLevel::Warning; + break; + case SourceMgr::DK_Note: + *LevelOut = LLVMRustDiagnosticLevel::Note; + break; + case SourceMgr::DK_Remark: + *LevelOut = LLVMRustDiagnosticLevel::Remark; + break; + default: + report_fatal_error("Invalid LLVMRustDiagnosticLevel value!"); + } + + if (D.getLoc() == SMLoc()) + return false; + + const SourceMgr &LSM = *D.getSourceMgr(); + const MemoryBuffer *LBuf = LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); + LLVMRustStringWriteImpl(BufferOut, LBuf->getBufferStart(), LBuf->getBufferSize()); + + *LocOut = D.getLoc().getPointer() - LBuf->getBufferStart(); + + *NumRanges = std::min(*NumRanges, D.getRanges().size()); + size_t LineStart = *LocOut - (size_t)D.getColumnNo(); + for (size_t i = 0; i < *NumRanges; i++) { + RangesOut[i * 2] = LineStart + D.getRanges()[i].first; + RangesOut[i * 2 + 1] = LineStart + D.getRanges()[i].second; + } + + return true; +} + +extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B, + LLVMValueRef ParentPad, + unsigned ArgCount, + LLVMValueRef *LLArgs, + const char *Name) { + Value **Args = unwrap(LLArgs); + if (ParentPad == nullptr) { + Type *Ty = Type::getTokenTy(unwrap(B)->getContext()); + ParentPad = wrap(Constant::getNullValue(Ty)); + } + return wrap(unwrap(B)->CreateCleanupPad( + unwrap(ParentPad), ArrayRef<Value *>(Args, ArgCount), Name)); +} + +extern "C" LLVMValueRef LLVMRustBuildCleanupRet(LLVMBuilderRef B, + LLVMValueRef CleanupPad, + LLVMBasicBlockRef UnwindBB) { + CleanupPadInst *Inst = cast<CleanupPadInst>(unwrap(CleanupPad)); + return wrap(unwrap(B)->CreateCleanupRet(Inst, unwrap(UnwindBB))); +} + +extern "C" LLVMValueRef +LLVMRustBuildCatchPad(LLVMBuilderRef B, LLVMValueRef ParentPad, + unsigned ArgCount, LLVMValueRef *LLArgs, const char *Name) { + Value **Args = unwrap(LLArgs); + return wrap(unwrap(B)->CreateCatchPad( + unwrap(ParentPad), ArrayRef<Value *>(Args, ArgCount), Name)); +} + +extern "C" LLVMValueRef LLVMRustBuildCatchRet(LLVMBuilderRef B, + LLVMValueRef Pad, + LLVMBasicBlockRef BB) { + return wrap(unwrap(B)->CreateCatchRet(cast<CatchPadInst>(unwrap(Pad)), + unwrap(BB))); +} + +extern "C" LLVMValueRef LLVMRustBuildCatchSwitch(LLVMBuilderRef B, + LLVMValueRef ParentPad, + LLVMBasicBlockRef BB, + unsigned NumHandlers, + const char *Name) { + if (ParentPad == nullptr) { + Type *Ty = Type::getTokenTy(unwrap(B)->getContext()); + ParentPad = wrap(Constant::getNullValue(Ty)); + } + return wrap(unwrap(B)->CreateCatchSwitch(unwrap(ParentPad), unwrap(BB), + NumHandlers, Name)); +} + +extern "C" void LLVMRustAddHandler(LLVMValueRef CatchSwitchRef, + LLVMBasicBlockRef Handler) { + Value *CatchSwitch = unwrap(CatchSwitchRef); + cast<CatchSwitchInst>(CatchSwitch)->addHandler(unwrap(Handler)); +} + +extern "C" OperandBundleDef *LLVMRustBuildOperandBundleDef(const char *Name, + LLVMValueRef *Inputs, + unsigned NumInputs) { + return new OperandBundleDef(Name, makeArrayRef(unwrap(Inputs), NumInputs)); +} + +extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) { + delete Bundle; +} + +extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, + LLVMValueRef *Args, unsigned NumArgs, + OperandBundleDef *Bundle) { + Value *Callee = unwrap(Fn); + FunctionType *FTy = cast<FunctionType>(Callee->getType()->getPointerElementType()); + unsigned Len = Bundle ? 1 : 0; + ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len); + return wrap(unwrap(B)->CreateCall( + FTy, Callee, makeArrayRef(unwrap(Args), NumArgs), Bundles)); +} + +extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) { + return wrap(llvm::Intrinsic::getDeclaration(unwrap(M), + (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment)); +} + +extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, + LLVMValueRef Dst, unsigned DstAlign, + LLVMValueRef Src, unsigned SrcAlign, + LLVMValueRef Size, bool IsVolatile) { +#if LLVM_VERSION_GE(10, 0) + return wrap(unwrap(B)->CreateMemCpy( + unwrap(Dst), MaybeAlign(DstAlign), + unwrap(Src), MaybeAlign(SrcAlign), + unwrap(Size), IsVolatile)); +#else + return wrap(unwrap(B)->CreateMemCpy( + unwrap(Dst), DstAlign, + unwrap(Src), SrcAlign, + unwrap(Size), IsVolatile)); +#endif +} + +extern "C" LLVMValueRef LLVMRustBuildMemMove(LLVMBuilderRef B, + LLVMValueRef Dst, unsigned DstAlign, + LLVMValueRef Src, unsigned SrcAlign, + LLVMValueRef Size, bool IsVolatile) { +#if LLVM_VERSION_GE(10, 0) + return wrap(unwrap(B)->CreateMemMove( + unwrap(Dst), MaybeAlign(DstAlign), + unwrap(Src), MaybeAlign(SrcAlign), + unwrap(Size), IsVolatile)); +#else + return wrap(unwrap(B)->CreateMemMove( + unwrap(Dst), DstAlign, + unwrap(Src), SrcAlign, + unwrap(Size), IsVolatile)); +#endif +} + +extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, + LLVMValueRef Dst, unsigned DstAlign, + LLVMValueRef Val, + LLVMValueRef Size, bool IsVolatile) { +#if LLVM_VERSION_GE(10, 0) + return wrap(unwrap(B)->CreateMemSet( + unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile)); +#else + return wrap(unwrap(B)->CreateMemSet( + unwrap(Dst), unwrap(Val), unwrap(Size), DstAlign, IsVolatile)); +#endif +} + +extern "C" LLVMValueRef +LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, + unsigned NumArgs, LLVMBasicBlockRef Then, + LLVMBasicBlockRef Catch, OperandBundleDef *Bundle, + const char *Name) { + Value *Callee = unwrap(Fn); + FunctionType *FTy = cast<FunctionType>(Callee->getType()->getPointerElementType()); + unsigned Len = Bundle ? 1 : 0; + ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len); + return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch), + makeArrayRef(unwrap(Args), NumArgs), + Bundles, Name)); +} + +extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, + LLVMBasicBlockRef BB) { + auto Point = unwrap(BB)->getFirstInsertionPt(); + unwrap(B)->SetInsertPoint(unwrap(BB), Point); +} + +extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, + const char *Name, size_t NameLen) { + Triple TargetTriple(unwrap(M)->getTargetTriple()); + GlobalObject *GV = unwrap<GlobalObject>(V); + if (!TargetTriple.isOSBinFormatMachO()) { + StringRef NameRef(Name, NameLen); + GV->setComdat(unwrap(M)->getOrInsertComdat(NameRef)); + } +} + +extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) { + GlobalObject *GV = unwrap<GlobalObject>(V); + GV->setComdat(nullptr); +} + +enum class LLVMRustLinkage { + ExternalLinkage = 0, + AvailableExternallyLinkage = 1, + LinkOnceAnyLinkage = 2, + LinkOnceODRLinkage = 3, + WeakAnyLinkage = 4, + WeakODRLinkage = 5, + AppendingLinkage = 6, + InternalLinkage = 7, + PrivateLinkage = 8, + ExternalWeakLinkage = 9, + CommonLinkage = 10, +}; + +static LLVMRustLinkage toRust(LLVMLinkage Linkage) { + switch (Linkage) { + case LLVMExternalLinkage: + return LLVMRustLinkage::ExternalLinkage; + case LLVMAvailableExternallyLinkage: + return LLVMRustLinkage::AvailableExternallyLinkage; + case LLVMLinkOnceAnyLinkage: + return LLVMRustLinkage::LinkOnceAnyLinkage; + case LLVMLinkOnceODRLinkage: + return LLVMRustLinkage::LinkOnceODRLinkage; + case LLVMWeakAnyLinkage: + return LLVMRustLinkage::WeakAnyLinkage; + case LLVMWeakODRLinkage: + return LLVMRustLinkage::WeakODRLinkage; + case LLVMAppendingLinkage: + return LLVMRustLinkage::AppendingLinkage; + case LLVMInternalLinkage: + return LLVMRustLinkage::InternalLinkage; + case LLVMPrivateLinkage: + return LLVMRustLinkage::PrivateLinkage; + case LLVMExternalWeakLinkage: + return LLVMRustLinkage::ExternalWeakLinkage; + case LLVMCommonLinkage: + return LLVMRustLinkage::CommonLinkage; + default: + report_fatal_error("Invalid LLVMRustLinkage value!"); + } +} + +static LLVMLinkage fromRust(LLVMRustLinkage Linkage) { + switch (Linkage) { + case LLVMRustLinkage::ExternalLinkage: + return LLVMExternalLinkage; + case LLVMRustLinkage::AvailableExternallyLinkage: + return LLVMAvailableExternallyLinkage; + case LLVMRustLinkage::LinkOnceAnyLinkage: + return LLVMLinkOnceAnyLinkage; + case LLVMRustLinkage::LinkOnceODRLinkage: + return LLVMLinkOnceODRLinkage; + case LLVMRustLinkage::WeakAnyLinkage: + return LLVMWeakAnyLinkage; + case LLVMRustLinkage::WeakODRLinkage: + return LLVMWeakODRLinkage; + case LLVMRustLinkage::AppendingLinkage: + return LLVMAppendingLinkage; + case LLVMRustLinkage::InternalLinkage: + return LLVMInternalLinkage; + case LLVMRustLinkage::PrivateLinkage: + return LLVMPrivateLinkage; + case LLVMRustLinkage::ExternalWeakLinkage: + return LLVMExternalWeakLinkage; + case LLVMRustLinkage::CommonLinkage: + return LLVMCommonLinkage; + } + report_fatal_error("Invalid LLVMRustLinkage value!"); +} + +extern "C" LLVMRustLinkage LLVMRustGetLinkage(LLVMValueRef V) { + return toRust(LLVMGetLinkage(V)); +} + +extern "C" void LLVMRustSetLinkage(LLVMValueRef V, + LLVMRustLinkage RustLinkage) { + LLVMSetLinkage(V, fromRust(RustLinkage)); +} + +// Returns true if both high and low were successfully set. Fails in case constant wasn’t any of +// the common sizes (1, 8, 16, 32, 64, 128 bits) +extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *high, uint64_t *low) +{ + auto C = unwrap<llvm::ConstantInt>(CV); + if (C->getBitWidth() > 128) { return false; } + APInt AP; + if (sext) { + AP = C->getValue().sextOrSelf(128); + } else { + AP = C->getValue().zextOrSelf(128); + } + *low = AP.getLoBits(64).getZExtValue(); + *high = AP.getHiBits(64).getZExtValue(); + return true; +} + +enum class LLVMRustVisibility { + Default = 0, + Hidden = 1, + Protected = 2, +}; + +static LLVMRustVisibility toRust(LLVMVisibility Vis) { + switch (Vis) { + case LLVMDefaultVisibility: + return LLVMRustVisibility::Default; + case LLVMHiddenVisibility: + return LLVMRustVisibility::Hidden; + case LLVMProtectedVisibility: + return LLVMRustVisibility::Protected; + } + report_fatal_error("Invalid LLVMRustVisibility value!"); +} + +static LLVMVisibility fromRust(LLVMRustVisibility Vis) { + switch (Vis) { + case LLVMRustVisibility::Default: + return LLVMDefaultVisibility; + case LLVMRustVisibility::Hidden: + return LLVMHiddenVisibility; + case LLVMRustVisibility::Protected: + return LLVMProtectedVisibility; + } + report_fatal_error("Invalid LLVMRustVisibility value!"); +} + +extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) { + return toRust(LLVMGetVisibility(V)); +} + +// Oh hey, a binding that makes sense for once? (because LLVM’s own do not) +extern "C" LLVMValueRef LLVMRustBuildIntCast(LLVMBuilderRef B, LLVMValueRef Val, + LLVMTypeRef DestTy, bool isSigned) { + return wrap(unwrap(B)->CreateIntCast(unwrap(Val), unwrap(DestTy), isSigned, "")); +} + +extern "C" void LLVMRustSetVisibility(LLVMValueRef V, + LLVMRustVisibility RustVisibility) { + LLVMSetVisibility(V, fromRust(RustVisibility)); +} + +struct LLVMRustModuleBuffer { + std::string data; +}; + +extern "C" LLVMRustModuleBuffer* +LLVMRustModuleBufferCreate(LLVMModuleRef M) { +#if LLVM_VERSION_GE(10, 0) + auto Ret = std::make_unique<LLVMRustModuleBuffer>(); +#else + auto Ret = llvm::make_unique<LLVMRustModuleBuffer>(); +#endif + { + raw_string_ostream OS(Ret->data); + { + legacy::PassManager PM; + PM.add(createBitcodeWriterPass(OS)); + PM.run(*unwrap(M)); + } + } + return Ret.release(); +} + +extern "C" void +LLVMRustModuleBufferFree(LLVMRustModuleBuffer *Buffer) { + delete Buffer; +} + +extern "C" const void* +LLVMRustModuleBufferPtr(const LLVMRustModuleBuffer *Buffer) { + return Buffer->data.data(); +} + +extern "C" size_t +LLVMRustModuleBufferLen(const LLVMRustModuleBuffer *Buffer) { + return Buffer->data.length(); +} + +extern "C" uint64_t +LLVMRustModuleCost(LLVMModuleRef M) { + auto f = unwrap(M)->functions(); + return std::distance(std::begin(f), std::end(f)); +} + +// Vector reductions: +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFAdd(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateFAddReduce(unwrap(Acc),unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFMul(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateFMulReduce(unwrap(Acc),unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceAdd(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateAddReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceMul(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateMulReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceAnd(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateAndReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceOr(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateOrReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceXor(LLVMBuilderRef B, LLVMValueRef Src) { + return wrap(unwrap(B)->CreateXorReduce(unwrap(Src))); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceMin(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) { + return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Src), IsSigned)); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceMax(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) { + return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Src), IsSigned)); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFMin(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { + return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Src), NoNaN)); +} +extern "C" LLVMValueRef +LLVMRustBuildVectorReduceFMax(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) { + return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Src), NoNaN)); +} + +extern "C" LLVMValueRef +LLVMRustBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) { + return wrap(unwrap(B)->CreateMinNum(unwrap(LHS),unwrap(RHS))); +} +extern "C" LLVMValueRef +LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) { + return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS),unwrap(RHS))); +} diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs new file mode 100644 index 00000000000..a7a10b91b4e --- /dev/null +++ b/compiler/rustc_llvm/src/lib.rs @@ -0,0 +1,173 @@ +#![feature(nll)] +#![feature(static_nobundle)] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] + +// NOTE: This crate only exists to allow linking on mingw targets. + +use libc::{c_char, size_t}; +use std::cell::RefCell; +use std::slice; + +#[repr(C)] +pub struct RustString { + pub bytes: RefCell<Vec<u8>>, +} + +impl RustString { + pub fn len(&self) -> usize { + self.bytes.borrow().len() + } +} + +/// Appending to a Rust string -- used by RawRustStringOstream. +#[no_mangle] +#[allow(improper_ctypes_definitions)] +pub unsafe extern "C" fn LLVMRustStringWriteImpl( + sr: &RustString, + ptr: *const c_char, + size: size_t, +) { + let slice = slice::from_raw_parts(ptr as *const u8, size as usize); + + sr.bytes.borrow_mut().extend_from_slice(slice); +} + +/// Initialize targets enabled by the build script via `cfg(llvm_component = "...")`. +/// N.B., this function can't be moved to `rustc_codegen_llvm` because of the `cfg`s. +pub fn initialize_available_targets() { + macro_rules! init_target( + ($cfg:meta, $($method:ident),*) => { { + #[cfg($cfg)] + fn init() { + extern { + $(fn $method();)* + } + unsafe { + $($method();)* + } + } + #[cfg(not($cfg))] + fn init() { } + init(); + } } + ); + init_target!( + llvm_component = "x86", + LLVMInitializeX86TargetInfo, + LLVMInitializeX86Target, + LLVMInitializeX86TargetMC, + LLVMInitializeX86AsmPrinter, + LLVMInitializeX86AsmParser + ); + init_target!( + llvm_component = "arm", + LLVMInitializeARMTargetInfo, + LLVMInitializeARMTarget, + LLVMInitializeARMTargetMC, + LLVMInitializeARMAsmPrinter, + LLVMInitializeARMAsmParser + ); + init_target!( + llvm_component = "aarch64", + LLVMInitializeAArch64TargetInfo, + LLVMInitializeAArch64Target, + LLVMInitializeAArch64TargetMC, + LLVMInitializeAArch64AsmPrinter, + LLVMInitializeAArch64AsmParser + ); + init_target!( + llvm_component = "amdgpu", + LLVMInitializeAMDGPUTargetInfo, + LLVMInitializeAMDGPUTarget, + LLVMInitializeAMDGPUTargetMC, + LLVMInitializeAMDGPUAsmPrinter, + LLVMInitializeAMDGPUAsmParser + ); + init_target!( + llvm_component = "avr", + LLVMInitializeAVRTargetInfo, + LLVMInitializeAVRTarget, + LLVMInitializeAVRTargetMC, + LLVMInitializeAVRAsmPrinter, + LLVMInitializeAVRAsmParser + ); + init_target!( + llvm_component = "mips", + LLVMInitializeMipsTargetInfo, + LLVMInitializeMipsTarget, + LLVMInitializeMipsTargetMC, + LLVMInitializeMipsAsmPrinter, + LLVMInitializeMipsAsmParser + ); + init_target!( + llvm_component = "powerpc", + LLVMInitializePowerPCTargetInfo, + LLVMInitializePowerPCTarget, + LLVMInitializePowerPCTargetMC, + LLVMInitializePowerPCAsmPrinter, + LLVMInitializePowerPCAsmParser + ); + init_target!( + llvm_component = "systemz", + LLVMInitializeSystemZTargetInfo, + LLVMInitializeSystemZTarget, + LLVMInitializeSystemZTargetMC, + LLVMInitializeSystemZAsmPrinter, + LLVMInitializeSystemZAsmParser + ); + init_target!( + llvm_component = "jsbackend", + LLVMInitializeJSBackendTargetInfo, + LLVMInitializeJSBackendTarget, + LLVMInitializeJSBackendTargetMC + ); + init_target!( + llvm_component = "msp430", + LLVMInitializeMSP430TargetInfo, + LLVMInitializeMSP430Target, + LLVMInitializeMSP430TargetMC, + LLVMInitializeMSP430AsmPrinter + ); + init_target!( + all(llvm_component = "msp430", llvm_has_msp430_asm_parser), + LLVMInitializeMSP430AsmParser + ); + init_target!( + llvm_component = "riscv", + LLVMInitializeRISCVTargetInfo, + LLVMInitializeRISCVTarget, + LLVMInitializeRISCVTargetMC, + LLVMInitializeRISCVAsmPrinter, + LLVMInitializeRISCVAsmParser + ); + init_target!( + llvm_component = "sparc", + LLVMInitializeSparcTargetInfo, + LLVMInitializeSparcTarget, + LLVMInitializeSparcTargetMC, + LLVMInitializeSparcAsmPrinter, + LLVMInitializeSparcAsmParser + ); + init_target!( + llvm_component = "nvptx", + LLVMInitializeNVPTXTargetInfo, + LLVMInitializeNVPTXTarget, + LLVMInitializeNVPTXTargetMC, + LLVMInitializeNVPTXAsmPrinter + ); + init_target!( + llvm_component = "hexagon", + LLVMInitializeHexagonTargetInfo, + LLVMInitializeHexagonTarget, + LLVMInitializeHexagonTargetMC, + LLVMInitializeHexagonAsmPrinter, + LLVMInitializeHexagonAsmParser + ); + init_target!( + llvm_component = "webassembly", + LLVMInitializeWebAssemblyTargetInfo, + LLVMInitializeWebAssemblyTarget, + LLVMInitializeWebAssemblyTargetMC, + LLVMInitializeWebAssemblyAsmPrinter + ); +} diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 7fb3b0e7ea6..5c28839c9b7 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(proc_macro_diagnostic)] #![allow(rustc::default_hash_types)] #![recursion_limit = "128"] @@ -9,6 +10,7 @@ mod hash_stable; mod lift; mod query; mod serialize; +mod session_diagnostic; mod symbols; mod type_foldable; @@ -36,3 +38,14 @@ decl_derive!([MetadataDecodable] => serialize::meta_decodable_derive); decl_derive!([MetadataEncodable] => serialize::meta_encodable_derive); decl_derive!([TypeFoldable, attributes(type_foldable)] => type_foldable::type_foldable_derive); decl_derive!([Lift, attributes(lift)] => lift::lift_derive); +decl_derive!( + [SessionDiagnostic, attributes( + message, + lint, + error, + label, + suggestion, + suggestion_short, + suggestion_hidden, + suggestion_verbose)] => session_diagnostic::session_diagnostic_derive +); diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index c17d5311e8f..204e8e800cd 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -5,8 +5,8 @@ use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ - braced, parenthesized, parse_macro_input, Attribute, Block, Error, Expr, Ident, ReturnType, - Token, Type, + braced, parenthesized, parse_macro_input, AttrStyle, Attribute, Block, Error, Expr, Ident, + ReturnType, Token, Type, }; #[allow(non_camel_case_types)] @@ -128,17 +128,25 @@ impl Parse for QueryModifier { } /// Ensures only doc comment attributes are used -fn check_attributes(attrs: Vec<Attribute>) -> Result<()> { - for attr in attrs { +fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> { + let inner = |attr: Attribute| { if !attr.path.is_ident("doc") { - return Err(Error::new(attr.span(), "attributes not supported on queries")); + Err(Error::new(attr.span(), "attributes not supported on queries")) + } else if attr.style != AttrStyle::Outer { + Err(Error::new( + attr.span(), + "attributes must be outer attributes (`///`), not inner attributes", + )) + } else { + Ok(attr) } - } - Ok(()) + }; + attrs.into_iter().map(inner).collect() } /// A compiler query. `query ... { ... }` struct Query { + doc_comments: Vec<Attribute>, modifiers: List<QueryModifier>, name: Ident, key: IdentOrWild, @@ -148,7 +156,7 @@ struct Query { impl Parse for Query { fn parse(input: ParseStream<'_>) -> Result<Self> { - check_attributes(input.call(Attribute::parse_outer)?)?; + let doc_comments = check_attributes(input.call(Attribute::parse_outer)?)?; // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>` input.parse::<kw::query>()?; @@ -165,7 +173,7 @@ impl Parse for Query { braced!(content in input); let modifiers = content.parse()?; - Ok(Query { modifiers, name, key, arg, result }) + Ok(Query { doc_comments, modifiers, name, key, arg, result }) } } @@ -392,7 +400,7 @@ fn add_query_description_impl( #tcx: TyCtxt<'tcx>, #key: #arg, ) -> Cow<'static, str> { - format!(#desc).into() + ::rustc_middle::ty::print::with_no_trimmed_paths(|| format!(#desc).into()) } }; @@ -476,9 +484,10 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { }; let attribute_stream = quote! {#(#attributes),*}; - + let doc_comments = query.doc_comments.iter(); // Add the query to the group group_stream.extend(quote! { + #(#doc_comments)* [#attribute_stream] fn #name: #name(#arg) #result, }); diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs new file mode 100644 index 00000000000..610b9155cfc --- /dev/null +++ b/compiler/rustc_macros/src/session_diagnostic.rs @@ -0,0 +1,666 @@ +#![deny(unused_must_use)] +use proc_macro::Diagnostic; +use quote::{format_ident, quote}; +use syn::spanned::Spanned; + +use std::collections::{BTreeSet, HashMap}; + +/// Implements #[derive(SessionDiagnostic)], which allows for errors to be specified as a struct, independent +/// from the actual diagnostics emitting code. +/// ```ignore (pseudo-rust) +/// # extern crate rustc_errors; +/// # use rustc_errors::Applicability; +/// # extern crate rustc_span; +/// # use rustc_span::{symbol::Ident, Span}; +/// # extern crate rust_middle; +/// # use rustc_middle::ty::Ty; +/// #[derive(SessionDiagnostic)] +/// #[code = "E0505"] +/// #[error = "cannot move out of {name} because it is borrowed"] +/// pub struct MoveOutOfBorrowError<'tcx> { +/// pub name: Ident, +/// pub ty: Ty<'tcx>, +/// #[label = "cannot move out of borrow"] +/// pub span: Span, +/// #[label = "`{ty}` first borrowed here"] +/// pub other_span: Span, +/// #[suggestion(message = "consider cloning here", code = "{name}.clone()")] +/// pub opt_sugg: Option<(Span, Applicability)> +/// } +/// ``` +/// Then, later, to emit the error: +/// +/// ```ignore (pseudo-rust) +/// sess.emit_err(MoveOutOfBorrowError { +/// expected, +/// actual, +/// span, +/// other_span, +/// opt_sugg: Some(suggestion, Applicability::MachineApplicable), +/// }); +/// ``` +pub fn session_diagnostic_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + // Names for the diagnostic we build and the session we build it from. + let diag = format_ident!("diag"); + let sess = format_ident!("sess"); + + SessionDiagnosticDerive::new(diag, sess, s).into_tokens() +} + +// Checks whether the type name of `ty` matches `name`. +// +// Given some struct at a::b::c::Foo, this will return true for c::Foo, b::c::Foo, or +// a::b::c::Foo. This reasonably allows qualified names to be used in the macro. +fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool { + if let syn::Type::Path(ty) = ty { + ty.path + .segments + .iter() + .map(|s| s.ident.to_string()) + .rev() + .zip(name.iter().rev()) + .all(|(x, y)| &x.as_str() == y) + } else { + false + } +} + +/// The central struct for constructing the as_error method from an annotated struct. +struct SessionDiagnosticDerive<'a> { + structure: synstructure::Structure<'a>, + builder: SessionDiagnosticDeriveBuilder<'a>, +} + +impl std::convert::From<syn::Error> for SessionDiagnosticDeriveError { + fn from(e: syn::Error) -> Self { + SessionDiagnosticDeriveError::SynError(e) + } +} + +/// Equivalent to rustc:errors::diagnostic::DiagnosticId, except stores the quoted expression to +/// initialise the code with. +enum DiagnosticId { + Error(proc_macro2::TokenStream), + Lint(proc_macro2::TokenStream), +} + +#[derive(Debug)] +enum SessionDiagnosticDeriveError { + SynError(syn::Error), + ErrorHandled, +} + +impl SessionDiagnosticDeriveError { + fn to_compile_error(self) -> proc_macro2::TokenStream { + match self { + SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(), + SessionDiagnosticDeriveError::ErrorHandled => { + // Return ! to avoid having to create a blank DiagnosticBuilder to return when an + // error has already been emitted to the compiler. + quote! { + unreachable!() + } + } + } + } +} + +fn span_err(span: impl proc_macro::MultiSpan, msg: &str) -> proc_macro::Diagnostic { + Diagnostic::spanned(span, proc_macro::Level::Error, msg) +} + +/// For methods that return a Result<_, SessionDiagnosticDeriveError>: emit a diagnostic on +/// span $span with msg $msg (and, optionally, perform additional decoration using the FnOnce +/// passed in `diag`). Then, return Err(ErrorHandled). +macro_rules! throw_span_err { + ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }}; + ($span:expr, $msg:expr, $f:expr) => {{ + return Err(_throw_span_err($span, $msg, $f)); + }}; +} + +/// When possible, prefer using throw_span_err! over using this function directly. This only exists +/// as a function to constrain `f` to an impl FnOnce. +fn _throw_span_err( + span: impl proc_macro::MultiSpan, + msg: &str, + f: impl FnOnce(proc_macro::Diagnostic) -> proc_macro::Diagnostic, +) -> SessionDiagnosticDeriveError { + let diag = span_err(span, msg); + f(diag).emit(); + SessionDiagnosticDeriveError::ErrorHandled +} + +impl<'a> SessionDiagnosticDerive<'a> { + fn new(diag: syn::Ident, sess: syn::Ident, structure: synstructure::Structure<'a>) -> Self { + // Build the mapping of field names to fields. This allows attributes to peek values from + // other fields. + let mut fields_map = HashMap::new(); + + // Convenience bindings. + let ast = structure.ast(); + + if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data { + for field in fields.iter() { + if let Some(ident) = &field.ident { + fields_map.insert(ident.to_string(), field); + } + } + } + + Self { + builder: SessionDiagnosticDeriveBuilder { diag, sess, fields: fields_map, kind: None }, + structure, + } + } + fn into_tokens(self) -> proc_macro2::TokenStream { + let SessionDiagnosticDerive { structure, mut builder } = self; + + let ast = structure.ast(); + let attrs = &ast.attrs; + + let implementation = { + if let syn::Data::Struct(..) = ast.data { + let preamble = { + let preamble = attrs.iter().map(|attr| { + builder + .generate_structure_code(attr) + .unwrap_or_else(|v| v.to_compile_error()) + }); + quote! { + #(#preamble)*; + } + }; + + let body = structure.each(|field_binding| { + let field = field_binding.ast(); + let result = field.attrs.iter().map(|attr| { + builder + .generate_field_code( + attr, + FieldInfo { + vis: &field.vis, + binding: field_binding, + ty: &field.ty, + span: &field.span(), + }, + ) + .unwrap_or_else(|v| v.to_compile_error()) + }); + return quote! { + #(#result);* + }; + }); + // Finally, putting it altogether. + match builder.kind { + None => { + span_err(ast.span().unwrap(), "`code` not specified") + .help("use the [code = \"...\"] attribute to set this diagnostic's error code ") + .emit(); + SessionDiagnosticDeriveError::ErrorHandled.to_compile_error() + } + Some((kind, _)) => match kind { + DiagnosticId::Lint(_lint) => todo!(), + DiagnosticId::Error(code) => { + let (diag, sess) = (&builder.diag, &builder.sess); + quote! { + let mut #diag = #sess.struct_err_with_code("", rustc_errors::DiagnosticId::Error(#code)); + #preamble + match self { + #body + } + #diag + } + } + }, + } + } else { + span_err( + ast.span().unwrap(), + "`#[derive(SessionDiagnostic)]` can only be used on structs", + ) + .emit(); + SessionDiagnosticDeriveError::ErrorHandled.to_compile_error() + } + }; + + let sess = &builder.sess; + structure.gen_impl(quote! { + gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess> + for @Self + { + fn into_diagnostic( + self, + #sess: &'__session_diagnostic_sess rustc_session::Session + ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess> { + #implementation + } + } + }) + } +} + +/// Field information passed to the builder. Deliberately omits attrs to discourage the generate_* +/// methods from walking the attributes themselves. +struct FieldInfo<'a> { + vis: &'a syn::Visibility, + binding: &'a synstructure::BindingInfo<'a>, + ty: &'a syn::Type, + span: &'a proc_macro2::Span, +} + +/// Tracks persistent information required for building up the individual calls to diagnostic +/// methods for the final generated method. This is a separate struct to SessionDerive only to be +/// able to destructure and split self.builder and the self.structure up to avoid a double mut +/// borrow later on. +struct SessionDiagnosticDeriveBuilder<'a> { + /// Name of the session parameter that's passed in to the as_error method. + sess: syn::Ident, + + /// Store a map of field name to its corresponding field. This is built on construction of the + /// derive builder. + fields: HashMap<String, &'a syn::Field>, + + /// The identifier to use for the generated DiagnosticBuilder instance. + diag: syn::Ident, + + /// Whether this is a lint or an error. This dictates how the diag will be initialised. Span + /// stores at what Span the kind was first set at (for error reporting purposes, if the kind + /// was multiply specified). + kind: Option<(DiagnosticId, proc_macro2::Span)>, +} + +impl<'a> SessionDiagnosticDeriveBuilder<'a> { + fn generate_structure_code( + &mut self, + attr: &syn::Attribute, + ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> { + Ok(match attr.parse_meta()? { + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let formatted_str = self.build_format(&s.value(), attr.span()); + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + match name { + "message" => { + let diag = &self.diag; + quote! { + #diag.set_primary_message(#formatted_str); + } + } + attr @ "error" | attr @ "lint" => { + self.set_kind_once( + if attr == "error" { + DiagnosticId::Error(formatted_str) + } else if attr == "lint" { + DiagnosticId::Lint(formatted_str) + } else { + unreachable!() + }, + s.span(), + )?; + // This attribute is only allowed to be applied once, and the attribute + // will be set in the initialisation code. + quote! {} + } + other => throw_span_err!( + attr.span().unwrap(), + &format!( + "`#[{} = ...]` is not a valid SessionDiagnostic struct attribute", + other + ) + ), + } + } + _ => todo!("unhandled meta kind"), + }) + } + + #[must_use] + fn set_kind_once( + &mut self, + kind: DiagnosticId, + span: proc_macro2::Span, + ) -> Result<(), SessionDiagnosticDeriveError> { + if self.kind.is_none() { + self.kind = Some((kind, span)); + Ok(()) + } else { + let kind_str = |kind: &DiagnosticId| match kind { + DiagnosticId::Lint(..) => "lint", + DiagnosticId::Error(..) => "error", + }; + + let existing_kind = kind_str(&self.kind.as_ref().unwrap().0); + let this_kind = kind_str(&kind); + + let msg = if this_kind == existing_kind { + format!("`{}` specified multiple times", existing_kind) + } else { + format!("`{}` specified when `{}` was already specified", this_kind, existing_kind) + }; + throw_span_err!(span.unwrap(), &msg); + } + } + + fn generate_field_code( + &mut self, + attr: &syn::Attribute, + info: FieldInfo<'_>, + ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> { + let field_binding = &info.binding.binding; + + 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), + span: info.span, + }, + )?; + Ok(if option_ty.is_none() { + quote! { #generated_code } + } else { + quote! { + if let Some(#field_binding) = #field_binding { + #generated_code + } + } + }) + } + + fn generate_non_option_field_code( + &mut self, + attr: &syn::Attribute, + info: FieldInfo<'_>, + ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> { + let diag = &self.diag; + let field_binding = &info.binding.binding; + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + // At this point, we need to dispatch based on the attribute key + the + // type. + let meta = attr.parse_meta()?; + Ok(match meta { + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let formatted_str = self.build_format(&s.value(), attr.span()); + match name { + "message" => { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + quote! { + #diag.set_span(*#field_binding); + #diag.set_primary_message(#formatted_str); + } + } else { + throw_span_err!( + attr.span().unwrap(), + "the `#[message = \"...\"]` attribute can only be applied to fields of type Span" + ); + } + } + "label" => { + if type_matches_path(&info.ty, &["rustc_span", "Span"]) { + quote! { + #diag.span_label(*#field_binding, #formatted_str); + } + } else { + throw_span_err!( + attr.span().unwrap(), + "The `#[label = ...]` attribute can only be applied to fields of type Span" + ); + } + } + other => throw_span_err!( + attr.span().unwrap(), + &format!( + "`#[{} = ...]` is not a valid SessionDiagnostic field attribute", + other + ) + ), + } + } + syn::Meta::List(list) => { + match list.path.segments.iter().last().unwrap().ident.to_string().as_str() { + suggestion_kind @ "suggestion" + | suggestion_kind @ "suggestion_short" + | suggestion_kind @ "suggestion_hidden" + | suggestion_kind @ "suggestion_verbose" => { + // For suggest, we need to ensure we are running on a (Span, + // Applicability) pair. + let (span, applicability) = (|| match &info.ty { + ty @ syn::Type::Path(..) + if type_matches_path(ty, &["rustc_span", "Span"]) => + { + let binding = &info.binding.binding; + Ok(( + quote!(*#binding), + quote!(rustc_errors::Applicability::Unspecified), + )) + } + syn::Type::Tuple(tup) => { + let mut span_idx = None; + let mut applicability_idx = None; + for (idx, elem) in tup.elems.iter().enumerate() { + if type_matches_path(elem, &["rustc_span", "Span"]) { + if span_idx.is_none() { + span_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.clone().unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more than one Span" + ); + } + } else if type_matches_path( + elem, + &["rustc_errors", "Applicability"], + ) { + if applicability_idx.is_none() { + applicability_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.clone().unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more than one Applicability" + ); + } + } + } + if let Some(span_idx) = span_idx { + let binding = &info.binding.binding; + let span = quote!(#binding.#span_idx); + let applicability = applicability_idx + .map( + |applicability_idx| quote!(#binding.#applicability_idx), + ) + .unwrap_or(quote!( + rustc_errors::Applicability::Unspecified + )); + return Ok((span, applicability)); + } + throw_span_err!( + info.span.clone().unwrap(), + "wrong types for suggestion", + |diag| { + diag.help("#[suggestion(...)] on a tuple field must be applied to fields of type (Span, Applicability)") + } + ); + } + _ => throw_span_err!( + info.span.clone().unwrap(), + "wrong field type for suggestion", + |diag| { + diag.help("#[suggestion(...)] should be applied to fields of type Span or (Span, Applicability)") + } + ), + })()?; + // Now read the key-value pairs. + let mut msg = None; + let mut code = None; + + for arg in list.nested.iter() { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(arg_name_value)) = arg + { + if let syn::MetaNameValue { lit: syn::Lit::Str(s), .. } = + arg_name_value + { + let name = arg_name_value + .path + .segments + .last() + .unwrap() + .ident + .to_string(); + let name = name.as_str(); + let formatted_str = self.build_format(&s.value(), arg.span()); + match name { + "message" => { + msg = Some(formatted_str); + } + "code" => { + code = Some(formatted_str); + } + other => throw_span_err!( + arg.span().unwrap(), + &format!( + "`{}` is not a valid key for `#[suggestion(...)]`", + other + ) + ), + } + } + } + } + let msg = if let Some(msg) = msg { + quote!(#msg.as_str()) + } else { + throw_span_err!( + list.span().unwrap(), + "missing suggestion message", + |diag| { + diag.help("provide a suggestion message using #[suggestion(message = \"...\")]") + } + ); + }; + let code = code.unwrap_or_else(|| quote! { String::new() }); + // Now build it out: + let suggestion_method = format_ident!("span_{}", suggestion_kind); + quote! { + #diag.#suggestion_method(#span, #msg, #code, #applicability); + } + } + other => throw_span_err!( + list.span().unwrap(), + &format!("invalid annotation list `#[{}(...)]`", other) + ), + } + } + _ => panic!("unhandled meta kind"), + }) + } + + /// In the strings in the attributes supplied to this macro, we want callers to be able to + /// reference fields in the format string. Take this, for example: + /// ```ignore (not-usage-example) + /// struct Point { + /// #[error = "Expected a point greater than ({x}, {y})"] + /// x: i32, + /// y: i32, + /// } + /// ``` + /// We want to automatically pick up that {x} refers `self.x` and {y} refers to `self.y`, then + /// generate this call to format!: + /// ```ignore (not-usage-example) + /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y) + /// ``` + /// This function builds the entire call to format!. + fn build_format(&self, input: &String, span: proc_macro2::Span) -> proc_macro2::TokenStream { + // This set is used later to generate the final format string. To keep builds reproducible, + // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead + // of a HashSet. + let mut referenced_fields: BTreeSet<String> = BTreeSet::new(); + + // At this point, we can start parsing the format string. + let mut it = input.chars().peekable(); + // Once the start of a format string has been found, process the format string and spit out + // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so the + // next call to `it.next()` retrieves the next character. + while let Some(c) = it.next() { + if c == '{' && *it.peek().unwrap_or(&'\0') != '{' { + #[must_use] + let mut eat_argument = || -> Option<String> { + let mut result = String::new(); + // Format specifiers look like + // format := '{' [ argument ] [ ':' format_spec ] '}' . + // Therefore, we only need to eat until ':' or '}' to find the argument. + while let Some(c) = it.next() { + result.push(c); + let next = *it.peek().unwrap_or(&'\0'); + if next == '}' { + break; + } else if next == ':' { + // Eat the ':' character. + assert_eq!(it.next().unwrap(), ':'); + break; + } + } + // Eat until (and including) the matching '}' + while it.next()? != '}' { + continue; + } + Some(result) + }; + + if let Some(referenced_field) = eat_argument() { + referenced_fields.insert(referenced_field); + } + } + } + // At this point, `referenced_fields` contains a set of the unique fields that were + // referenced in the format string. Generate the corresponding "x = self.x" format + // string parameters: + let args = referenced_fields.into_iter().map(|field: String| { + let field_ident = format_ident!("{}", field); + let value = if self.fields.contains_key(&field) { + quote! { + &self.#field_ident + } + } else { + // This field doesn't exist. Emit a diagnostic. + Diagnostic::spanned( + span.unwrap(), + proc_macro::Level::Error, + format!("`{}` doesn't refer to a field on this type", field), + ) + .emit(); + quote! { + "{#field}" + } + }; + quote! { + #field_ident = #value + } + }); + quote! { + format!(#input #(,#args)*) + } + } +} + +/// If `ty` is an Option, returns Some(inner type). Else, returns None. +fn option_inner_ty(ty: &syn::Type) -> Option<&syn::Type> { + if type_matches_path(ty, &["std", "option", "Option"]) { + if let syn::Type::Path(ty_path) = ty { + let path = &ty_path.path; + let ty = path.segments.iter().last().unwrap(); + if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments { + if bracketed.args.len() == 1 { + if let syn::GenericArgument::Type(ty) = &bracketed.args[0] { + return Some(ty); + } + } + } + } + } + None +} diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 4b144f94ea7..f1975e78801 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -17,6 +17,7 @@ rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } +rustc_feature = { path = "../rustc_feature" } rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 85490f5f6e9..77766be7397 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,4 +1,4 @@ -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(core_intrinsics)] #![feature(crate_visibility_modifier)] diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 3976475cb06..e76c2cb356f 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -170,7 +170,7 @@ impl Collector<'tcx> { feature_err( &self.tcx.sess.parse_sess, sym::static_nobundle, - span.unwrap_or_else(|| rustc_span::DUMMY_SP), + span.unwrap_or(rustc_span::DUMMY_SP), "kind=\"static-nobundle\" is unstable", ) .emit(); @@ -179,7 +179,7 @@ impl Collector<'tcx> { feature_err( &self.tcx.sess.parse_sess, sym::raw_dylib, - span.unwrap_or_else(|| rustc_span::DUMMY_SP), + span.unwrap_or(rustc_span::DUMMY_SP), "kind=\"raw-dylib\" is unstable", ) .emit(); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 43d76e9fdb4..72d54a26b01 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -11,6 +11,7 @@ use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{AtomicCell, Lock, LockGuard, Lrc, OnceCell}; +use rustc_errors::ErrorReported; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, ProcMacroDerive}; use rustc_hir as hir; @@ -562,6 +563,12 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span { } } +impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [mir::abstract_const::Node<'tcx>] { + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result<Self, String> { + ty::codec::RefDecodable::decode(d) + } +} + impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] { fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result<Self, String> { ty::codec::RefDecodable::decode(d) @@ -1191,6 +1198,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } + fn get_mir_abstract_const( + &self, + tcx: TyCtxt<'tcx>, + id: DefIndex, + ) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> { + self.root + .tables + .mir_abstract_consts + .get(self, id) + .filter(|_| !self.is_proc_macro(id)) + .map_or(Ok(None), |v| Ok(Some(v.decode((self, tcx))))) + } + fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet<u32> { self.root .tables diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 36ff65fc5eb..d4f577a7d1b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::utils::NativeLibKind; use rustc_session::{CrateDisambiguator, Session}; -use rustc_span::source_map::{self, Span, Spanned}; +use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::Symbol; use rustc_data_structures::sync::Lrc; @@ -112,6 +112,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, } optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) } promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) } + mir_abstract_const => { cdata.get_mir_abstract_const(tcx, def_id.index) } unused_generic_params => { cdata.get_unused_generic_params(def_id.index) } mir_const_qualif => { cdata.mir_const_qualif(def_id.index) } fn_sig => { cdata.fn_sig(def_id.index, tcx) } @@ -421,7 +422,11 @@ impl CStore { span, attrs: attrs.to_vec(), kind: ast::ItemKind::MacroDef(data.get_macro(id.index, sess)), - vis: source_map::respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), + vis: ast::Visibility { + span: span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, tokens: None, }, data.root.edition, diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 509ef1caf1a..556cf419920 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -40,6 +40,7 @@ use tracing::{debug, trace}; pub(super) struct EncodeContext<'a, 'tcx> { opaque: opaque::Encoder, tcx: TyCtxt<'tcx>, + feat: &'tcx rustc_feature::Features, tables: TableBuilders<'tcx>, @@ -162,7 +163,7 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnId { impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Span { fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { - if self.is_dummy() { + if *self == rustc_span::DUMMY_SP { return TAG_INVALID_SPAN.encode(s); } @@ -320,6 +321,12 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { } } +impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for &'tcx [mir::abstract_const::Node<'tcx>] { + fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + (**self).encode(s) + } +} + impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] { fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { (**self).encode(s) @@ -1108,6 +1115,11 @@ impl EncodeContext<'a, 'tcx> { if !unused.is_empty() { record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused); } + + let abstract_const = self.tcx.mir_abstract_const(def_id); + if let Ok(Some(abstract_const)) = abstract_const { + record!(self.tables.mir_abstract_consts[def_id.to_def_id()] <- abstract_const); + } } } @@ -1132,15 +1144,25 @@ impl EncodeContext<'a, 'tcx> { fn encode_stability(&mut self, def_id: DefId) { debug!("EncodeContext::encode_stability({:?})", def_id); - if let Some(stab) = self.tcx.lookup_stability(def_id) { - record!(self.tables.stability[def_id] <- stab) + + // The query lookup can take a measurable amount of time in crates with many items. Check if + // the stability attributes are even enabled before using their queries. + if self.feat.staged_api || self.tcx.sess.opts.debugging_opts.force_unstable_if_unmarked { + if let Some(stab) = self.tcx.lookup_stability(def_id) { + record!(self.tables.stability[def_id] <- stab) + } } } fn encode_const_stability(&mut self, def_id: DefId) { debug!("EncodeContext::encode_const_stability({:?})", def_id); - if let Some(stab) = self.tcx.lookup_const_stability(def_id) { - record!(self.tables.const_stability[def_id] <- stab) + + // The query lookup can take a measurable amount of time in crates with many items. Check if + // the stability attributes are even enabled before using their queries. + if self.feat.staged_api || self.tcx.sess.opts.debugging_opts.force_unstable_if_unmarked { + if let Some(stab) = self.tcx.lookup_const_stability(def_id) { + record!(self.tables.const_stability[def_id] <- stab) + } } } @@ -1278,7 +1300,7 @@ impl EncodeContext<'a, 'tcx> { }); record!(self.tables.visibility[def_id] <- ty::Visibility::from_hir(&item.vis, item.hir_id, tcx)); - record!(self.tables.span[def_id] <- item.span); + record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id] <- item.attrs); // FIXME(eddyb) there should be a nicer way to do this. match item.kind { @@ -1418,7 +1440,7 @@ impl EncodeContext<'a, 'tcx> { let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); let ty = self.tcx.typeck(def_id).node_type(hir_id); - record!(self.tables.kind[def_id.to_def_id()] <- match ty.kind { + record!(self.tables.kind[def_id.to_def_id()] <- match ty.kind() { ty::Generator(..) => { let data = self.tcx.generator_kind(def_id).unwrap(); EntryKind::Generator(data) @@ -1432,7 +1454,7 @@ impl EncodeContext<'a, 'tcx> { record!(self.tables.span[def_id.to_def_id()] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id.to_def_id()] <- &self.tcx.get_attrs(def_id.to_def_id())[..]); self.encode_item_type(def_id.to_def_id()); - if let ty::Closure(def_id, substs) = ty.kind { + if let ty::Closure(def_id, substs) = *ty.kind() { record!(self.tables.fn_sig[def_id] <- substs.as_closure().sig()); } self.encode_generics(def_id.to_def_id()); @@ -1979,6 +2001,7 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { let mut ecx = EncodeContext { opaque: encoder, tcx, + feat: tcx.features(), tables: Default::default(), lazy_state: LazyState::NoNode, type_shorthands: Default::default(), diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 1ba5962d119..ba540c94411 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -284,6 +284,7 @@ define_tables! { super_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>, mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>, promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>, + mir_abstract_consts: Table<DefIndex, Lazy!(&'tcx [mir::abstract_const::Node<'tcx>])>, unused_generic_params: Table<DefIndex, Lazy<FiniteBitSet<u32>>>, // `def_keys` and `def_path_hashes` represent a lazy version of a // `DefPathTable`. This allows us to avoid deserializing an entire diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 5a82cbf2997..5136e2743cd 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -26,8 +26,7 @@ rustc_index = { path = "../rustc_index" } rustc_serialize = { path = "../rustc_serialize" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -byteorder = { version = "1.3" } -chalk-ir = "0.14.0" +chalk-ir = "0.28.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } measureme = "0.7.1" rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index 2580ac6bebd..a60a17befef 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -124,6 +124,7 @@ pub struct ConstVariableOrigin { pub enum ConstVariableOriginKind { MiscVariable, ConstInference, + // FIXME(const_generics): Consider storing the `DefId` of the param here. ConstParameterDefinition(Symbol), SubstitutionPlaceholder, } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 1b2dea8a378..fa885ce2e7c 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -22,7 +22,8 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(array_windows)] #![feature(backtrace)] #![feature(bool_to_option)] #![feature(box_patterns)] @@ -30,25 +31,21 @@ #![feature(cmp_min_max_by)] #![feature(const_fn)] #![feature(const_panic)] -#![feature(const_fn_transmute)] #![feature(core_intrinsics)] #![feature(discriminant_kind)] -#![feature(drain_filter)] #![feature(never_type)] -#![feature(exhaustive_patterns)] #![feature(extern_types)] #![feature(nll)] +#![feature(once_cell)] #![feature(option_expect_none)] #![feature(or_patterns)] #![feature(min_specialization)] #![feature(trusted_len)] -#![feature(stmt_expr_attributes)] #![feature(test)] #![feature(in_band_lifetimes)] #![feature(crate_visibility_modifier)] #![feature(associated_type_bounds)] #![feature(rustc_attrs)] -#![feature(hash_raw_entry)] #![feature(int_error_matching)] #![recursion_limit = "512"] diff --git a/compiler/rustc_middle/src/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs index 1af1d581817..f3d7c8506ab 100644 --- a/compiler/rustc_middle/src/middle/cstore.rs +++ b/compiler/rustc_middle/src/middle/cstore.rs @@ -69,7 +69,7 @@ pub enum LibSource { impl LibSource { pub fn is_some(&self) -> bool { - if let LibSource::Some(_) = *self { true } else { false } + matches!(self, LibSource::Some(_)) } pub fn option(&self) -> Option<PathBuf> { diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index 7194a035e89..cc9706f2d86 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -17,7 +17,7 @@ use rustc_target::spec::PanicStrategy; impl<'tcx> TyCtxt<'tcx> { /// Returns the `DefId` for a given `LangItem`. /// If not found, fatally aborts compilation. - pub fn require_lang_item(&self, lang_item: LangItem, span: Option<Span>) -> DefId { + pub fn require_lang_item(self, lang_item: LangItem, span: Option<Span>) -> DefId { self.lang_items().require(lang_item).unwrap_or_else(|msg| { if let Some(span) = span { self.sess.span_fatal(span, &msg) @@ -27,7 +27,7 @@ impl<'tcx> TyCtxt<'tcx> { }) } - pub fn fn_trait_kind_from_lang_item(&self, id: DefId) -> Option<ty::ClosureKind> { + pub fn fn_trait_kind_from_lang_item(self, id: DefId) -> Option<ty::ClosureKind> { let items = self.lang_items(); match Some(id) { x if x == items.fn_trait() => Some(ty::ClosureKind::Fn), @@ -37,7 +37,7 @@ impl<'tcx> TyCtxt<'tcx> { } } - pub fn is_weak_lang_item(&self, item_def_id: DefId) -> bool { + pub fn is_weak_lang_item(self, item_def_id: DefId) -> bool { self.lang_items().is_weak_lang_item(item_def_id) } } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index b32eebbb11e..27658d50d45 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -13,6 +13,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc_hir::{self, HirId}; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; use rustc_session::lint::{BuiltinLintDiagnostics, Lint, LintBuffer}; use rustc_session::parse::feature_err_issue; @@ -308,7 +309,7 @@ impl<'tcx> TyCtxt<'tcx> { // #[rustc_deprecated] however wants to emit down the whole // hierarchy. if !skip || depr_entry.attr.is_since_rustc_version { - let path = &self.def_path_str(def_id); + let path = &with_no_trimmed_paths(|| self.def_path_str(def_id)); let kind = self.def_kind(def_id).descr(def_id); let (message, lint) = deprecation_message(&depr_entry.attr, kind, path); late_report_deprecation( diff --git a/compiler/rustc_middle/src/mir/abstract_const.rs b/compiler/rustc_middle/src/mir/abstract_const.rs new file mode 100644 index 00000000000..b85f1e6e5de --- /dev/null +++ b/compiler/rustc_middle/src/mir/abstract_const.rs @@ -0,0 +1,20 @@ +//! A subset of a mir body used for const evaluatability checking. +use crate::mir; +use crate::ty; + +rustc_index::newtype_index! { + /// An index into an `AbstractConst`. + pub struct NodeId { + derive [HashStable] + DEBUG_FORMAT = "n{}", + } +} + +/// A node of an `AbstractConst`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum Node<'tcx> { + Leaf(&'tcx ty::Const<'tcx>), + Binop(mir::BinOp, NodeId, NodeId), + UnaryOp(mir::UnOp, NodeId), + FunctionCall(NodeId, &'tcx [NodeId]), +} diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 505939d56ed..ee1ea816e01 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -345,10 +345,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { /// Reads a *non-ZST* scalar. /// - /// ZSTs can't be read for two reasons: - /// * byte-order cannot work with zero-element buffers; - /// * in order to obtain a `Pointer`, we need to check for ZSTness anyway due to integer - /// pointers being valid for ZSTs. + /// ZSTs can't be read because in order to obtain a `Pointer`, we need to check + /// for ZSTness anyway due to integer pointers being valid for ZSTs. /// /// It is the caller's responsibility to check bounds and alignment beforehand. /// Most likely, you want to call `InterpCx::read_scalar` instead of this method. @@ -397,10 +395,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { /// Writes a *non-ZST* scalar. /// - /// ZSTs can't be read for two reasons: - /// * byte-order cannot work with zero-element buffers; - /// * in order to obtain a `Pointer`, we need to check for ZSTness anyway due to integer - /// pointers being valid for ZSTs. + /// ZSTs can't be read because in order to obtain a `Pointer`, we need to check + /// for ZSTness anyway due to integer pointers being valid for ZSTs. /// /// It is the caller's responsibility to check bounds and alignment beforehand. /// Most likely, you want to call `InterpCx::write_scalar` instead of this method. diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 059925088ce..d41e5680602 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -1,4 +1,4 @@ -use super::{AllocId, Pointer, RawConst, Scalar}; +use super::{AllocId, ConstAlloc, Pointer, Scalar}; use crate::mir::interpret::ConstValue; use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty}; @@ -23,12 +23,18 @@ pub enum ErrorHandled { TooGeneric, } +impl From<ErrorReported> for ErrorHandled { + fn from(err: ErrorReported) -> ErrorHandled { + ErrorHandled::Reported(err) + } +} + CloneTypeFoldableAndLiftImpls! { ErrorHandled, } -pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>; -pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>; +pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>; +pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>; pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> { struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg) diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 0dc3d6e344a..20363625e42 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -98,16 +98,17 @@ mod value; use std::convert::TryFrom; use std::fmt; use std::io; +use std::io::{Read, Write}; use std::num::NonZeroU32; use std::sync::atomic::{AtomicU32, Ordering}; -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{HashMapExt, Lock}; use rustc_data_structures::tiny_list::TinyList; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_serialize::{Decodable, Encodable}; use rustc_target::abi::{Endian, Size}; @@ -117,12 +118,12 @@ use crate::ty::subst::GenericArgKind; use crate::ty::{self, Instance, Ty, TyCtxt}; pub use self::error::{ - struct_error, CheckInAllocMsg, ConstEvalRawResult, ConstEvalResult, ErrorHandled, InterpError, - InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, ResourceExhaustionInfo, - UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo, + struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, + InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, + ResourceExhaustionInfo, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo, }; -pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUninit}; +pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar, ScalarMaybeUninit}; pub use self::allocation::{Allocation, AllocationExtra, InitMask, Relocations}; @@ -145,7 +146,7 @@ pub struct GlobalId<'tcx> { impl GlobalId<'tcx> { pub fn display(self, tcx: TyCtxt<'tcx>) -> String { - let instance_name = tcx.def_path_str(self.instance.def.def_id()); + let instance_name = with_no_trimmed_paths(|| tcx.def_path_str(self.instance.def.def_id())); if let Some(promoted) = self.promoted { format!("{}::{:?}", instance_name, promoted) } else { @@ -446,14 +447,14 @@ impl<'tcx> TyCtxt<'tcx> { /// /// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such /// an `AllocId` from a query. - pub fn reserve_alloc_id(&self) -> AllocId { + pub fn reserve_alloc_id(self) -> AllocId { self.alloc_map.lock().reserve() } /// Reserves a new ID *if* this allocation has not been dedup-reserved before. /// Should only be used for function pointers and statics, we don't want /// to dedup IDs for "real" memory! - fn reserve_and_set_dedup(&self, alloc: GlobalAlloc<'tcx>) -> AllocId { + fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId { let mut alloc_map = self.alloc_map.lock(); match alloc { GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {} @@ -471,13 +472,13 @@ impl<'tcx> TyCtxt<'tcx> { /// Generates an `AllocId` for a static or return a cached one in case this function has been /// called on the same static before. - pub fn create_static_alloc(&self, static_id: DefId) -> AllocId { + pub fn create_static_alloc(self, static_id: DefId) -> AllocId { self.reserve_and_set_dedup(GlobalAlloc::Static(static_id)) } /// Generates an `AllocId` for a function. Depending on the function type, /// this might get deduplicated or assigned a new ID each time. - pub fn create_fn_alloc(&self, instance: Instance<'tcx>) -> AllocId { + pub fn create_fn_alloc(self, instance: Instance<'tcx>) -> AllocId { // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be // duplicated across crates. @@ -506,7 +507,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Statics with identical content will still point to the same `Allocation`, i.e., /// their data will be deduplicated through `Allocation` interning -- but they /// are different places in memory and as such need different IDs. - pub fn create_memory_alloc(&self, mem: &'tcx Allocation) -> AllocId { + pub fn create_memory_alloc(self, mem: &'tcx Allocation) -> AllocId { let id = self.reserve_alloc_id(); self.set_alloc_id_memory(id, mem); id @@ -518,7 +519,7 @@ impl<'tcx> TyCtxt<'tcx> { /// This function exists to allow const eval to detect the difference between evaluation- /// local dangling pointers and allocations in constants/statics. #[inline] - pub fn get_global_alloc(&self, id: AllocId) -> Option<GlobalAlloc<'tcx>> { + pub fn get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> { self.alloc_map.lock().alloc_map.get(&id).cloned() } @@ -528,7 +529,7 @@ impl<'tcx> TyCtxt<'tcx> { /// constants (as all constants must pass interning and validation that check for dangling /// ids), this function is frequently used throughout rustc, but should not be used within /// the miri engine. - pub fn global_alloc(&self, id: AllocId) -> GlobalAlloc<'tcx> { + pub fn global_alloc(self, id: AllocId) -> GlobalAlloc<'tcx> { match self.get_global_alloc(id) { Some(alloc) => alloc, None => bug!("could not find allocation for {}", id), @@ -537,7 +538,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to /// call this function twice, even with the same `Allocation` will ICE the compiler. - pub fn set_alloc_id_memory(&self, id: AllocId, mem: &'tcx Allocation) { + pub fn set_alloc_id_memory(self, id: AllocId, mem: &'tcx Allocation) { if let Some(old) = self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Memory(mem)) { bug!("tried to set allocation ID {}, but it was already existing as {:#?}", id, old); } @@ -545,7 +546,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called /// twice for the same `(AllocId, Allocation)` pair. - fn set_alloc_id_same_memory(&self, id: AllocId, mem: &'tcx Allocation) { + fn set_alloc_id_same_memory(self, id: AllocId, mem: &'tcx Allocation) { self.alloc_map.lock().alloc_map.insert_same(id, GlobalAlloc::Memory(mem)); } } @@ -560,19 +561,33 @@ pub fn write_target_uint( mut target: &mut [u8], data: u128, ) -> Result<(), io::Error> { - let len = target.len(); + // This u128 holds an "any-size uint" (since smaller uints can fits in it) + // So we do not write all bytes of the u128, just the "payload". match endianness { - Endian::Little => target.write_uint128::<LittleEndian>(data, len), - Endian::Big => target.write_uint128::<BigEndian>(data, len), - } + Endian::Little => target.write(&data.to_le_bytes())?, + Endian::Big => target.write(&data.to_be_bytes()[16 - target.len()..])?, + }; + debug_assert!(target.len() == 0); // We should have filled the target buffer. + Ok(()) } #[inline] pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result<u128, io::Error> { - match endianness { - Endian::Little => source.read_uint128::<LittleEndian>(source.len()), - Endian::Big => source.read_uint128::<BigEndian>(source.len()), - } + // This u128 holds an "any-size uint" (since smaller uints can fits in it) + let mut buf = [0u8; std::mem::size_of::<u128>()]; + // So we do not read exactly 16 bytes into the u128, just the "payload". + let uint = match endianness { + Endian::Little => { + source.read(&mut buf)?; + Ok(u128::from_le_bytes(buf)) + } + Endian::Big => { + source.read(&mut buf[16 - source.len()..])?; + Ok(u128::from_be_bytes(buf)) + } + }; + debug_assert!(source.len() == 0); // We should have consumed the source buffer. + uint } //////////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index dcc1f8b1a4b..f366681bc75 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -1,4 +1,4 @@ -use super::{ConstEvalResult, ErrorHandled, GlobalId}; +use super::{ErrorHandled, EvalToConstValueResult, GlobalId}; use crate::mir; use crate::ty::subst::{InternalSubsts, SubstsRef}; @@ -10,7 +10,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Evaluates a constant without providing any substitutions. This is useful to evaluate consts /// that can't take any generic arguments like statics, const items or enum discriminants. If a /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned. - pub fn const_eval_poly(self, def_id: DefId) -> ConstEvalResult<'tcx> { + pub fn const_eval_poly(self, def_id: DefId) -> EvalToConstValueResult<'tcx> { // In some situations def_id will have substitutions within scope, but they aren't allowed // to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions // into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are @@ -38,7 +38,7 @@ impl<'tcx> TyCtxt<'tcx> { substs: SubstsRef<'tcx>, promoted: Option<mir::Promoted>, span: Option<Span>, - ) -> ConstEvalResult<'tcx> { + ) -> EvalToConstValueResult<'tcx> { match ty::Instance::resolve_opt_const_arg(self, param_env, def, substs) { Ok(Some(instance)) => { let cid = GlobalId { instance, promoted }; @@ -54,7 +54,7 @@ impl<'tcx> TyCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, instance: ty::Instance<'tcx>, span: Option<Span>, - ) -> ConstEvalResult<'tcx> { + ) -> EvalToConstValueResult<'tcx> { self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span) } @@ -64,14 +64,14 @@ impl<'tcx> TyCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, cid: GlobalId<'tcx>, span: Option<Span>, - ) -> ConstEvalResult<'tcx> { + ) -> EvalToConstValueResult<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. let inputs = self.erase_regions(¶m_env.and(cid)); if let Some(span) = span { - self.at(span).const_eval_validated(inputs) + self.at(span).eval_to_const_value_raw(inputs) } else { - self.const_eval_validated(inputs) + self.eval_to_const_value_raw(inputs) } } @@ -94,7 +94,7 @@ impl<'tcx> TyCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> Result<&'tcx mir::Allocation, ErrorHandled> { trace!("eval_to_allocation: Need to compute {:?}", gid); - let raw_const = self.const_eval_raw(param_env.and(gid))?; + let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?; Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory()) } } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 7d6ff3eb5c1..206f01c2498 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -12,9 +12,9 @@ use crate::ty::{ParamEnv, Ty, TyCtxt}; use super::{sign_extend, truncate, AllocId, Allocation, InterpResult, Pointer, PointerArithmetic}; -/// Represents the result of a raw const operation, pre-validation. -#[derive(Clone, HashStable)] -pub struct RawConst<'tcx> { +/// Represents the result of const evaluation via the `eval_to_allocation` query. +#[derive(Clone, HashStable, TyEncodable, TyDecodable)] +pub struct ConstAlloc<'tcx> { // the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory` // (so you can use `AllocMap::unwrap_memory`). pub alloc_id: AllocId, @@ -578,6 +578,9 @@ pub enum ScalarMaybeUninit<Tag = ()> { Uninit, } +#[cfg(target_arch = "x86_64")] +static_assert_size!(ScalarMaybeUninit, 24); + impl<Tag> From<Scalar<Tag>> for ScalarMaybeUninit<Tag> { #[inline(always)] fn from(s: Scalar<Tag>) -> Self { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 785a7f0c51a..8ff75bf392e 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -40,6 +40,7 @@ use std::{iter, mem, option}; use self::predecessors::{PredecessorCache, Predecessors}; pub use self::query::*; +pub mod abstract_const; pub mod coverage; pub mod interpret; pub mod mono; @@ -186,6 +187,23 @@ pub struct Body<'tcx> { /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components. pub ignore_interior_mut_in_const_validation: bool, + /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check. + /// + /// Note that this does not actually mean that this body is not computable right now. + /// The repeat count in the following example is polymorphic, but can still be evaluated + /// without knowing anything about the type parameter `T`. + /// + /// ```rust + /// fn test<T>() { + /// let _ = [0; std::mem::size_of::<*mut T>()]; + /// } + /// ``` + /// + /// **WARNING**: Do not change this flags after the MIR was originally created, even if an optimization + /// removed the last mention of all generic params. We do not want to rely on optimizations and + /// potentially allow things like `[u8; std::mem::size_of::<T>() * 0]` due to this. + pub is_polymorphic: bool, + predecessor_cache: PredecessorCache, } @@ -208,7 +226,7 @@ impl<'tcx> Body<'tcx> { local_decls.len() ); - Body { + let mut body = Body { phase: MirPhase::Build, basic_blocks, source_scopes, @@ -224,8 +242,11 @@ impl<'tcx> Body<'tcx> { span, required_consts: Vec::new(), ignore_interior_mut_in_const_validation: false, + is_polymorphic: false, predecessor_cache: PredecessorCache::new(), - } + }; + body.is_polymorphic = body.has_param_types_or_consts(); + body } /// Returns a partially initialized MIR body containing only a list of basic blocks. @@ -234,7 +255,7 @@ impl<'tcx> Body<'tcx> { /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different /// crate. pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self { - Body { + let mut body = Body { phase: MirPhase::Build, basic_blocks, source_scopes: IndexVec::new(), @@ -250,8 +271,11 @@ impl<'tcx> Body<'tcx> { generator_kind: None, var_debug_info: Vec::new(), ignore_interior_mut_in_const_validation: false, + is_polymorphic: false, predecessor_cache: PredecessorCache::new(), - } + }; + body.is_polymorphic = body.has_param_types_or_consts(); + body } #[inline] @@ -899,6 +923,8 @@ pub enum LocalInfo<'tcx> { User(ClearCrossCrate<BindingForm<'tcx>>), /// A temporary created that references the static with the given `DefId`. StaticRef { def_id: DefId, is_thread_local: bool }, + /// A temporary created that references the const with the given `DefId` + ConstRef { def_id: DefId }, } impl<'tcx> LocalDecl<'tcx> { @@ -1051,6 +1077,25 @@ pub struct VarDebugInfo<'tcx> { // BasicBlock rustc_index::newtype_index! { + /// A node in the MIR [control-flow graph][CFG]. + /// + /// There are no branches (e.g., `if`s, function calls, etc.) within a basic block, which makes + /// it easier to do [data-flow analyses] and optimizations. Instead, branches are represented + /// as an edge in a graph between basic blocks. + /// + /// Basic blocks consist of a series of [statements][Statement], ending with a + /// [terminator][Terminator]. Basic blocks can have multiple predecessors and successors, + /// however there is a MIR pass ([`CriticalCallEdges`]) that removes *critical edges*, which + /// are edges that go from a multi-successor node to a multi-predecessor node. This pass is + /// needed because some analyses require that there are no critical edges in the CFG. + /// + /// Read more about basic blocks in the [rustc-dev-guide][guide-mir]. + /// + /// [CFG]: https://rustc-dev-guide.rust-lang.org/appendix/background.html#cfg + /// [data-flow analyses]: + /// https://rustc-dev-guide.rust-lang.org/appendix/background.html#what-is-a-dataflow-analysis + /// [`CriticalCallEdges`]: ../../rustc_mir/transform/add_call_guards/enum.AddCallGuards.html#variant.CriticalCallEdges + /// [guide-mir]: https://rustc-dev-guide.rust-lang.org/mir/ pub struct BasicBlock { derive [HashStable] DEBUG_FORMAT = "bb{}", @@ -1067,6 +1112,7 @@ impl BasicBlock { /////////////////////////////////////////////////////////////////////////// // BasicBlockData and Terminator +/// See [`BasicBlock`] for documentation on what basic blocks are at a high level. #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub struct BasicBlockData<'tcx> { /// List of statements in this block. @@ -1530,7 +1576,24 @@ impl Debug for Statement<'_> { AscribeUserType(box (ref place, ref c_ty), ref variance) => { write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) } - Coverage(box ref coverage) => write!(fmt, "{:?}", coverage), + Coverage(box ref coverage) => { + let rgn = &coverage.code_region; + match coverage.kind { + CoverageKind::Counter { id, .. } => { + write!(fmt, "Coverage::Counter({:?}) for {:?}", id.index(), rgn) + } + CoverageKind::Expression { id, lhs, op, rhs } => write!( + fmt, + "Coverage::Expression({:?}) = {} {} {} for {:?}", + id.index(), + lhs.index(), + if op == coverage::Op::Add { "+" } else { "-" }, + rhs.index(), + rgn + ), + CoverageKind::Unreachable => write!(fmt, "Coverage::Unreachable for {:?}", rgn), + } + } Nop => write!(fmt, "nop"), } } @@ -1937,6 +2000,15 @@ impl<'tcx> Operand<'tcx> { Operand::Constant(_) => None, } } + + /// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a + /// place. + pub fn constant(&self) -> Option<&Constant<'tcx>> { + match self { + Operand::Constant(x) => Some(&**x), + Operand::Copy(_) | Operand::Move(_) => None, + } + } } /////////////////////////////////////////////////////////////////////////// @@ -2234,8 +2306,8 @@ impl<'tcx> Debug for Rvalue<'tcx> { /// Constants /// /// Two constants are equal if they are the same constant. Note that -/// this does not necessarily mean that they are "==" in Rust -- in -/// particular one must be wary of `NaN`! +/// this does not necessarily mean that they are `==` in Rust. In +/// particular, one must be wary of `NaN`! #[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, HashStable)] pub struct Constant<'tcx> { @@ -2465,7 +2537,7 @@ impl<'tcx> Debug for Constant<'tcx> { impl<'tcx> Display for Constant<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - match self.literal.ty.kind { + match self.literal.ty.kind() { ty::FnDef(..) => {} _ => write!(fmt, "const ")?, } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 0d5f6619df5..79e2c5aac23 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -1,6 +1,5 @@ use crate::dep_graph::{DepConstructor, DepNode, WorkProduct, WorkProductId}; use crate::ich::{NodeIdHashingMode, StableHashingContext}; -use crate::ty::print::obsolete::DefPathBasedNames; use crate::ty::{subst::InternalSubsts, Instance, InstanceDef, SymbolName, TyCtxt}; use rustc_attr::InlineAttr; use rustc_data_structures::base_n; @@ -86,7 +85,7 @@ impl<'tcx> MonoItem<'tcx> { .debugging_opts .inline_in_all_cgus .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No) - && tcx.sess.opts.cg.link_dead_code != Some(true); + && !tcx.sess.link_dead_code(); match *self { MonoItem::Fn(ref instance) => { @@ -171,30 +170,6 @@ impl<'tcx> MonoItem<'tcx> { !tcx.subst_and_check_impossible_predicates((def_id, &substs)) } - pub fn to_string(&self, tcx: TyCtxt<'tcx>, debug: bool) -> String { - return match *self { - MonoItem::Fn(instance) => to_string_internal(tcx, "fn ", instance, debug), - MonoItem::Static(def_id) => { - let instance = Instance::new(def_id, tcx.intern_substs(&[])); - to_string_internal(tcx, "static ", instance, debug) - } - MonoItem::GlobalAsm(..) => "global_asm".to_string(), - }; - - fn to_string_internal<'tcx>( - tcx: TyCtxt<'tcx>, - prefix: &str, - instance: Instance<'tcx>, - debug: bool, - ) -> String { - let mut result = String::with_capacity(32); - result.push_str(prefix); - let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_instance_as_string(instance, &mut result, debug); - result - } - } - pub fn local_span(&self, tcx: TyCtxt<'tcx>) -> Option<Span> { match *self { MonoItem::Fn(Instance { def, .. }) => { @@ -229,6 +204,18 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for MonoItem<'tcx> { } } +impl<'tcx> fmt::Display for MonoItem<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + MonoItem::Fn(instance) => write!(f, "fn {}", instance), + MonoItem::Static(def_id) => { + write!(f, "static {}", Instance::new(def_id, InternalSubsts::empty())) + } + MonoItem::GlobalAsm(..) => write!(f, "global_asm"), + } + } +} + pub struct CodegenUnit<'tcx> { /// A name for this CGU. Incremental compilation requires that /// name be unique amongst **all** crates. Therefore, it should diff --git a/compiler/rustc_middle/src/mir/predecessors.rs b/compiler/rustc_middle/src/mir/predecessors.rs index b16a1d53fff..a8b74883355 100644 --- a/compiler/rustc_middle/src/mir/predecessors.rs +++ b/compiler/rustc_middle/src/mir/predecessors.rs @@ -33,7 +33,7 @@ impl PredecessorCache { self.cache = OnceCell::new(); } - /// Returns the the predecessor graph for this MIR. + /// Returns the predecessor graph for this MIR. #[inline] pub(super) fn compute( &self, diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index efcd41e5c18..b9e4f6fb12e 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -33,7 +33,7 @@ impl<'tcx> PlaceTy<'tcx> { /// /// Note that the resulting type has not been normalized. pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: &Field) -> Ty<'tcx> { - let answer = match self.ty.kind { + let answer = match self.ty.kind() { ty::Adt(adt_def, substs) => { let variant_def = match self.variant_index { None => adt_def.non_enum_variant(), @@ -90,7 +90,7 @@ impl<'tcx> PlaceTy<'tcx> { PlaceTy::from_ty(self.ty.builtin_index().unwrap()) } ProjectionElem::Subslice { from, to, from_end } => { - PlaceTy::from_ty(match self.ty.kind { + PlaceTy::from_ty(match self.ty.kind() { ty::Slice(..) => self.ty, ty::Array(inner, _) if !from_end => tcx.mk_array(inner, (to - from) as u64), ty::Array(inner, size) if from_end => { diff --git a/compiler/rustc_middle/src/mir/terminator/mod.rs b/compiler/rustc_middle/src/mir/terminator/mod.rs index fcfd648c2b7..8909f02270c 100644 --- a/compiler/rustc_middle/src/mir/terminator/mod.rs +++ b/compiler/rustc_middle/src/mir/terminator/mod.rs @@ -96,6 +96,8 @@ pub enum TerminatorKind<'tcx> { /// P <- V /// } /// ``` + /// + /// Note that DropAndReplace is eliminated as part of the `ElaborateDrops` pass. DropAndReplace { place: Place<'tcx>, value: Operand<'tcx>, diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index 6bb6abe0289..ad2eae0298c 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -175,7 +175,7 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { use crate::mir::Rvalue::*; match *self { Use(ref op) => Use(op.fold_with(folder)), - Repeat(ref op, len) => Repeat(op.fold_with(folder), len), + Repeat(ref op, len) => Repeat(op.fold_with(folder), len.fold_with(folder)), ThreadLocalRef(did) => ThreadLocalRef(did.fold_with(folder)), Ref(region, bk, ref place) => { Ref(region.fold_with(folder), bk, place.fold_with(folder)) diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 6515ae31b46..a008bd5f75f 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1150,8 +1150,6 @@ pub enum NonUseContext { StorageDead, /// User type annotation assertions for NLL. AscribeUserTy, - /// Coverage code region and counter metadata. - Coverage, /// The data of an user variable, for debug info. VarDebugInfo, } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index e05752f08f6..9c047cbfaef 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -42,48 +42,48 @@ rustc_queries! { } Other { - // Represents crate as a whole (as distinct from the top-level crate module). - // If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`), - // we will have to assume that any change means that you need to be recompiled. - // This is because the `hir_crate` query gives you access to all other items. - // To avoid this fate, do not call `tcx.hir().krate()`; instead, - // prefer wrappers like `tcx.visit_all_items_in_krate()`. + /// Represents crate as a whole (as distinct from the top-level crate module). + /// If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`), + /// we will have to assume that any change means that you need to be recompiled. + /// This is because the `hir_crate` query gives you access to all other items. + /// To avoid this fate, do not call `tcx.hir().krate()`; instead, + /// prefer wrappers like `tcx.visit_all_items_in_krate()`. query hir_crate(key: CrateNum) -> &'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. + /// The indexed HIR. This can be conveniently accessed by `tcx.hir()`. + /// Avoid calling this query directly. query index_hir(_: CrateNum) -> &'tcx map::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`. - // Avoid calling this query directly. + /// The items in a module. + /// + /// This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`. + /// Avoid calling this query directly. query hir_module_items(key: LocalDefId) -> &'tcx hir::ModuleItems { eval_always desc { |tcx| "HIR module items in `{}`", tcx.def_path_str(key.to_def_id()) } } - // Gives access to the HIR node for the HIR owner `key`. - // - // This can be conveniently accessed by methods on `tcx.hir()`. - // Avoid calling this query directly. + /// Gives access to the HIR node for the HIR owner `key`. + /// + /// This can be conveniently accessed by methods on `tcx.hir()`. + /// Avoid calling this query directly. query hir_owner(key: LocalDefId) -> Option<&'tcx crate::hir::Owner<'tcx>> { eval_always desc { |tcx| "HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) } } - // Gives access to the HIR nodes and bodies inside the HIR owner `key`. - // - // This can be conveniently accessed by methods on `tcx.hir()`. - // Avoid calling this query directly. + /// Gives access to the HIR nodes and bodies inside the HIR owner `key`. + /// + /// 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 desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } @@ -244,6 +244,35 @@ rustc_queries! { no_hash } + /// Try to build an abstract representation of the given constant. + query mir_abstract_const( + key: DefId + ) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> { + desc { + |tcx| "building an abstract representation for {}", tcx.def_path_str(key), + } + } + /// Try to build an abstract representation of the given constant. + query mir_abstract_const_of_const_arg( + key: (LocalDefId, DefId) + ) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> { + desc { + |tcx| + "building an abstract representation for the const argument {}", + tcx.def_path_str(key.0.to_def_id()), + } + } + + query try_unify_abstract_consts(key: ( + (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>), + (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>) + )) -> bool { + desc { + |tcx| "trying to unify the generic constants {} and {}", + tcx.def_path_str(key.0.0.did), tcx.def_path_str(key.1.0.did) + } + } + query mir_drops_elaborated_and_const_checked( key: ty::WithOptConstParam<LocalDefId> ) -> &'tcx Steal<mir::Body<'tcx>> { @@ -305,9 +334,9 @@ rustc_queries! { } TypeChecking { - // Erases regions from `ty` to yield a new type. - // Normally you would just use `tcx.erase_regions(&value)`, - // however, which uses this query as a kind of cache. + /// Erases regions from `ty` to yield a new type. + /// Normally you would just use `tcx.erase_regions(&value)`, + /// however, which uses this query as a kind of cache. query erase_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { // This query is not expected to have input -- as a result, it // is not a good candidates for "replay" because it is essentially a @@ -678,38 +707,31 @@ rustc_queries! { } Other { - /// Evaluates a constant without running sanity checks. + /// Evaluates a constant and returns the computed allocation. /// - /// **Do not use this** outside const eval. Const eval uses this to break query cycles - /// during validation. Please add a comment to every use site explaining why using - /// `const_eval_validated` isn't sufficient. The returned constant also isn't in a suitable - /// form to be used outside of const eval. - query const_eval_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) - -> ConstEvalRawResult<'tcx> { + /// **Do not use this** directly, use the `tcx.eval_static_initializer` wrapper. + query eval_to_allocation_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + -> EvalToAllocationRawResult<'tcx> { desc { |tcx| - "const-evaluating `{}`", + "const-evaluating + checking `{}`", key.value.display(tcx) } + cache_on_disk_if { true } } - /// Results of evaluating const items or constants embedded in - /// other items (such as enum variant explicit discriminants). - /// - /// In contrast to `const_eval_raw` this performs some validation on the constant, and - /// returns a proper constant that is usable by the rest of the compiler. + /// Evaluates const items or anonymous constants + /// (such as enum variant explicit discriminants or array lengths) + /// into a representation suitable for the type system and const generics. /// /// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`, /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`. - query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) - -> ConstEvalResult<'tcx> { + query eval_to_const_value_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + -> EvalToConstValueResult<'tcx> { desc { |tcx| - "const-evaluating + checking `{}`", + "simplifying constant for the type system `{}`", key.value.display(tcx) } - cache_on_disk_if(_, opt_result) { - // Only store results without errors - opt_result.map_or(true, |r| r.is_ok()) - } + cache_on_disk_if { true } } /// Destructure a constant ADT or array into its variant index and its @@ -1255,6 +1277,11 @@ rustc_queries! { storage(ArenaCacheSelector<'tcx>) desc { "calculating the visible parent map" } } + query trimmed_def_paths(_: CrateNum) + -> FxHashMap<DefId, Symbol> { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating trimmed def paths" } + } query missing_extern_crate_item(_: CrateNum) -> bool { eval_always desc { "seeing if we're missing an `extern crate` item for this crate" } @@ -1394,7 +1421,7 @@ rustc_queries! { } query evaluate_goal( - goal: traits::ChalkCanonicalGoal<'tcx> + goal: traits::CanonicalChalkEnvironmentAndGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, NoSolution @@ -1509,7 +1536,7 @@ rustc_queries! { desc { "looking up supported target features" } } - // Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. + /// Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. query instance_def_size_estimate(def: ty::InstanceDef<'tcx>) -> usize { desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) } diff --git a/compiler/rustc_middle/src/traits/chalk.rs b/compiler/rustc_middle/src/traits/chalk.rs index 405af8cb240..d8507d08c1b 100644 --- a/compiler/rustc_middle/src/traits/chalk.rs +++ b/compiler/rustc_middle/src/traits/chalk.rs @@ -6,14 +6,11 @@ //! interned Chalk types. use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; +use rustc_middle::ty::{self, AdtDef, TyCtxt}; use rustc_hir::def_id::DefId; use rustc_target::spec::abi::Abi; -use smallvec::SmallVec; - use std::cmp::Ordering; use std::fmt; use std::hash::{Hash, Hasher}; @@ -75,6 +72,7 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>; type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>; type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>; + type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>; type DefId = DefId; type InternedAdtId = &'tcx AdtDef; type Identifier = (); @@ -108,8 +106,42 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { application_ty: &chalk_ir::ApplicationTy<Self>, fmt: &mut fmt::Formatter<'_>, ) -> Option<fmt::Result> { - let chalk_ir::ApplicationTy { name, substitution } = application_ty; - Some(write!(fmt, "{:?}{:?}", name, chalk_ir::debug::Angle(substitution.interned()))) + match application_ty.name { + chalk_ir::TypeName::Ref(mutbl) => { + let data = application_ty.substitution.interned(); + match (&**data[0].interned(), &**data[1].interned()) { + ( + chalk_ir::GenericArgData::Lifetime(lifetime), + chalk_ir::GenericArgData::Ty(ty), + ) => Some(match mutbl { + chalk_ir::Mutability::Not => write!(fmt, "(&{:?} {:?})", lifetime, ty), + chalk_ir::Mutability::Mut => write!(fmt, "(&{:?} mut {:?})", lifetime, ty), + }), + _ => unreachable!(), + } + } + chalk_ir::TypeName::Array => { + let data = application_ty.substitution.interned(); + match (&**data[0].interned(), &**data[1].interned()) { + (chalk_ir::GenericArgData::Ty(ty), chalk_ir::GenericArgData::Const(len)) => { + Some(write!(fmt, "[{:?}; {:?}]", ty, len)) + } + _ => unreachable!(), + } + } + chalk_ir::TypeName::Slice => { + let data = application_ty.substitution.interned(); + let ty = match &**data[0].interned() { + chalk_ir::GenericArgData::Ty(t) => t, + _ => unreachable!(), + }; + Some(write!(fmt, "[{:?}]", ty)) + } + _ => { + let chalk_ir::ApplicationTy { name, substitution } = application_ty; + Some(write!(fmt, "{:?}{:?}", name, chalk_ir::debug::Angle(substitution.interned()))) + } + } } fn debug_substitution( @@ -321,37 +353,30 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { ) -> &'a [chalk_ir::CanonicalVarKind<Self>] { canonical_var_kinds } + + fn intern_constraints<E>( + &self, + data: impl IntoIterator<Item = Result<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>, E>>, + ) -> Result<Self::InternedConstraints, E> { + data.into_iter().collect::<Result<Vec<_>, _>>() + } + + fn constraints_data<'a>( + &self, + constraints: &'a Self::InternedConstraints, + ) -> &'a [chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] { + constraints + } } impl<'tcx> chalk_ir::interner::HasInterner for RustInterner<'tcx> { type Interner = Self; } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)] -pub enum ChalkEnvironmentClause<'tcx> { - /// A normal rust `ty::Predicate` in the environment. - Predicate(ty::Predicate<'tcx>), - /// A special clause in the environment that gets lowered to - /// `chalk_ir::FromEnv::Ty`. - TypeFromEnv(Ty<'tcx>), -} - -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ChalkEnvironmentClause<'tcx>> { - fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { - let v = self.iter().map(|t| t.fold_with(folder)).collect::<SmallVec<[_; 8]>>(); - folder.tcx().intern_chalk_environment_clause_list(&v) - } - - fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} -/// We have to elaborate the environment of a chalk goal *before* -/// canonicalization. This type wraps the predicate and the elaborated -/// environment. +/// A chalk environment and goal. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable)] pub struct ChalkEnvironmentAndGoal<'tcx> { - pub environment: &'tcx ty::List<ChalkEnvironmentClause<'tcx>>, + pub environment: &'tcx ty::List<ty::Predicate<'tcx>>, pub goal: ty::Predicate<'tcx>, } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index f86403fa502..a554c80cdaa 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -26,14 +26,12 @@ use std::rc::Rc; pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache}; -pub type ChalkCanonicalGoal<'tcx> = Canonical<'tcx, ChalkEnvironmentAndGoal<'tcx>>; +pub type CanonicalChalkEnvironmentAndGoal<'tcx> = Canonical<'tcx, ChalkEnvironmentAndGoal<'tcx>>; pub use self::ImplSource::*; pub use self::ObligationCauseCode::*; -pub use self::chalk::{ - ChalkEnvironmentAndGoal, ChalkEnvironmentClause, RustInterner as ChalkRustInterner, -}; +pub use self::chalk::{ChalkEnvironmentAndGoal, RustInterner as ChalkRustInterner}; /// Depending on the stage of compilation, we want projection to be /// more or less conservative. @@ -350,13 +348,16 @@ pub struct MatchExpressionArmCause<'tcx> { pub prior_arms: Vec<Span>, pub last_ty: Ty<'tcx>, pub scrut_hir_id: hir::HirId, + pub opt_suggest_box_span: Option<Span>, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct IfExpressionCause { pub then: Span, + pub else_sp: Span, pub outer: Option<Span>, pub semicolon: Option<Span>, + pub opt_suggest_box_span: Option<Span>, } #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 4b7663e9ade..f9cadb3bb2d 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -190,74 +190,6 @@ impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx> { } } -/// This returns true if the type `ty` is "trivial" for -/// dropck-outlives -- that is, if it doesn't require any types to -/// outlive. This is similar but not *quite* the same as the -/// `needs_drop` test in the compiler already -- that is, for every -/// type T for which this function return true, needs-drop would -/// return `false`. But the reverse does not hold: in particular, -/// `needs_drop` returns false for `PhantomData`, but it is not -/// trivial for dropck-outlives. -/// -/// Note also that `needs_drop` requires a "global" type (i.e., one -/// with erased regions), but this function does not. -pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind { - // None of these types have a destructor and hence they do not - // require anything in particular to outlive the dtor's - // execution. - ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) - | ty::Bool - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Never - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Char - | ty::GeneratorWitness(..) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Str - | ty::Foreign(..) - | ty::Error(_) => true, - - // [T; N] and [T] have same properties as T. - ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), - - // (T1..Tn) and closures have same properties as T1..Tn -- - // check if *any* of those are trivial. - ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), - ty::Closure(_, ref substs) => { - substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t)) - } - - ty::Adt(def, _) => { - if Some(def.did) == tcx.lang_items().manually_drop() { - // `ManuallyDrop` never has a dtor. - true - } else { - // Other types might. Moreover, PhantomData doesn't - // have a dtor, but it is considered to own its - // content, so it is non-trivial. Unions can have `impl Drop`, - // and hence are non-trivial as well. - false - } - } - - // The following *might* require a destructor: needs deeper inspection. - ty::Dynamic(..) - | ty::Projection(..) - | ty::Param(_) - | ty::Opaque(..) - | ty::Placeholder(..) - | ty::Infer(_) - | ty::Bound(..) - | ty::Generator(..) => false, - } -} - #[derive(Debug, HashStable)] pub struct CandidateStep<'tcx> { pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_middle/src/ty/_match.rs index 4693a2f66fb..27bccc0bcaf 100644 --- a/compiler/rustc_middle/src/ty/_match.rs +++ b/compiler/rustc_middle/src/ty/_match.rs @@ -67,7 +67,7 @@ impl TypeRelation<'tcx> for Match<'tcx> { return Ok(a); } - match (&a.kind, &b.kind) { + match (a.kind(), b.kind()) { ( _, &ty::Infer(ty::FreshTy(_)) diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 6a9bb8d6c28..46ef5ff7dd8 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -4,6 +4,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_macros::HashStable; +use rustc_span::Span; #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] pub enum PointerCast { @@ -113,6 +114,9 @@ pub enum Adjust<'tcx> { pub struct OverloadedDeref<'tcx> { pub region: ty::Region<'tcx>, pub mutbl: hir::Mutability, + /// The `Span` associated with the field access or method call + /// that triggered this overloaded deref. + pub span: Span, } impl<'tcx> OverloadedDeref<'tcx> { diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index 79a3008c364..b47d9c50e1d 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -50,7 +50,7 @@ impl<'tcx> CastTy<'tcx> { /// Returns `Some` for integral/pointer casts. /// casts like unsizing casts will return `None` pub fn from_ty(t: Ty<'tcx>) -> Option<CastTy<'tcx>> { - match t.kind { + match *t.kind() { ty::Bool => Some(CastTy::Int(IntTy::Bool)), ty::Char => Some(CastTy::Int(IntTy::Char)), ty::Int(_) => Some(CastTy::Int(IntTy::I)), diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 291648869fb..8ea34f9161a 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -36,8 +36,10 @@ pub trait EncodableWithShorthand<'tcx, E: TyEncoder<'tcx>>: Copy + Eq + Hash { #[allow(rustc::usage_of_ty_tykind)] impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for Ty<'tcx> { type Variant = ty::TyKind<'tcx>; + + #[inline] fn variant(&self) -> &Self::Variant { - &self.kind + self.kind() } } @@ -355,6 +357,26 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::Predicate<'tcx>, } } +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::Node<'tcx>] { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + Ok(decoder.tcx().arena.alloc_from_iter( + (0..decoder.read_usize()?) + .map(|_| Decodable::decode(decoder)) + .collect::<Result<Vec<_>, _>>()?, + )) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::NodeId] { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + Ok(decoder.tcx().arena.alloc_from_iter( + (0..decoder.read_usize()?) + .map(|_| Decodable::decode(decoder)) + .collect::<Result<Vec<_>, _>>()?, + )) + } +} + impl_decodable_via_ref! { &'tcx ty::TypeckResults<'tcx>, &'tcx ty::List<Ty<'tcx>>, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 18ae744cb1e..ccc8ffd9a9c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -34,6 +34,7 @@ use rustc_data_structures::stable_hasher::{ hash_stable_hashmap, HashStable, StableHasher, StableVec, }; use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; +use rustc_data_structures::unhash::UnhashMap; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -65,8 +66,8 @@ use std::mem; use std::ops::{Bound, Deref}; use std::sync::Arc; -/// A type that is not publicly constructable. This prevents people from making `TyKind::Error` -/// except through `tcx.err*()`, which are in this module. +/// A type that is not publicly constructable. This prevents people from making [`TyKind::Error`]s +/// except through the error-reporting functions on a [`tcx`][TyCtxt]. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(TyEncodable, TyDecodable, HashStable)] pub struct DelaySpanBugEmitted(()); @@ -90,8 +91,6 @@ pub struct CtxtInterners<'tcx> { projs: InternedSet<'tcx, List<ProjectionKind>>, place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>, const_: InternedSet<'tcx, Const<'tcx>>, - - chalk_environment_clause_list: InternedSet<'tcx, List<traits::ChalkEnvironmentClause<'tcx>>>, } impl<'tcx> CtxtInterners<'tcx> { @@ -109,7 +108,6 @@ impl<'tcx> CtxtInterners<'tcx> { projs: Default::default(), place_elems: Default::default(), const_: Default::default(), - chalk_environment_clause_list: Default::default(), } } @@ -758,10 +756,10 @@ impl CanonicalUserType<'tcx> { user_substs.substs.iter().zip(BoundVar::new(0)..).all(|(kind, cvar)| { match kind.unpack() { - GenericArgKind::Type(ty) => match ty.kind { + GenericArgKind::Type(ty) => match ty.kind() { ty::Bound(debruijn, b) => { // We only allow a `ty::INNERMOST` index in substitutions. - assert_eq!(debruijn, ty::INNERMOST); + assert_eq!(*debruijn, ty::INNERMOST); cvar == b.var } _ => false, @@ -935,7 +933,7 @@ pub struct GlobalCtxt<'tcx> { /// A map from `DefPathHash` -> `DefId`. Includes `DefId`s from the local crate /// as well as all upstream crates. Only populated in incremental mode. - pub def_path_hash_to_def_id: Option<FxHashMap<DefPathHash, DefId>>, + pub def_path_hash_to_def_id: Option<UnhashMap<DefPathHash, DefId>>, pub queries: query::Queries<'tcx>, @@ -943,7 +941,7 @@ pub struct GlobalCtxt<'tcx> { maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, /// A map of glob use to a set of names it actually imports. Currently only /// used in save-analysis. - glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>, + pub(crate) glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>, /// Extern prelude entries. The value is `true` if the entry was introduced /// via `extern crate` item and not `--extern` option or compiler built-in. pub extern_prelude: FxHashMap<Symbol, bool>, @@ -1104,7 +1102,7 @@ impl<'tcx> TyCtxt<'tcx> { let def_path_hash_to_def_id = if s.opts.build_dep_graph() { let capacity = definitions.def_path_table().num_def_ids() + crates.iter().map(|cnum| cstore.num_def_ids(*cnum)).sum::<usize>(); - let mut map = FxHashMap::with_capacity_and_hasher(capacity, Default::default()); + let mut map = UnhashMap::with_capacity_and_hasher(capacity, Default::default()); map.extend(definitions.def_path_table().all_def_path_hashes_and_def_ids(LOCAL_CRATE)); for cnum in &crates { @@ -1181,7 +1179,7 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_const(ty::Const { val: ty::ConstKind::Error(DelaySpanBugEmitted(())), ty }) } - pub fn consider_optimizing<T: Fn() -> String>(&self, msg: T) -> bool { + pub fn consider_optimizing<T: Fn() -> String>(self, msg: T) -> bool { let cname = self.crate_name(LOCAL_CRATE).as_str(); self.sess.consider_optimizing(&cname, msg) } @@ -1405,7 +1403,7 @@ impl<'tcx> TyCtxt<'tcx> { } // Returns the `DefId` and the `BoundRegion` corresponding to the given region. - pub fn is_suitable_region(&self, region: Region<'tcx>) -> Option<FreeRegionInfo> { + pub fn is_suitable_region(self, region: Region<'tcx>) -> Option<FreeRegionInfo> { let (suitable_region_binding_scope, bound_region) = match *region { ty::ReFree(ref free_region) => { (free_region.scope.expect_local(), free_region.bound_region) @@ -1435,7 +1433,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type. pub fn return_type_impl_or_dyn_traits( - &self, + self, scope_def_id: LocalDefId, ) -> Vec<&'tcx hir::Ty<'tcx>> { let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id); @@ -1481,7 +1479,7 @@ impl<'tcx> TyCtxt<'tcx> { v.0 } - pub fn return_type_impl_trait(&self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> { + pub fn return_type_impl_trait(self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> { // HACK: `type_of_def_id()` will fail on these (#55796), so return `None`. let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id); match self.hir().get(hir_id) { @@ -1497,9 +1495,9 @@ impl<'tcx> TyCtxt<'tcx> { } let ret_ty = self.type_of(scope_def_id); - match ret_ty.kind { + match ret_ty.kind() { ty::FnDef(_, _) => { - let sig = ret_ty.fn_sig(*self); + let sig = ret_ty.fn_sig(self); let output = self.erase_late_bound_regions(&sig.output()); if output.is_impl_trait() { let fn_decl = self.hir().fn_decl_by_hir_id(hir_id).unwrap(); @@ -1513,7 +1511,7 @@ impl<'tcx> TyCtxt<'tcx> { } // Checks if the bound region is in Impl Item. - pub fn is_bound_region_in_impl_item(&self, suitable_region_binding_scope: LocalDefId) -> bool { + pub fn is_bound_region_in_impl_item(self, suitable_region_binding_scope: LocalDefId) -> bool { let container_id = self.associated_item(suitable_region_binding_scope.to_def_id()).container.id(); if self.impl_trait_ref(container_id).is_some() { @@ -1530,21 +1528,21 @@ impl<'tcx> TyCtxt<'tcx> { /// Determines whether identifiers in the assembly have strict naming rules. /// Currently, only NVPTX* targets need it. - pub fn has_strict_asm_symbol_naming(&self) -> bool { + pub fn has_strict_asm_symbol_naming(self) -> bool { self.sess.target.target.arch.contains("nvptx") } /// Returns `&'static core::panic::Location<'static>`. - pub fn caller_location_ty(&self) -> Ty<'tcx> { + pub fn caller_location_ty(self) -> Ty<'tcx> { self.mk_imm_ref( self.lifetimes.re_static, self.type_of(self.require_lang_item(LangItem::PanicLocation, None)) - .subst(*self, self.mk_substs([self.lifetimes.re_static.into()].iter())), + .subst(self, self.mk_substs([self.lifetimes.re_static.into()].iter())), ) } /// Returns a displayable description and article for the given `def_id` (e.g. `("a", "struct")`). - pub fn article_and_description(&self, def_id: DefId) -> (&'static str, &'static str) { + pub fn article_and_description(self, def_id: DefId) -> (&'static str, &'static str) { match self.def_kind(def_id) { DefKind::Generator => match self.generator_kind(def_id).unwrap() { rustc_hir::GeneratorKind::Async(..) => ("an", "async closure"), @@ -1821,15 +1819,15 @@ macro_rules! sty_debug_print { let shards = tcx.interners.type_.lock_shards(); let types = shards.iter().flat_map(|shard| shard.keys()); for &Interned(t) in types { - let variant = match t.kind { + let variant = match t.kind() { ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Str | ty::Never => continue, ty::Error(_) => /* unimportant */ continue, $(ty::$variant(..) => &mut $variant,)* }; - let lt = t.flags.intersects(ty::TypeFlags::HAS_RE_INFER); - let ty = t.flags.intersects(ty::TypeFlags::HAS_TY_INFER); - let ct = t.flags.intersects(ty::TypeFlags::HAS_CT_INFER); + let lt = t.flags().intersects(ty::TypeFlags::HAS_RE_INFER); + let ty = t.flags().intersects(ty::TypeFlags::HAS_TY_INFER); + let ct = t.flags().intersects(ty::TypeFlags::HAS_CT_INFER); variant.total += 1; total.total += 1; @@ -1930,7 +1928,7 @@ impl<'tcx, T: 'tcx + ?Sized> IntoPointer for Interned<'tcx, T> { // N.B., an `Interned<Ty>` compares and hashes as a `TyKind`. impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> { fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool { - self.0.kind == other.0.kind + self.0.kind() == other.0.kind() } } @@ -1938,14 +1936,14 @@ impl<'tcx> Eq for Interned<'tcx, TyS<'tcx>> {} impl<'tcx> Hash for Interned<'tcx, TyS<'tcx>> { fn hash<H: Hasher>(&self, s: &mut H) { - self.0.kind.hash(s) + self.0.kind().hash(s) } } #[allow(rustc::usage_of_ty_tykind)] impl<'tcx> Borrow<TyKind<'tcx>> for Interned<'tcx, TyS<'tcx>> { fn borrow<'a>(&'a self) -> &'a TyKind<'tcx> { - &self.0.kind + &self.0.kind() } } // N.B., an `Interned<PredicateInner>` compares and hashes as a `PredicateKind`. @@ -2040,7 +2038,7 @@ direct_interners! { } macro_rules! slice_interners { - ($($field:ident: $method:ident($ty:ty)),+) => ( + ($($field:ident: $method:ident($ty:ty)),+ $(,)?) => ( $(impl<'tcx> TyCtxt<'tcx> { pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> { self.interners.$field.intern_ref(v, || { @@ -2059,8 +2057,6 @@ slice_interners!( predicates: _intern_predicates(Predicate<'tcx>), projs: _intern_projs(ProjectionKind), place_elems: _intern_place_elems(PlaceElem<'tcx>), - chalk_environment_clause_list: - _intern_chalk_environment_clause_list(traits::ChalkEnvironmentClause<'tcx>) ); impl<'tcx> TyCtxt<'tcx> { @@ -2085,7 +2081,7 @@ impl<'tcx> TyCtxt<'tcx> { unsafety: hir::Unsafety, ) -> PolyFnSig<'tcx> { sig.map_bound(|s| { - let params_iter = match s.inputs()[0].kind { + let params_iter = match s.inputs()[0].kind() { ty::Tuple(params) => params.into_iter().map(|k| k.expect_ty()), _ => bug!(), }; @@ -2423,7 +2419,7 @@ impl<'tcx> TyCtxt<'tcx> { eps: &[ExistentialPredicate<'tcx>], ) -> &'tcx List<ExistentialPredicate<'tcx>> { assert!(!eps.is_empty()); - assert!(eps.windows(2).all(|w| w[0].stable_cmp(self, &w[1]) != Ordering::Greater)); + assert!(eps.array_windows().all(|[a, b]| a.stable_cmp(self, b) != Ordering::Greater)); self._intern_existential_predicates(eps) } @@ -2459,13 +2455,6 @@ impl<'tcx> TyCtxt<'tcx> { if ts.is_empty() { List::empty() } else { self._intern_canonical_var_infos(ts) } } - pub fn intern_chalk_environment_clause_list( - self, - ts: &[traits::ChalkEnvironmentClause<'tcx>], - ) -> &'tcx List<traits::ChalkEnvironmentClause<'tcx>> { - if ts.is_empty() { List::empty() } else { self._intern_chalk_environment_clause_list(ts) } - } - pub fn mk_fn_sig<I>( self, inputs: I, @@ -2523,18 +2512,6 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_substs(iter::once(self_ty.into()).chain(rest.iter().cloned())) } - pub fn mk_chalk_environment_clause_list< - I: InternAs< - [traits::ChalkEnvironmentClause<'tcx>], - &'tcx List<traits::ChalkEnvironmentClause<'tcx>>, - >, - >( - self, - iter: I, - ) -> I::Output { - iter.intern_with(|xs| self.intern_chalk_environment_clause_list(xs)) - } - /// Walks upwards from `id` to find a node which might change lint levels with attributes. /// It stops at `bound` and just returns it if reached. pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId { diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index b22727bdd75..715319747e3 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -11,7 +11,7 @@ use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; impl<'tcx> TyS<'tcx> { /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive. pub fn is_primitive_ty(&self) -> bool { - match self.kind { + match self.kind() { Bool | Char | Str @@ -31,7 +31,7 @@ impl<'tcx> TyS<'tcx> { /// Whether the type is succinctly representable as a type instead of just referred to with a /// description in error messages. This is used in the main error message. pub fn is_simple_ty(&self) -> bool { - match self.kind { + match self.kind() { Bool | Char | Str @@ -55,7 +55,7 @@ impl<'tcx> TyS<'tcx> { /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to /// ADTs with no type arguments. pub fn is_simple_text(&self) -> bool { - match self.kind { + match self.kind() { Adt(_, substs) => substs.types().next().is_none(), Ref(_, ty, _) => ty.is_simple_text(), _ => self.is_simple_ty(), @@ -64,7 +64,7 @@ impl<'tcx> TyS<'tcx> { /// Whether the type can be safely suggested during error recovery. pub fn is_suggestable(&self) -> bool { - match self.kind { + match self.kind() { Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..) | Projection(..) => false, _ => true, @@ -202,33 +202,59 @@ pub fn suggest_constraining_type_param( // Suggestion: // fn foo<T>(t: T) where T: Foo, T: Bar {... } // - insert: `, T: Zar` + // + // Additionally, there may be no `where` clause whatsoever in the case that this was + // reached because the generic parameter has a default: + // + // Message: + // trait Foo<T=()> {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // trait Foo<T=()> where T: Zar {... } + // - insert: `where T: Zar` - let mut param_spans = Vec::new(); + if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) + && generics.where_clause.predicates.len() == 0 + { + // Suggest a bound, but there is no existing `where` clause *and* the type param has a + // default (`<T=Foo>`), so we suggest adding `where T: Bar`. + err.span_suggestion_verbose( + generics.where_clause.tail_span_for_suggestion(), + &msg_restrict_type_further, + format!(" where {}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } else { + let mut param_spans = Vec::new(); - for predicate in generics.where_clause.predicates { - if let WherePredicate::BoundPredicate(WhereBoundPredicate { - span, bounded_ty, .. - }) = predicate - { - if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { - if let Some(segment) = path.segments.first() { - if segment.ident.to_string() == param_name { - param_spans.push(span); + for predicate in generics.where_clause.predicates { + if let WherePredicate::BoundPredicate(WhereBoundPredicate { + span, + bounded_ty, + .. + }) = predicate + { + if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { + if let Some(segment) = path.segments.first() { + if segment.ident.to_string() == param_name { + param_spans.push(span); + } } } } } - } - match ¶m_spans[..] { - &[¶m_span] => suggest_restrict(param_span.shrink_to_hi()), - _ => { - err.span_suggestion_verbose( - generics.where_clause.tail_span_for_suggestion(), - &msg_restrict_type_further, - format!(", {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); + match ¶m_spans[..] { + &[¶m_span] => suggest_restrict(param_span.shrink_to_hi()), + _ => { + err.span_suggestion_verbose( + generics.where_clause.tail_span_for_suggestion(), + &msg_restrict_type_further, + format!(", {}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } } } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 1963881626e..342a37cadba 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -56,6 +56,7 @@ pub enum TypeError<'tcx> { /// created a cycle (because it appears somewhere within that /// type). CyclicTy(Ty<'tcx>), + CyclicConst(&'tcx ty::Const<'tcx>), ProjectionMismatched(ExpectedFound<DefId>), ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>), ObjectUnsafeCoercion(DefId), @@ -100,6 +101,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { match *self { CyclicTy(_) => write!(f, "cyclic type of infinite size"), + CyclicConst(_) => write!(f, "encountered a self-referencing constant"), Mismatch => write!(f, "types differ"), UnsafetyMismatch(values) => { write!(f, "expected {} fn, found {} fn", values.expected, values.found) @@ -195,9 +197,9 @@ impl<'tcx> TypeError<'tcx> { pub fn must_include_note(&self) -> bool { use self::TypeError::*; match self { - CyclicTy(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) - | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_) - | TargetFeatureCast(_) => false, + CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) + | FixedArraySize(_) | Sorts(_) | IntMismatch(_) | FloatMismatch(_) + | VariadicMismatch(_) | TargetFeatureCast(_) => false, Mutability | TupleSize(_) @@ -218,7 +220,7 @@ impl<'tcx> TypeError<'tcx> { impl<'tcx> ty::TyS<'tcx> { pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { - match self.kind { + match *self.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { format!("`{}`", self).into() } @@ -282,7 +284,7 @@ impl<'tcx> ty::TyS<'tcx> { } pub fn prefix_string(&self) -> Cow<'static, str> { - match self.kind { + match *self.kind() { ty::Infer(_) | ty::Error(_) | ty::Bool @@ -351,7 +353,7 @@ impl<'tcx> TyCtxt<'tcx> { ); } } - match (&values.expected.kind, &values.found.kind) { + match (values.expected.kind(), values.found.kind()) { (ty::Float(_), ty::Infer(ty::IntVar(_))) => { if let Ok( // Issue #53280 @@ -512,7 +514,10 @@ impl<T> Trait<T> for X { } debug!( "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", - values.expected, values.expected.kind, values.found, values.found.kind, + values.expected, + values.expected.kind(), + values.found, + values.found.kind(), ); } CyclicTy(ty) => { @@ -543,7 +548,7 @@ impl<T> Trait<T> for X { } fn suggest_constraint( - &self, + self, db: &mut DiagnosticBuilder<'_>, msg: &str, body_owner_def_id: DefId, @@ -551,14 +556,14 @@ impl<T> Trait<T> for X { ty: Ty<'tcx>, ) -> bool { let assoc = self.associated_item(proj_ty.item_def_id); - let trait_ref = proj_ty.trait_ref(*self); + let trait_ref = proj_ty.trait_ref(self); if let Some(item) = self.hir().get_if_local(body_owner_def_id) { if let Some(hir_generics) = item.generics() { // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`. // This will also work for `impl Trait`. - let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind { + let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() { let generics = self.generics_of(body_owner_def_id); - generics.type_param(¶m_ty, *self).def_id + generics.type_param(param_ty, self).def_id } else { return false; }; @@ -626,7 +631,7 @@ impl<T> Trait<T> for X { /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc /// fn that returns the type. fn expected_projection( - &self, + self, db: &mut DiagnosticBuilder<'_>, proj_ty: &ty::ProjectionTy<'tcx>, values: &ExpectedFound<Ty<'tcx>>, @@ -680,7 +685,7 @@ impl<T> Trait<T> for X { } } - if let ty::Opaque(def_id, _) = proj_ty.self_ty().kind { + if let ty::Opaque(def_id, _) = *proj_ty.self_ty().kind() { // When the expected `impl Trait` is not defined in the current item, it will come from // a return type. This can occur when dealing with `TryStream` (#71035). if self.constrain_associated_type_structured_suggestion( @@ -731,7 +736,7 @@ fn foo(&self) -> Self::T { String::new() } } fn point_at_methods_that_satisfy_associated_type( - &self, + self, db: &mut DiagnosticBuilder<'_>, assoc_container_id: DefId, current_method_ident: Option<Symbol>, @@ -750,7 +755,7 @@ fn foo(&self) -> Self::T { String::new() } }) .filter_map(|(_, item)| { let method = self.fn_sig(item.def_id); - match method.output().skip_binder().kind { + match *method.output().skip_binder().kind() { ty::Projection(ty::ProjectionTy { item_def_id, .. }) if item_def_id == proj_ty_item_def_id => { @@ -786,7 +791,7 @@ fn foo(&self) -> Self::T { String::new() } } fn point_at_associated_type( - &self, + self, db: &mut DiagnosticBuilder<'_>, body_owner_def_id: DefId, found: Ty<'tcx>, @@ -829,14 +834,11 @@ fn foo(&self) -> Self::T { String::new() } kind: hir::ItemKind::Impl { items, .. }, .. })) => { for item in &items[..] { - match item.kind { - hir::AssocItemKind::Type => { - if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { - db.span_label(item.span, "expected this associated type"); - return true; - } + if let hir::AssocItemKind::Type = item.kind { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { + db.span_label(item.span, "expected this associated type"); + return true; } - _ => {} } } } @@ -848,7 +850,7 @@ fn foo(&self) -> Self::T { String::new() } /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` /// requirement, provide a strucuted suggestion to constrain it to a given type `ty`. fn constrain_generic_bound_associated_type_structured_suggestion( - &self, + self, db: &mut DiagnosticBuilder<'_>, trait_ref: &ty::TraitRef<'tcx>, bounds: hir::GenericBounds<'_>, @@ -872,7 +874,7 @@ fn foo(&self) -> Self::T { String::new() } /// Given a span corresponding to a bound, provide a structured suggestion to set an /// associated type to a given type `ty`. fn constrain_associated_type_structured_suggestion( - &self, + self, db: &mut DiagnosticBuilder<'_>, span: Span, assoc: &ty::AssocItem, diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 1bee2d60f75..860f91db2bf 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -60,7 +60,7 @@ pub fn simplify_type( ty: Ty<'_>, can_simplify_params: bool, ) -> Option<SimplifiedType> { - match ty.kind { + match *ty.kind() { ty::Bool => Some(BoolSimplifiedType), ty::Char => Some(CharSimplifiedType), ty::Int(int_type) => Some(IntSimplifiedType(int_type)), diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 27f50c240db..c9a4022330a 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -249,11 +249,14 @@ impl FlagComputation { self.add_const(expected); self.add_const(found); } + ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { + self.add_ty(ty); + } } } fn add_ty(&mut self, ty: Ty<'_>) { - self.add_flags(ty.flags); + self.add_flags(ty.flags()); self.add_exclusive_binder(ty.outer_exclusive_binder); } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 492f8ce9ef1..84134bedef0 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -352,7 +352,7 @@ impl<'tcx> TyCtxt<'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { // We're only interested in types involving regions - if ty.flags.intersects(TypeFlags::HAS_FREE_REGIONS) { + if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) { ty.super_visit_with(self) } else { false // keep visiting @@ -471,7 +471,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind { + match *t.kind() { ty::Bound(debruijn, bound_ty) => { if debruijn == self.current_index { let fld_t = &mut self.fld_t; @@ -623,7 +623,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Replaces any late-bound regions bound in `value` with /// free variants attached to `all_outlive_scope`. pub fn liberate_late_bound_regions<T>( - &self, + self, all_outlive_scope: DefId, value: &ty::Binder<T>, ) -> T @@ -644,7 +644,7 @@ impl<'tcx> TyCtxt<'tcx> { /// variables and equate `value` with something else, those /// variables will also be equated. pub fn collect_constrained_late_bound_regions<T>( - &self, + self, value: &Binder<T>, ) -> FxHashSet<ty::BoundRegion> where @@ -655,7 +655,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns a set of all late-bound regions that appear in `value` anywhere. pub fn collect_referenced_late_bound_regions<T>( - &self, + self, value: &Binder<T>, ) -> FxHashSet<ty::BoundRegion> where @@ -665,7 +665,7 @@ impl<'tcx> TyCtxt<'tcx> { } fn collect_late_bound_regions<T>( - &self, + self, value: &Binder<T>, just_constraint: bool, ) -> FxHashSet<ty::BoundRegion> @@ -771,7 +771,7 @@ impl TypeFolder<'tcx> for Shifter<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { + match *ty.kind() { ty::Bound(debruijn, bound_ty) => { if self.amount == 0 || debruijn < self.current_index { ty @@ -922,8 +922,13 @@ struct HasTypeFlagsVisitor { impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { fn visit_ty(&mut self, t: Ty<'_>) -> bool { - debug!("HasTypeFlagsVisitor: t={:?} t.flags={:?} self.flags={:?}", t, t.flags, self.flags); - t.flags.intersects(self.flags) + debug!( + "HasTypeFlagsVisitor: t={:?} t.flags={:?} self.flags={:?}", + t, + t.flags(), + self.flags + ); + t.flags().intersects(self.flags) } fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { @@ -987,7 +992,7 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { // ignore the inputs to a projection, as they may not appear // in the normalized form if self.just_constrained { - if let ty::Projection(..) | ty::Opaque(..) = t.kind { + if let ty::Projection(..) | ty::Opaque(..) = t.kind() { return false; } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index d1b5eed921b..2c1179c21fb 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -196,7 +196,7 @@ impl<'tcx> FieldDef { impl<'tcx> TyS<'tcx> { /// Calculates the forest of `DefId`s from which this type is visibly uninhabited. fn uninhabited_from(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> DefIdForest { - match self.kind { + match *self.kind() { Adt(def, substs) => { ensure_sufficient_stack(|| def.uninhabited_from(tcx, substs, param_env)) } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 8e08fe4b87b..a6b62097d5b 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -62,10 +62,6 @@ pub enum InstanceDef<'tcx> { /// `<fn() as FnTrait>::call_*` (generated `FnTrait` implementation for `fn()` pointers). /// /// `DefId` is `FnTrait::call_*`. - /// - /// NB: the (`fn` pointer) type must currently be monomorphic to avoid double substitution - /// problems with the MIR shim bodies. `Instance::resolve` enforces this. - // FIXME(#69925) support polymorphic MIR shim bodies properly instead. FnPtrShim(DefId, Ty<'tcx>), /// Dynamic dispatch to `<dyn Trait as Trait>::fn`. @@ -87,10 +83,6 @@ pub enum InstanceDef<'tcx> { /// The `DefId` is for `core::ptr::drop_in_place`. /// The `Option<Ty<'tcx>>` is either `Some(T)`, or `None` for empty drop /// glue. - /// - /// NB: the type must currently be monomorphic to avoid double substitution - /// problems with the MIR shim bodies. `Instance::resolve` enforces this. - // FIXME(#69925) support polymorphic MIR shim bodies properly instead. DropGlue(DefId, Option<Ty<'tcx>>), /// Compiler-generated `<T as Clone>::clone` implementation. @@ -99,10 +91,6 @@ pub enum InstanceDef<'tcx> { /// Additionally, arrays, tuples, and closures get a `Clone` shim even if they aren't `Copy`. /// /// The `DefId` is for `Clone::clone`, the `Ty` is the type `T` with the builtin `Clone` impl. - /// - /// NB: the type must currently be monomorphic to avoid double substitution - /// problems with the MIR shim bodies. `Instance::resolve` enforces this. - // FIXME(#69925) support polymorphic MIR shim bodies properly instead. CloneShim(DefId, Ty<'tcx>), } @@ -243,6 +231,27 @@ impl<'tcx> InstanceDef<'tcx> { _ => false, } } + + /// Returns `true` when the MIR body associated with this instance should be monomorphized + /// by its users (e.g. codegen or miri) by substituting the `substs` from `Instance` (see + /// `Instance::substs_for_mir_body`). + /// + /// Otherwise, returns `false` only for some kinds of shims where the construction of the MIR + /// body should perform necessary substitutions. + pub fn has_polymorphic_mir_body(&self) -> bool { + match *self { + InstanceDef::CloneShim(..) + | InstanceDef::FnPtrShim(..) + | InstanceDef::DropGlue(_, Some(_)) => false, + InstanceDef::ClosureOnceShim { .. } + | InstanceDef::DropGlue(..) + | InstanceDef::Item(_) + | InstanceDef::Intrinsic(..) + | InstanceDef::ReifyShim(..) + | InstanceDef::Virtual(..) + | InstanceDef::VtableShim(..) => true, + } + } } impl<'tcx> fmt::Display for Instance<'tcx> { @@ -260,10 +269,11 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), - InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty), + InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty), InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), - InstanceDef::DropGlue(_, ty) => write!(f, " - shim({:?})", ty), - InstanceDef::CloneShim(_, ty) => write!(f, " - shim({:?})", ty), + InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"), + InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty), + InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty), } } } @@ -439,30 +449,18 @@ impl<'tcx> Instance<'tcx> { Instance { def, substs } } - /// FIXME(#69925) Depending on the kind of `InstanceDef`, the MIR body associated with an + /// Depending on the kind of `InstanceDef`, the MIR body associated with an /// instance is expressed in terms of the generic parameters of `self.def_id()`, and in other /// cases the MIR body is expressed in terms of the types found in the substitution array. /// In the former case, we want to substitute those generic types and replace them with the /// values from the substs when monomorphizing the function body. But in the latter case, we /// don't want to do that substitution, since it has already been done effectively. /// - /// This function returns `Some(substs)` in the former case and None otherwise -- i.e., if + /// This function returns `Some(substs)` in the former case and `None` otherwise -- i.e., if /// this function returns `None`, then the MIR body does not require substitution during - /// monomorphization. + /// codegen. pub fn substs_for_mir_body(&self) -> Option<SubstsRef<'tcx>> { - match self.def { - InstanceDef::CloneShim(..) - | InstanceDef::DropGlue(_, Some(_)) => None, - InstanceDef::ClosureOnceShim { .. } - | InstanceDef::DropGlue(..) - // FIXME(#69925): `FnPtrShim` should be in the other branch. - | InstanceDef::FnPtrShim(..) - | InstanceDef::Item(_) - | InstanceDef::Intrinsic(..) - | InstanceDef::ReifyShim(..) - | InstanceDef::Virtual(..) - | InstanceDef::VtableShim(..) => Some(self.substs), - } + if self.def.has_polymorphic_mir_body() { Some(self.substs) } else { None } } /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 3ec08423472..0fda1473f64 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -174,9 +174,9 @@ pub enum LayoutError<'tcx> { impl<'tcx> fmt::Display for LayoutError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - LayoutError::Unknown(ty) => write!(f, "the type `{:?}` has an unknown layout", ty), + LayoutError::Unknown(ty) => write!(f, "the type `{}` has an unknown layout", ty), LayoutError::SizeOverflow(ty) => { - write!(f, "the type `{:?}` is too big for the current architecture", ty) + write!(f, "the type `{}` is too big for the current architecture", ty) } } } @@ -477,7 +477,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { }; debug_assert!(!ty.has_infer_types_or_consts()); - Ok(match ty.kind { + Ok(match *ty.kind() { // Basic scalars. ty::Bool => tcx.intern_layout(Layout::scalar( self, @@ -522,7 +522,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - let metadata = match unsized_part.kind { + let metadata = match unsized_part.kind() { ty::Foreign(..) => { return Ok(tcx.intern_layout(Layout::scalar(self, data_ptr))); } @@ -1241,11 +1241,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { tcx.layout_raw(param_env.and(normalized))? } - ty::Bound(..) | ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => { + ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => { bug!("Layout::compute: unexpected type `{}`", ty) } - ty::Param(_) | ty::Error(_) => { + ty::Bound(..) | ty::Param(_) | ty::Error(_) => { return Err(LayoutError::Unknown(ty)); } }) @@ -1606,7 +1606,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { ); }; - let adt_def = match layout.ty.kind { + let adt_def = match *layout.ty.kind() { ty::Adt(ref adt_def, _) => { debug!("print-type-size t: `{:?}` process adt", layout.ty); adt_def @@ -1749,11 +1749,11 @@ impl<'tcx> SizeSkeleton<'tcx> { Err(err) => err, }; - match ty.kind { + match *ty.kind() { ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { let non_zero = !ty.is_unsafe_ptr(); let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - match tail.kind { + match tail.kind() { ty::Param(_) | ty::Projection(_) => { debug_assert!(tail.has_param_types_or_consts()); Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(&tail) }) @@ -2000,7 +2000,7 @@ where assert_eq!(original_layout.variants, Variants::Single { index }); } - let fields = match this.ty.kind { + let fields = match this.ty.kind() { ty::Adt(def, _) if def.variants.is_empty() => bug!("for_variant called on zero-variant enum"), ty::Adt(def, _) => def.variants[variant_index].fields.len(), @@ -2038,7 +2038,7 @@ where })) }; - cx.layout_of(match this.ty.kind { + cx.layout_of(match *this.ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -2074,7 +2074,7 @@ where )); } - match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind { + match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() { ty::Slice(_) | ty::Str => tcx.types.usize, ty::Dynamic(_, _) => { tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3)) @@ -2152,7 +2152,7 @@ where if ty.is_fn() { cx.data_layout().instruction_address_space } else { AddressSpace::DATA } }; - let pointee_info = match this.ty.kind { + let pointee_info = match *this.ty.kind() { ty::RawPtr(mt) if offset.bytes() == 0 => { cx.layout_of(mt.ty).to_result().ok().map(|layout| PointeeInfo { size: layout.size, @@ -2268,7 +2268,7 @@ where // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`. if let Some(ref mut pointee) = result { - if let ty::Adt(def, _) = this.ty.kind { + if let ty::Adt(def, _) = this.ty.kind() { if def.is_box() && offset.bytes() == 0 { pointee.safe = Some(PointerKind::UniqueOwned); } @@ -2281,7 +2281,9 @@ where debug!( "pointee_info_at (offset={:?}, type kind: {:?}) => {:?}", - offset, this.ty.kind, pointee_info + offset, + this.ty.kind(), + pointee_info ); pointee_info @@ -2308,14 +2310,14 @@ impl<'tcx> ty::Instance<'tcx> { fn fn_sig_for_fn_abi(&self, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { // FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function. let ty = self.ty(tcx, ty::ParamEnv::reveal_all()); - match ty.kind { + match *ty.kind() { ty::FnDef(..) => { // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering // parameters unused if they show up in the signature, but not in the `mir::Body` // (i.e. due to being inside a projection that got normalized, see // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping // track of a polymorphization `ParamEnv` to allow normalizing later. - let mut sig = match ty.kind { + let mut sig = match *ty.kind() { ty::FnDef(def_id, substs) => tcx .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id)) .subst(tcx, substs), @@ -2578,7 +2580,7 @@ where assert!(!sig.c_variadic && extra_args.is_empty()); if let Some(input) = sig.inputs().last() { - if let ty::Tuple(tupled_arguments) = input.kind { + if let ty::Tuple(tupled_arguments) = input.kind() { inputs = &sig.inputs()[0..sig.inputs().len() - 1]; tupled_arguments.iter().map(|k| k.expect_ty()).collect() } else { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index b6300a40b0d..637ef4c17eb 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -580,8 +580,12 @@ bitflags! { #[allow(rustc::usage_of_ty_tykind)] pub struct TyS<'tcx> { - pub kind: TyKind<'tcx>, - pub flags: TypeFlags, + /// This field shouldn't be used directly and may be removed in the future. + /// Use `TyS::kind()` instead. + kind: TyKind<'tcx>, + /// This field shouldn't be used directly and may be removed in the future. + /// Use `TyS::flags()` instead. + flags: TypeFlags, /// This is a kind of confusing thing: it stores the smallest /// binder such that @@ -609,13 +613,13 @@ static_assert_size!(TyS<'_>, 32); impl<'tcx> Ord for TyS<'tcx> { fn cmp(&self, other: &TyS<'tcx>) -> Ordering { - self.kind.cmp(&other.kind) + self.kind().cmp(other.kind()) } } impl<'tcx> PartialOrd for TyS<'tcx> { fn partial_cmp(&self, other: &TyS<'tcx>) -> Option<Ordering> { - Some(self.kind.cmp(&other.kind)) + Some(self.kind().cmp(other.kind())) } } @@ -1151,6 +1155,11 @@ pub enum PredicateAtom<'tcx> { /// Constants must be equal. The first component is the const that is expected. ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>), + + /// Represents a type found in the environment that we can use for implied bounds. + /// + /// Only used for Chalk. + TypeWellFormedFromEnv(Ty<'tcx>), } impl<'tcx> PredicateAtom<'tcx> { @@ -1446,7 +1455,8 @@ impl<'tcx> Predicate<'tcx> { | PredicateAtom::ClosureKind(..) | PredicateAtom::TypeOutlives(..) | PredicateAtom::ConstEvaluatable(..) - | PredicateAtom::ConstEquate(..) => None, + | PredicateAtom::ConstEquate(..) + | PredicateAtom::TypeWellFormedFromEnv(..) => None, } } @@ -1461,7 +1471,8 @@ impl<'tcx> Predicate<'tcx> { | PredicateAtom::ObjectSafe(..) | PredicateAtom::ClosureKind(..) | PredicateAtom::ConstEvaluatable(..) - | PredicateAtom::ConstEquate(..) => None, + | PredicateAtom::ConstEquate(..) + | PredicateAtom::TypeWellFormedFromEnv(..) => None, } } } @@ -1735,10 +1746,8 @@ pub struct ParamEnv<'tcx> { /// Note: This is packed, use the reveal() method to access it. packed: CopyTaggedPtr<&'tcx List<Predicate<'tcx>>, traits::Reveal, true>, - /// If this `ParamEnv` comes from a call to `tcx.param_env(def_id)`, - /// register that `def_id` (useful for transitioning to the chalk trait - /// solver). - pub def_id: Option<DefId>, + /// FIXME: This field is not used, but removing it causes a performance degradation. See #76913. + unused_field: Option<DefId>, } unsafe impl rustc_data_structures::tagged_ptr::Tag for traits::Reveal { @@ -1763,7 +1772,6 @@ impl<'tcx> fmt::Debug for ParamEnv<'tcx> { f.debug_struct("ParamEnv") .field("caller_bounds", &self.caller_bounds()) .field("reveal", &self.reveal()) - .field("def_id", &self.def_id) .finish() } } @@ -1772,23 +1780,16 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ParamEnv<'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.caller_bounds().hash_stable(hcx, hasher); self.reveal().hash_stable(hcx, hasher); - self.def_id.hash_stable(hcx, hasher); } } impl<'tcx> TypeFoldable<'tcx> for ParamEnv<'tcx> { fn super_fold_with<F: ty::fold::TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { - ParamEnv::new( - self.caller_bounds().fold_with(folder), - self.reveal().fold_with(folder), - self.def_id.fold_with(folder), - ) + ParamEnv::new(self.caller_bounds().fold_with(folder), self.reveal().fold_with(folder)) } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { - self.caller_bounds().visit_with(visitor) - || self.reveal().visit_with(visitor) - || self.def_id.visit_with(visitor) + self.caller_bounds().visit_with(visitor) || self.reveal().visit_with(visitor) } } @@ -1799,7 +1800,7 @@ impl<'tcx> ParamEnv<'tcx> { /// type-checking. #[inline] pub fn empty() -> Self { - Self::new(List::empty(), Reveal::UserFacing, None) + Self::new(List::empty(), Reveal::UserFacing) } #[inline] @@ -1821,17 +1822,13 @@ impl<'tcx> ParamEnv<'tcx> { /// or invoke `param_env.with_reveal_all()`. #[inline] pub fn reveal_all() -> Self { - Self::new(List::empty(), Reveal::All, None) + Self::new(List::empty(), Reveal::All) } /// Construct a trait environment with the given set of predicates. #[inline] - pub fn new( - caller_bounds: &'tcx List<Predicate<'tcx>>, - reveal: Reveal, - def_id: Option<DefId>, - ) -> Self { - ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, reveal), def_id } + pub fn new(caller_bounds: &'tcx List<Predicate<'tcx>>, reveal: Reveal) -> Self { + ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, reveal), unused_field: None } } pub fn with_user_facing(mut self) -> Self { @@ -1853,12 +1850,12 @@ impl<'tcx> ParamEnv<'tcx> { return self; } - ParamEnv::new(tcx.normalize_opaque_types(self.caller_bounds()), Reveal::All, self.def_id) + ParamEnv::new(tcx.normalize_opaque_types(self.caller_bounds()), Reveal::All) } /// Returns this same environment but with no caller bounds. pub fn without_caller_bounds(self) -> Self { - Self::new(List::empty(), self.reveal(), self.def_id) + Self::new(List::empty(), self.reveal()) } /// Creates a suitable environment in which to perform trait @@ -2005,7 +2002,7 @@ pub struct VariantDef { flags: VariantFlags, } -impl<'tcx> VariantDef { +impl VariantDef { /// Creates a new `VariantDef`. /// /// `variant_did` is the `DefId` that identifies the enum variant (if this `VariantDef` @@ -2071,19 +2068,6 @@ impl<'tcx> VariantDef { pub fn is_recovered(&self) -> bool { self.flags.intersects(VariantFlags::IS_RECOVERED) } - - /// `repr(transparent)` structs can have a single non-ZST field, this function returns that - /// field. - pub fn transparent_newtype_field(&self, tcx: TyCtxt<'tcx>) -> Option<&FieldDef> { - for field in &self.fields { - let field_ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, self.def_id)); - if !field_ty.is_zst(tcx, self.def_id) { - return Some(field); - } - } - - None - } } #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] @@ -3101,6 +3085,7 @@ pub fn provide(providers: &mut ty::query::Providers) { erase_regions::provide(providers); layout::provide(providers); util::provide(providers); + print::provide(providers); super::util::bug::provide(providers); *providers = ty::query::Providers { trait_impls_of: trait_def::trait_impls_of_provider, diff --git a/compiler/rustc_middle/src/ty/outlives.rs b/compiler/rustc_middle/src/ty/outlives.rs index 1a8693b8df7..ca992d36e95 100644 --- a/compiler/rustc_middle/src/ty/outlives.rs +++ b/compiler/rustc_middle/src/ty/outlives.rs @@ -4,6 +4,7 @@ use crate::ty::subst::{GenericArg, GenericArgKind}; use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc_data_structures::mini_set::MiniSet; use smallvec::SmallVec; #[derive(Debug)] @@ -50,18 +51,24 @@ impl<'tcx> TyCtxt<'tcx> { /// Push onto `out` all the things that must outlive `'a` for the condition /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**. pub fn push_outlives_components(self, ty0: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) { - compute_components(self, ty0, out); + let mut visited = MiniSet::new(); + compute_components(self, ty0, out, &mut visited); debug!("components({:?}) = {:?}", ty0, out); } } -fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) { +fn compute_components( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + out: &mut SmallVec<[Component<'tcx>; 4]>, + visited: &mut MiniSet<GenericArg<'tcx>>, +) { // Descend through the types, looking for the various "base" // components and collecting them into `out`. This is not written // with `collect()` because of the need to sometimes skip subtrees // in the `subtys` iterator (e.g., when encountering a // projection). - match ty.kind { + match *ty.kind() { ty::FnDef(_, substs) => { // HACK(eddyb) ignore lifetimes found shallowly in `substs`. // This is inconsistent with `ty::Adt` (including all substs) @@ -73,11 +80,11 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo for child in substs { match child.unpack() { GenericArgKind::Type(ty) => { - compute_components(tcx, ty, out); + compute_components(tcx, ty, out, visited); } GenericArgKind::Lifetime(_) => {} GenericArgKind::Const(_) => { - compute_components_recursive(tcx, child, out); + compute_components_recursive(tcx, child, out, visited); } } } @@ -85,19 +92,19 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo ty::Array(element, _) => { // Don't look into the len const as it doesn't affect regions - compute_components(tcx, element, out); + compute_components(tcx, element, out, visited); } ty::Closure(_, ref substs) => { for upvar_ty in substs.as_closure().upvar_tys() { - compute_components(tcx, upvar_ty, out); + compute_components(tcx, upvar_ty, out, visited); } } ty::Generator(_, ref substs, _) => { // Same as the closure case for upvar_ty in substs.as_generator().upvar_tys() { - compute_components(tcx, upvar_ty, out); + compute_components(tcx, upvar_ty, out, visited); } // We ignore regions in the generator interior as we don't @@ -135,7 +142,8 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo // OutlivesProjectionComponents. Continue walking // through and constrain Pi. let mut subcomponents = smallvec![]; - compute_components_recursive(tcx, ty.into(), &mut subcomponents); + let mut subvisited = MiniSet::new(); + compute_components_recursive(tcx, ty.into(), &mut subcomponents, &mut subvisited); out.push(Component::EscapingProjection(subcomponents.into_iter().collect())); } } @@ -177,7 +185,7 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo // the "bound regions list". In our representation, no such // list is maintained explicitly, because bound regions // themselves can be readily identified. - compute_components_recursive(tcx, ty.into(), out); + compute_components_recursive(tcx, ty.into(), out, visited); } } } @@ -186,11 +194,12 @@ fn compute_components_recursive( tcx: TyCtxt<'tcx>, parent: GenericArg<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>, + visited: &mut MiniSet<GenericArg<'tcx>>, ) { - for child in parent.walk_shallow() { + for child in parent.walk_shallow(visited) { match child.unpack() { GenericArgKind::Type(ty) => { - compute_components(tcx, ty, out); + compute_components(tcx, ty, out, visited); } GenericArgKind::Lifetime(lt) => { // Ignore late-bound regions. @@ -199,7 +208,7 @@ fn compute_components_recursive( } } GenericArgKind::Const(_) => { - compute_components_recursive(tcx, child, out); + compute_components_recursive(tcx, child, out, visited); } } } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 6c8f23c139f..225ea2399fb 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -2,6 +2,7 @@ use crate::ty::subst::{GenericArg, Subst}; use crate::ty::{self, DefIdTree, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::mini_set::MiniSet; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; @@ -9,8 +10,6 @@ use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; mod pretty; pub use self::pretty::*; -pub mod obsolete; - // FIXME(eddyb) false positive, the lifetime parameters are used with `P: Printer<...>`. #[allow(unused_lifetimes)] pub trait Print<'tcx, P> { @@ -265,21 +264,33 @@ pub trait Printer<'tcx>: Sized { /// function tries to find a "characteristic `DefId`" for a /// type. It's just a heuristic so it makes some questionable /// decisions and we may want to adjust it later. -pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> { - match ty.kind { +/// +/// Visited set is needed to avoid full iteration over +/// deeply nested tuples that have no DefId. +fn characteristic_def_id_of_type_cached<'a>( + ty: Ty<'a>, + visited: &mut MiniSet<Ty<'a>>, +) -> Option<DefId> { + match *ty.kind() { ty::Adt(adt_def, _) => Some(adt_def.did), ty::Dynamic(data, ..) => data.principal_def_id(), - ty::Array(subty, _) | ty::Slice(subty) => characteristic_def_id_of_type(subty), + ty::Array(subty, _) | ty::Slice(subty) => { + characteristic_def_id_of_type_cached(subty, visited) + } - ty::RawPtr(mt) => characteristic_def_id_of_type(mt.ty), + ty::RawPtr(mt) => characteristic_def_id_of_type_cached(mt.ty, visited), - ty::Ref(_, ty, _) => characteristic_def_id_of_type(ty), + ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited), - ty::Tuple(ref tys) => { - tys.iter().find_map(|ty| characteristic_def_id_of_type(ty.expect_ty())) - } + ty::Tuple(ref tys) => tys.iter().find_map(|ty| { + let ty = ty.expect_ty(); + if visited.insert(ty) { + return characteristic_def_id_of_type_cached(ty, visited); + } + return None; + }), ty::FnDef(def_id, _) | ty::Closure(def_id, _) @@ -304,6 +315,9 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> { | ty::Float(_) => None, } } +pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> { + characteristic_def_id_of_type_cached(ty, &mut MiniSet::new()) +} impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind { type Output = P::Region; diff --git a/compiler/rustc_middle/src/ty/print/obsolete.rs b/compiler/rustc_middle/src/ty/print/obsolete.rs deleted file mode 100644 index 2ea7cd2a6dc..00000000000 --- a/compiler/rustc_middle/src/ty/print/obsolete.rs +++ /dev/null @@ -1,251 +0,0 @@ -//! Allows for producing a unique string key for a mono item. -//! These keys are used by the handwritten auto-tests, so they need to be -//! predictable and human-readable. -//! -//! Note: A lot of this could looks very similar to what's already in `ty::print`. -//! FIXME(eddyb) implement a custom `PrettyPrinter` for this. - -use crate::bug; -use crate::ty::subst::SubstsRef; -use crate::ty::{self, Const, Instance, Ty, TyCtxt}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use std::fmt::Write; -use std::iter; - -/// Same as `unique_type_name()` but with the result pushed onto the given -/// `output` parameter. -pub struct DefPathBasedNames<'tcx> { - tcx: TyCtxt<'tcx>, - omit_disambiguators: bool, - omit_local_crate_name: bool, -} - -impl DefPathBasedNames<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, omit_disambiguators: bool, omit_local_crate_name: bool) -> Self { - DefPathBasedNames { tcx, omit_disambiguators, omit_local_crate_name } - } - - // Pushes the type name of the specified type to the provided string. - // If `debug` is true, printing normally unprintable types is allowed - // (e.g. `ty::GeneratorWitness`). This parameter should only be set when - // this method is being used for logging purposes (e.g. with `debug!` or `info!`) - // When being used for codegen purposes, `debug` should be set to `false` - // in order to catch unexpected types that should never end up in a type name. - pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String, debug: bool) { - match t.kind { - ty::Bool => output.push_str("bool"), - ty::Char => output.push_str("char"), - ty::Str => output.push_str("str"), - ty::Never => output.push_str("!"), - ty::Int(ty) => output.push_str(ty.name_str()), - ty::Uint(ty) => output.push_str(ty.name_str()), - ty::Float(ty) => output.push_str(ty.name_str()), - ty::Adt(adt_def, substs) => { - self.push_def_path(adt_def.did, output); - self.push_generic_params(substs, iter::empty(), output, debug); - } - ty::Tuple(component_types) => { - output.push('('); - for component_type in component_types { - self.push_type_name(component_type.expect_ty(), output, debug); - output.push_str(", "); - } - if !component_types.is_empty() { - output.pop(); - output.pop(); - } - output.push(')'); - } - ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => { - output.push('*'); - match mutbl { - hir::Mutability::Not => output.push_str("const "), - hir::Mutability::Mut => output.push_str("mut "), - } - - self.push_type_name(inner_type, output, debug); - } - ty::Ref(_, inner_type, mutbl) => { - output.push('&'); - output.push_str(mutbl.prefix_str()); - - self.push_type_name(inner_type, output, debug); - } - ty::Array(inner_type, len) => { - output.push('['); - self.push_type_name(inner_type, output, debug); - let len = len.eval_usize(self.tcx, ty::ParamEnv::reveal_all()); - write!(output, "; {}", len).unwrap(); - output.push(']'); - } - ty::Slice(inner_type) => { - output.push('['); - self.push_type_name(inner_type, output, debug); - output.push(']'); - } - ty::Dynamic(ref trait_data, ..) => { - if let Some(principal) = trait_data.principal() { - self.push_def_path(principal.def_id(), output); - self.push_generic_params( - principal.skip_binder().substs, - trait_data.projection_bounds(), - output, - debug, - ); - } else { - output.push_str("dyn '_"); - } - } - ty::Foreign(did) => self.push_def_path(did, output), - ty::FnDef(..) | ty::FnPtr(_) => { - let sig = t.fn_sig(self.tcx); - output.push_str(sig.unsafety().prefix_str()); - - let abi = sig.abi(); - if abi != ::rustc_target::spec::abi::Abi::Rust { - output.push_str("extern \""); - output.push_str(abi.name()); - output.push_str("\" "); - } - - output.push_str("fn("); - - let sig = - self.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - - if !sig.inputs().is_empty() { - for ¶meter_type in sig.inputs() { - self.push_type_name(parameter_type, output, debug); - output.push_str(", "); - } - output.pop(); - output.pop(); - } - - if sig.c_variadic { - if !sig.inputs().is_empty() { - output.push_str(", ..."); - } else { - output.push_str("..."); - } - } - - output.push(')'); - - if !sig.output().is_unit() { - output.push_str(" -> "); - self.push_type_name(sig.output(), output, debug); - } - } - ty::Generator(def_id, substs, _) | ty::Closure(def_id, substs) => { - self.push_def_path(def_id, output); - let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id)); - let substs = substs.truncate_to(self.tcx, generics); - self.push_generic_params(substs, iter::empty(), output, debug); - } - ty::Param(_) => { - output.push_str(&t.to_string()); - } - ty::Error(_) - | ty::Bound(..) - | ty::Infer(_) - | ty::Placeholder(..) - | ty::Projection(..) - | ty::GeneratorWitness(_) - | ty::Opaque(..) => { - if debug { - output.push_str(&format!("`{:?}`", t)); - } else { - bug!( - "DefPathBasedNames: trying to create type name for unexpected type: {:?}", - t, - ); - } - } - } - } - - // Pushes the the name of the specified const to the provided string. - // If `debug` is true, the unprintable types of constants will be printed with `fmt::Debug` - // (see `push_type_name` for more details). - pub fn push_const_name(&self, ct: &Const<'tcx>, output: &mut String, debug: bool) { - write!(output, "{}", ct).unwrap(); - output.push_str(": "); - self.push_type_name(ct.ty, output, debug); - } - - pub fn push_def_path(&self, def_id: DefId, output: &mut String) { - let def_path = self.tcx.def_path(def_id); - - // some_crate:: - if !(self.omit_local_crate_name && def_id.is_local()) { - output.push_str(&self.tcx.crate_name(def_path.krate).as_str()); - output.push_str("::"); - } - - // foo::bar::ItemName:: - for part in self.tcx.def_path(def_id).data { - if self.omit_disambiguators { - write!(output, "{}::", part.data.as_symbol()).unwrap(); - } else { - write!(output, "{}[{}]::", part.data.as_symbol(), part.disambiguator).unwrap(); - } - } - - // remove final "::" - output.pop(); - output.pop(); - } - - fn push_generic_params<I>( - &self, - substs: SubstsRef<'tcx>, - projections: I, - output: &mut String, - debug: bool, - ) where - I: Iterator<Item = ty::PolyExistentialProjection<'tcx>>, - { - let mut projections = projections.peekable(); - if substs.non_erasable_generics().next().is_none() && projections.peek().is_none() { - return; - } - - output.push('<'); - - for type_parameter in substs.types() { - self.push_type_name(type_parameter, output, debug); - output.push_str(", "); - } - - for projection in projections { - let projection = projection.skip_binder(); - let name = &self.tcx.associated_item(projection.item_def_id).ident.as_str(); - output.push_str(name); - output.push_str("="); - self.push_type_name(projection.ty, output, debug); - output.push_str(", "); - } - - for const_parameter in substs.consts() { - self.push_const_name(const_parameter, output, debug); - output.push_str(", "); - } - - output.pop(); - output.pop(); - - output.push('>'); - } - - pub fn push_instance_as_string( - &self, - instance: Instance<'tcx>, - output: &mut String, - debug: bool, - ) { - self.push_def_path(instance.def_id(), output); - self.push_generic_params(instance.substs, iter::empty(), output, debug); - } -} diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 999a1d52a26..c9cc9bfc9fa 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -7,10 +7,13 @@ use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::Float; use rustc_ast as ast; use rustc_attr::{SignedInt, UnsignedInt}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Namespace}; -use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_hir::ItemKind; +use rustc_session::config::TrimmedDefPaths; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_target::abi::{Integer, Size}; use rustc_target::spec::abi::Abi; @@ -52,6 +55,7 @@ macro_rules! define_scoped_cx { thread_local! { static FORCE_IMPL_FILENAME_LINE: Cell<bool> = Cell::new(false); static SHOULD_PREFIX_WITH_CRATE: Cell<bool> = Cell::new(false); + static NO_TRIMMED_PATH: Cell<bool> = Cell::new(false); static NO_QUERIES: Cell<bool> = Cell::new(false); } @@ -94,6 +98,18 @@ pub fn with_crate_prefix<F: FnOnce() -> R, R>(f: F) -> R { }) } +/// Prevent path trimming if it is turned on. Path trimming affects `Display` impl +/// of various rustc types, for example `std::vec::Vec` would be trimmed to `Vec`, +/// if no other `Vec` is found. +pub fn with_no_trimmed_paths<F: FnOnce() -> R, R>(f: F) -> R { + NO_TRIMMED_PATH.with(|flag| { + let old = flag.replace(true); + let result = f(); + flag.set(old); + result + }) +} + /// The "region highlights" are used to control region printing during /// specific error messages. When a "region highlight" is enabled, it /// gives an alternate way to print specific regions. For now, we @@ -243,6 +259,28 @@ pub trait PrettyPrinter<'tcx>: self.try_print_visible_def_path_recur(def_id, &mut callers) } + /// Try to see if this path can be trimmed to a unique symbol name. + fn try_print_trimmed_def_path( + mut self, + def_id: DefId, + ) -> Result<(Self::Path, bool), Self::Error> { + if !self.tcx().sess.opts.debugging_opts.trim_diagnostic_paths + || matches!(self.tcx().sess.opts.trimmed_def_paths, TrimmedDefPaths::Never) + || NO_TRIMMED_PATH.with(|flag| flag.get()) + || SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) + { + return Ok((self, false)); + } + + match self.tcx().trimmed_def_paths(LOCAL_CRATE).get(&def_id) { + None => Ok((self, false)), + Some(symbol) => { + self.write_str(&symbol.as_str())?; + Ok((self, true)) + } + } + } + /// Does the work of `try_print_visible_def_path`, building the /// full definition path recursively before attempting to /// post-process it into the valid and visible version that @@ -419,7 +457,7 @@ pub trait PrettyPrinter<'tcx>: // Inherent impls. Try to print `Foo::bar` for an inherent // impl on `Foo`, but fallback to `<Foo>::bar` if self-type is // anything other than a simple path. - match self_ty.kind { + match self_ty.kind() { ty::Adt(..) | ty::Foreign(_) | ty::Bool @@ -470,7 +508,7 @@ pub trait PrettyPrinter<'tcx>: fn pretty_print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> { define_scoped_cx!(self); - match ty.kind { + match *ty.kind() { ty::Bool => p!(write("bool")), ty::Char => p!(write("char")), ty::Int(t) => p!(write("{}", t.name_str())), @@ -759,7 +797,7 @@ pub trait PrettyPrinter<'tcx>: // Special-case `Fn(...) -> ...` and resugar it. let fn_trait_kind = self.tcx().fn_trait_kind_from_lang_item(principal.def_id); if !self.tcx().sess.verbose() && fn_trait_kind.is_some() { - if let ty::Tuple(ref args) = principal.substs.type_at(0).kind { + if let ty::Tuple(ref args) = principal.substs.type_at(0).kind() { let mut projections = predicates.projection_bounds(); if let (Some(proj), None) = (projections.next(), projections.next()) { let tys: Vec<_> = args.iter().map(|k| k.expect_ty()).collect(); @@ -938,7 +976,7 @@ pub trait PrettyPrinter<'tcx>: ) -> Result<Self::Const, Self::Error> { define_scoped_cx!(self); - match (scalar, &ty.kind) { + match (scalar, &ty.kind()) { // Byte strings (&[u8; N]) ( Scalar::Ptr(ptr), @@ -1098,7 +1136,7 @@ pub trait PrettyPrinter<'tcx>: let u8_type = self.tcx().types.u8; - match (ct, &ty.kind) { + match (ct, ty.kind()) { // Byte/string slices, printed as (byte) string literals. ( ConstValue::Slice { data, start, end }, @@ -1151,7 +1189,7 @@ pub trait PrettyPrinter<'tcx>: ); let fields = contents.fields.iter().copied(); - match ty.kind { + match *ty.kind() { ty::Array(..) => { p!(write("["), comma_sep(fields), write("]")); } @@ -1226,6 +1264,7 @@ pub struct FmtPrinterData<'a, 'tcx, F> { used_region_names: FxHashSet<Symbol>, region_index: usize, binder_depth: usize, + printed_type_count: usize, pub region_highlight_mode: RegionHighlightMode, @@ -1256,6 +1295,7 @@ impl<F> FmtPrinter<'a, 'tcx, F> { used_region_names: Default::default(), region_index: 0, binder_depth: 0, + printed_type_count: 0, region_highlight_mode: RegionHighlightMode::default(), name_resolver: None, })) @@ -1324,6 +1364,11 @@ impl<F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { define_scoped_cx!(self); if substs.is_empty() { + match self.try_print_trimmed_def_path(def_id)? { + (cx, true) => return Ok(cx), + (cx, false) => self = cx, + } + match self.try_print_visible_def_path(def_id)? { (cx, true) => return Ok(cx), (cx, false) => self = cx, @@ -1368,8 +1413,14 @@ impl<F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { self.pretty_print_region(region) } - fn print_type(self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> { - self.pretty_print_type(ty) + fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> { + if self.tcx.sess.type_length_limit().value_within_limit(self.printed_type_count) { + self.printed_type_count += 1; + self.pretty_print_type(ty) + } else { + write!(self, "...")?; + Ok(self) + } } fn print_dyn_existential( @@ -2053,6 +2104,11 @@ define_print_and_forward_display! { print(c2), write("`")) } + ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { + p!(write("the type `"), + print(ty), + write("` is found in the environment")) + } } } @@ -2064,3 +2120,127 @@ define_print_and_forward_display! { } } } + +fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, Namespace, DefId)) { + // Iterate all local crate items no matter where they are defined. + let hir = tcx.hir(); + for item in hir.krate().items.values() { + if item.ident.name.as_str().is_empty() || matches!(item.kind, ItemKind::Use(_, _)) { + continue; + } + + if let Some(local_def_id) = hir.definitions().opt_hir_id_to_local_def_id(item.hir_id) { + let def_id = local_def_id.to_def_id(); + let ns = tcx.def_kind(def_id).ns().unwrap_or(Namespace::TypeNS); + collect_fn(&item.ident, ns, def_id); + } + } + + // Now take care of extern crate items. + let queue = &mut Vec::new(); + let mut seen_defs: DefIdSet = Default::default(); + + for &cnum in tcx.crates().iter() { + let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; + + // Ignore crates that are not direct dependencies. + match tcx.extern_crate(def_id) { + None => continue, + Some(extern_crate) => { + if !extern_crate.is_direct() { + continue; + } + } + } + + queue.push(def_id); + } + + // 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 { + continue; + } + + match child.res { + def::Res::Def(DefKind::AssocTy, _) => {} + def::Res::Def(defkind, def_id) => { + if let Some(ns) = defkind.ns() { + collect_fn(&child.ident, ns, def_id); + } + + if seen_defs.insert(def_id) { + queue.push(def_id); + } + } + _ => {} + } + } + } +} + +/// The purpose of this function is to collect public symbols names that are unique across all +/// crates in the build. Later, when printing about types we can use those names instead of the +/// full exported path to them. +/// +/// So essentially, if a symbol name can only be imported from one place for a type, and as +/// long as it was not glob-imported anywhere in the current crate, we can trim its printed +/// path and print only the name. +/// +/// This has wide implications on error messages with types, for example, shortening +/// `std::vec::Vec` to just `Vec`, as long as there is no other `Vec` importable anywhere. +/// +/// The implementation uses similar import discovery logic to that of 'use' suggestions. +fn trimmed_def_paths(tcx: TyCtxt<'_>, crate_num: CrateNum) -> FxHashMap<DefId, Symbol> { + assert_eq!(crate_num, LOCAL_CRATE); + + let mut map = FxHashMap::default(); + + if let TrimmedDefPaths::GoodPath = tcx.sess.opts.trimmed_def_paths { + // For good paths causing this bug, the `rustc_middle::ty::print::with_no_trimmed_paths` + // wrapper can be used to suppress this query, in exchange for full paths being formatted. + tcx.sess.delay_good_path_bug("trimmed_def_paths constructed"); + } + + let unique_symbols_rev: &mut FxHashMap<(Namespace, Symbol), Option<DefId>> = + &mut FxHashMap::default(); + + for symbol_set in tcx.glob_map.values() { + for symbol in symbol_set { + unique_symbols_rev.insert((Namespace::TypeNS, *symbol), None); + unique_symbols_rev.insert((Namespace::ValueNS, *symbol), None); + unique_symbols_rev.insert((Namespace::MacroNS, *symbol), None); + } + } + + for_each_def(tcx, |ident, ns, def_id| { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + + match unique_symbols_rev.entry((ns, ident.name)) { + Occupied(mut v) => match v.get() { + None => {} + Some(existing) => { + if *existing != def_id { + v.insert(None); + } + } + }, + Vacant(v) => { + v.insert(Some(def_id)); + } + } + }); + + for ((_, symbol), opt_def_id) in unique_symbols_rev.drain() { + if let Some(def_id) = opt_def_id { + map.insert(def_id, symbol); + } + } + + map +} + +pub fn provide(providers: &mut ty::query::Providers) { + *providers = ty::query::Providers { trimmed_def_paths, ..*providers }; +} diff --git a/compiler/rustc_middle/src/ty/query/keys.rs b/compiler/rustc_middle/src/ty/query/keys.rs index 3f7a20bba2b..a005990264c 100644 --- a/compiler/rustc_middle/src/ty/query/keys.rs +++ b/compiler/rustc_middle/src/ty/query/keys.rs @@ -193,6 +193,22 @@ impl<'tcx> Key for (DefId, SubstsRef<'tcx>) { } } +impl<'tcx> Key + for ( + (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>), + (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>), + ) +{ + type CacheSelector = DefaultCacheSelector; + + fn query_crate(&self) -> CrateNum { + (self.0).0.did.krate + } + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + (self.0).0.did.default_span(tcx) + } +} + impl<'tcx> Key for (LocalDefId, DefId, SubstsRef<'tcx>) { type CacheSelector = DefaultCacheSelector; diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index ee9b203b151..d3a7412ef14 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -14,7 +14,7 @@ use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLife use crate::middle::stability::{self, DeprecationEntry}; use crate::mir; use crate::mir::interpret::GlobalId; -use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult, ConstValue}; +use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult}; use crate::mir::interpret::{LitToConstError, LitToConstInput}; use crate::mir::mono::CodegenUnit; use crate::traits::query::{ diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs index dcfb8d31430..b0c48a860eb 100644 --- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -760,6 +760,12 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> } } +impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [mir::abstract_const::Node<'tcx>] { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result<Self, String> { + RefDecodable::decode(d) + } +} + impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] { fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result<Self, String> { RefDecodable::decode(d) diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index ae2820b460f..c4df0bba726 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -325,7 +325,7 @@ pub fn super_relate_tys<R: TypeRelation<'tcx>>( ) -> RelateResult<'tcx, Ty<'tcx>> { let tcx = relation.tcx(); debug!("super_relate_tys: a={:?} b={:?}", a, b); - match (&a.kind, &b.kind) { + match (a.kind(), b.kind()) { (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { // The caller should handle these cases! bug!("var types encountered in super_relate_tys") @@ -516,7 +516,7 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>( (ConstValue::Scalar(a_val), ConstValue::Scalar(b_val)) if a.ty == b.ty => { if a_val == b_val { Ok(ConstValue::Scalar(a_val)) - } else if let ty::FnPtr(_) = a.ty.kind { + } else if let ty::FnPtr(_) = a.ty.kind() { let a_instance = tcx.global_alloc(a_val.assert_ptr().alloc_id).unwrap_fn(); let b_instance = tcx.global_alloc(b_val.assert_ptr().alloc_id).unwrap_fn(); if a_instance == b_instance { @@ -540,7 +540,7 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>( } (ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => { - match a.ty.kind { + match a.ty.kind() { ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => { let a_destructured = tcx.destructure_const(relation.param_env().and(a)); let b_destructured = tcx.destructure_const(relation.param_env().and(b)); @@ -576,7 +576,20 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>( new_val.map(ty::ConstKind::Value) } - // FIXME(const_generics): this is wrong, as it is a projection + ( + ty::ConstKind::Unevaluated(a_def, a_substs, None), + ty::ConstKind::Unevaluated(b_def, b_substs, None), + ) if tcx.features().const_evaluatable_checked => { + if tcx.try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs))) { + Ok(a.val) + } else { + Err(TypeError::ConstMismatch(expected_found(relation, a, b))) + } + } + + // While this is slightly incorrect, it shouldn't matter for `min_const_generics` + // and is the better alternative to waiting until `const_evaluatable_checked` can + // be stabilized. ( ty::ConstKind::Unevaluated(a_def, a_substs, a_promoted), ty::ConstKind::Unevaluated(b_def, b_substs, b_promoted), diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 605e3545dea..597ceac9386 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -5,7 +5,7 @@ use crate::mir::interpret; use crate::mir::ProjectionKind; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::print::{FmtPrinter, Printer}; +use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer}; use crate::ty::{self, InferConst, Lift, Ty, TyCtxt}; use rustc_hir as hir; use rustc_hir::def::Namespace; @@ -20,7 +20,9 @@ use std::sync::Arc; impl fmt::Debug for ty::TraitDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { - FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[])?; + with_no_trimmed_paths(|| { + FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[]) + })?; Ok(()) }) } @@ -29,7 +31,9 @@ impl fmt::Debug for ty::TraitDef { impl fmt::Debug for ty::AdtDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { - FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[])?; + with_no_trimmed_paths(|| { + FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[]) + })?; Ok(()) }) } @@ -50,7 +54,7 @@ impl fmt::Debug for ty::UpvarBorrow<'tcx> { impl fmt::Debug for ty::ExistentialTraitRef<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) + with_no_trimmed_paths(|| fmt::Display::fmt(self, f)) } } @@ -183,13 +187,13 @@ impl fmt::Debug for ty::FloatVarValue { impl fmt::Debug for ty::TraitRef<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) + with_no_trimmed_paths(|| fmt::Display::fmt(self, f)) } } impl fmt::Debug for Ty<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) + with_no_trimmed_paths(|| fmt::Display::fmt(self, f)) } } @@ -256,6 +260,9 @@ impl fmt::Debug for ty::PredicateAtom<'tcx> { write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs) } ty::PredicateAtom::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2), + ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { + write!(f, "TypeWellFormedFromEnv({:?})", ty) + } } } } @@ -532,6 +539,9 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateAtom<'a> { ty::PredicateAtom::ConstEquate(c1, c2) => { tcx.lift(&(c1, c2)).map(|(c1, c2)| ty::PredicateAtom::ConstEquate(c1, c2)) } + ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { + tcx.lift(&ty).map(ty::PredicateAtom::TypeWellFormedFromEnv) + } } } } @@ -547,7 +557,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> { type Lifted = ty::ParamEnv<'tcx>; fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> { tcx.lift(&self.caller_bounds()) - .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal(), self.def_id)) + .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal())) } } @@ -602,8 +612,11 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> { impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::OverloadedDeref<'a> { type Lifted = ty::adjustment::OverloadedDeref<'tcx>; fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> { - tcx.lift(&self.region) - .map(|region| ty::adjustment::OverloadedDeref { region, mutbl: self.mutbl }) + tcx.lift(&self.region).map(|region| ty::adjustment::OverloadedDeref { + region, + mutbl: self.mutbl, + span: self.span, + }) } } @@ -676,6 +689,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { Traits(x) => Traits(x), VariadicMismatch(x) => VariadicMismatch(x), CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)), + CyclicConst(ct) => return tcx.lift(&ct).map(|ct| CyclicConst(ct)), ProjectionMismatched(x) => ProjectionMismatched(x), Sorts(ref x) => return tcx.lift(x).map(Sorts), ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch), @@ -919,25 +933,25 @@ impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> { impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { - let kind = match self.kind { + let kind = match self.kind() { ty::RawPtr(tm) => ty::RawPtr(tm.fold_with(folder)), ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)), ty::Slice(typ) => ty::Slice(typ.fold_with(folder)), - ty::Adt(tid, substs) => ty::Adt(tid, substs.fold_with(folder)), + ty::Adt(tid, substs) => ty::Adt(*tid, substs.fold_with(folder)), ty::Dynamic(ref trait_ty, ref region) => { ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) } ty::Tuple(ts) => ty::Tuple(ts.fold_with(folder)), - ty::FnDef(def_id, substs) => ty::FnDef(def_id, substs.fold_with(folder)), + ty::FnDef(def_id, substs) => ty::FnDef(*def_id, substs.fold_with(folder)), ty::FnPtr(f) => ty::FnPtr(f.fold_with(folder)), - ty::Ref(ref r, ty, mutbl) => ty::Ref(r.fold_with(folder), ty.fold_with(folder), mutbl), + ty::Ref(ref r, ty, mutbl) => ty::Ref(r.fold_with(folder), ty.fold_with(folder), *mutbl), ty::Generator(did, substs, movability) => { - ty::Generator(did, substs.fold_with(folder), movability) + ty::Generator(*did, substs.fold_with(folder), *movability) } ty::GeneratorWitness(types) => ty::GeneratorWitness(types.fold_with(folder)), - ty::Closure(did, substs) => ty::Closure(did, substs.fold_with(folder)), + ty::Closure(did, substs) => ty::Closure(*did, substs.fold_with(folder)), ty::Projection(ref data) => ty::Projection(data.fold_with(folder)), - ty::Opaque(did, substs) => ty::Opaque(did, substs.fold_with(folder)), + ty::Opaque(did, substs) => ty::Opaque(*did, substs.fold_with(folder)), ty::Bool | ty::Char @@ -954,7 +968,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { | ty::Foreign(..) => return self, }; - if self.kind == kind { self } else { folder.tcx().mk_ty(kind) } + if *self.kind() == kind { self } else { folder.tcx().mk_ty(kind) } } fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { @@ -962,7 +976,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { - match self.kind { + match self.kind() { ty::RawPtr(ref tm) => tm.visit_with(visitor), ty::Array(typ, sz) => typ.visit_with(visitor) || sz.visit_with(visitor), ty::Slice(typ) => typ.visit_with(visitor), diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c1f354c7a15..724ec101b23 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -367,7 +367,8 @@ impl<'tcx> ClosureSubsts<'tcx> { /// Used primarily by `ty::print::pretty` to be able to handle closure /// types that haven't had their synthetic types substituted in. pub fn is_valid(self) -> bool { - self.substs.len() >= 3 && matches!(self.split().tupled_upvars_ty.expect_ty().kind, Tuple(_)) + self.substs.len() >= 3 + && matches!(self.split().tupled_upvars_ty.expect_ty().kind(), Tuple(_)) } /// Returns the substitutions of the closure's parent. @@ -414,9 +415,9 @@ impl<'tcx> ClosureSubsts<'tcx> { /// Extracts the signature from the closure. pub fn sig(self) -> ty::PolyFnSig<'tcx> { let ty = self.sig_as_fn_ptr_ty(); - match ty.kind { - ty::FnPtr(sig) => sig, - _ => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {:?}", ty.kind), + match ty.kind() { + ty::FnPtr(sig) => *sig, + _ => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {:?}", ty.kind()), } } } @@ -484,7 +485,8 @@ impl<'tcx> GeneratorSubsts<'tcx> { /// Used primarily by `ty::print::pretty` to be able to handle generator /// types that haven't had their synthetic types substituted in. pub fn is_valid(self) -> bool { - self.substs.len() >= 5 && matches!(self.split().tupled_upvars_ty.expect_ty().kind, Tuple(_)) + self.substs.len() >= 5 + && matches!(self.split().tupled_upvars_ty.expect_ty().kind(), Tuple(_)) } /// Returns the substitutions of the generator's parent. @@ -1231,13 +1233,13 @@ rustc_index::newtype_index! { /// particular, imagine a type like this: /// /// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char) - /// ^ ^ | | | - /// | | | | | - /// | +------------+ 0 | | - /// | | | - /// +--------------------------------+ 1 | - /// | | - /// +------------------------------------------+ 0 + /// ^ ^ | | | + /// | | | | | + /// | +------------+ 0 | | + /// | | | + /// +----------------------------------+ 1 | + /// | | + /// +----------------------------------------------+ 0 /// /// In this type, there are two binders (the outer fn and the inner /// fn). We need to be able to determine, for any given region, which @@ -1741,9 +1743,19 @@ impl RegionKind { /// Type utilities impl<'tcx> TyS<'tcx> { + #[inline(always)] + pub fn kind(&self) -> &TyKind<'tcx> { + &self.kind + } + + #[inline(always)] + pub fn flags(&self) -> TypeFlags { + self.flags + } + #[inline] pub fn is_unit(&self) -> bool { - match self.kind { + match self.kind() { Tuple(ref tys) => tys.is_empty(), _ => false, } @@ -1751,7 +1763,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_never(&self) -> bool { - match self.kind { + match self.kind() { Never => true, _ => false, } @@ -1766,7 +1778,7 @@ impl<'tcx> TyS<'tcx> { pub fn conservative_is_privately_uninhabited(&self, tcx: TyCtxt<'tcx>) -> bool { // FIXME(varkor): we can make this less conversative by substituting concrete // type arguments. - match self.kind { + match self.kind() { ty::Never => true, ty::Adt(def, _) if def.is_union() => { // For now, `union`s are never considered uninhabited. @@ -1806,12 +1818,28 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_primitive(&self) -> bool { - self.kind.is_primitive() + self.kind().is_primitive() + } + + #[inline] + pub fn is_adt(&self) -> bool { + match self.kind() { + Adt(..) => true, + _ => false, + } + } + + #[inline] + pub fn is_ref(&self) -> bool { + match self.kind() { + Ref(..) => true, + _ => false, + } } #[inline] pub fn is_ty_var(&self) -> bool { - match self.kind { + match self.kind() { Infer(TyVar(_)) => true, _ => false, } @@ -1819,7 +1847,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_ty_infer(&self) -> bool { - match self.kind { + match self.kind() { Infer(_) => true, _ => false, } @@ -1827,23 +1855,23 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_phantom_data(&self) -> bool { - if let Adt(def, _) = self.kind { def.is_phantom_data() } else { false } + if let Adt(def, _) = self.kind() { def.is_phantom_data() } else { false } } #[inline] pub fn is_bool(&self) -> bool { - self.kind == Bool + *self.kind() == Bool } /// Returns `true` if this type is a `str`. #[inline] pub fn is_str(&self) -> bool { - self.kind == Str + *self.kind() == Str } #[inline] pub fn is_param(&self, index: u32) -> bool { - match self.kind { + match self.kind() { ty::Param(ref data) => data.index == index, _ => false, } @@ -1851,8 +1879,8 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_slice(&self) -> bool { - match self.kind { - RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => match ty.kind { + match self.kind() { + RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => match ty.kind() { Slice(_) | Str => true, _ => false, }, @@ -1861,15 +1889,23 @@ impl<'tcx> TyS<'tcx> { } #[inline] + pub fn is_array(&self) -> bool { + match self.kind() { + Array(..) => true, + _ => false, + } + } + + #[inline] pub fn is_simd(&self) -> bool { - match self.kind { + match self.kind() { Adt(def, _) => def.repr.simd(), _ => false, } } pub fn sequence_element_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.kind { + match self.kind() { Array(ty, _) | Slice(ty) => ty, Str => tcx.mk_mach_uint(ast::UintTy::U8), _ => bug!("`sequence_element_type` called on non-sequence value: {}", self), @@ -1877,7 +1913,7 @@ impl<'tcx> TyS<'tcx> { } pub fn simd_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.kind { + match self.kind() { Adt(def, substs) => def.non_enum_variant().fields[0].ty(tcx, substs), _ => bug!("`simd_type` called on invalid type"), } @@ -1886,14 +1922,14 @@ impl<'tcx> TyS<'tcx> { pub fn simd_size(&self, _tcx: TyCtxt<'tcx>) -> u64 { // Parameter currently unused, but probably needed in the future to // allow `#[repr(simd)] struct Simd<T, const N: usize>([T; N]);`. - match self.kind { + match self.kind() { Adt(def, _) => def.non_enum_variant().fields.len() as u64, _ => bug!("`simd_size` called on invalid type"), } } pub fn simd_size_and_type(&self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { - match self.kind { + match self.kind() { Adt(def, substs) => { let variant = def.non_enum_variant(); (variant.fields.len() as u64, variant.fields[0].ty(tcx, substs)) @@ -1904,7 +1940,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_region_ptr(&self) -> bool { - match self.kind { + match self.kind() { Ref(..) => true, _ => false, } @@ -1912,7 +1948,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_mutable_ptr(&self) -> bool { - match self.kind { + match self.kind() { RawPtr(TypeAndMut { mutbl: hir::Mutability::Mut, .. }) | Ref(_, _, hir::Mutability::Mut) => true, _ => false, @@ -1921,7 +1957,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_unsafe_ptr(&self) -> bool { - match self.kind { + match self.kind() { RawPtr(_) => true, _ => false, } @@ -1935,7 +1971,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_box(&self) -> bool { - match self.kind { + match self.kind() { Adt(def, _) => def.is_box(), _ => false, } @@ -1943,7 +1979,7 @@ impl<'tcx> TyS<'tcx> { /// Panics if called on any type other than `Box<T>`. pub fn boxed_ty(&self) -> Ty<'tcx> { - match self.kind { + match self.kind() { Adt(def, substs) if def.is_box() => substs.type_at(0), _ => bug!("`boxed_ty` is called on non-box type {:?}", self), } @@ -1954,7 +1990,7 @@ impl<'tcx> TyS<'tcx> { /// contents are abstract to rustc.) #[inline] pub fn is_scalar(&self) -> bool { - match self.kind { + match self.kind() { Bool | Char | Int(_) @@ -1971,7 +2007,7 @@ impl<'tcx> TyS<'tcx> { /// Returns `true` if this type is a floating point type. #[inline] pub fn is_floating_point(&self) -> bool { - match self.kind { + match self.kind() { Float(_) | Infer(FloatVar(_)) => true, _ => false, } @@ -1979,7 +2015,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_trait(&self) -> bool { - match self.kind { + match self.kind() { Dynamic(..) => true, _ => false, } @@ -1987,7 +2023,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_enum(&self) -> bool { - match self.kind { + match self.kind() { Adt(adt_def, _) => adt_def.is_enum(), _ => false, } @@ -1995,7 +2031,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_closure(&self) -> bool { - match self.kind { + match self.kind() { Closure(..) => true, _ => false, } @@ -2003,7 +2039,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_generator(&self) -> bool { - match self.kind { + match self.kind() { Generator(..) => true, _ => false, } @@ -2011,7 +2047,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_integral(&self) -> bool { - match self.kind { + match self.kind() { Infer(IntVar(_)) | Int(_) | Uint(_) => true, _ => false, } @@ -2019,7 +2055,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_fresh_ty(&self) -> bool { - match self.kind { + match self.kind() { Infer(FreshTy(_)) => true, _ => false, } @@ -2027,7 +2063,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_fresh(&self) -> bool { - match self.kind { + match self.kind() { Infer(FreshTy(_)) => true, Infer(FreshIntTy(_)) => true, Infer(FreshFloatTy(_)) => true, @@ -2037,7 +2073,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_char(&self) -> bool { - match self.kind { + match self.kind() { Char => true, _ => false, } @@ -2050,7 +2086,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_signed(&self) -> bool { - match self.kind { + match self.kind() { Int(_) => true, _ => false, } @@ -2058,7 +2094,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_ptr_sized_integral(&self) -> bool { - match self.kind { + match self.kind() { Int(ast::IntTy::Isize) | Uint(ast::UintTy::Usize) => true, _ => false, } @@ -2066,7 +2102,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_machine(&self) -> bool { - match self.kind { + match self.kind() { Int(..) | Uint(..) | Float(..) => true, _ => false, } @@ -2074,7 +2110,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn has_concrete_skeleton(&self) -> bool { - match self.kind { + match self.kind() { Param(_) | Infer(_) | Error(_) => false, _ => true, } @@ -2085,28 +2121,28 @@ impl<'tcx> TyS<'tcx> { /// The parameter `explicit` indicates if this is an *explicit* dereference. /// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly. pub fn builtin_deref(&self, explicit: bool) -> Option<TypeAndMut<'tcx>> { - match self.kind { + match self.kind() { Adt(def, _) if def.is_box() => { Some(TypeAndMut { ty: self.boxed_ty(), mutbl: hir::Mutability::Not }) } - Ref(_, ty, mutbl) => Some(TypeAndMut { ty, mutbl }), - RawPtr(mt) if explicit => Some(mt), + Ref(_, ty, mutbl) => Some(TypeAndMut { ty, mutbl: *mutbl }), + RawPtr(mt) if explicit => Some(*mt), _ => None, } } /// Returns the type of `ty[i]`. pub fn builtin_index(&self) -> Option<Ty<'tcx>> { - match self.kind { + match self.kind() { Array(ty, _) | Slice(ty) => Some(ty), _ => None, } } pub fn fn_sig(&self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> { - match self.kind { - FnDef(def_id, substs) => tcx.fn_sig(def_id).subst(tcx, substs), - FnPtr(f) => f, + match self.kind() { + FnDef(def_id, substs) => tcx.fn_sig(*def_id).subst(tcx, substs), + FnPtr(f) => *f, Error(_) => { // ignore errors (#54954) ty::Binder::dummy(FnSig::fake()) @@ -2120,7 +2156,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_fn(&self) -> bool { - match self.kind { + match self.kind() { FnDef(..) | FnPtr(_) => true, _ => false, } @@ -2128,7 +2164,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_fn_ptr(&self) -> bool { - match self.kind { + match self.kind() { FnPtr(_) => true, _ => false, } @@ -2136,7 +2172,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_impl_trait(&self) -> bool { - match self.kind { + match self.kind() { Opaque(..) => true, _ => false, } @@ -2144,7 +2180,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn ty_adt_def(&self) -> Option<&'tcx AdtDef> { - match self.kind { + match self.kind() { Adt(adt, _) => Some(adt), _ => None, } @@ -2153,7 +2189,7 @@ impl<'tcx> TyS<'tcx> { /// Iterates over tuple fields. /// Panics when called on anything but a tuple. pub fn tuple_fields(&self) -> impl DoubleEndedIterator<Item = Ty<'tcx>> { - match self.kind { + match self.kind() { Tuple(substs) => substs.iter().map(|field| field.expect_ty()), _ => bug!("tuple_fields called on non-tuple"), } @@ -2164,10 +2200,10 @@ impl<'tcx> TyS<'tcx> { // FIXME: This requires the optimized MIR in the case of generators. #[inline] pub fn variant_range(&self, tcx: TyCtxt<'tcx>) -> Option<Range<VariantIdx>> { - match self.kind { + match self.kind() { TyKind::Adt(adt, _) => Some(adt.variant_range()), TyKind::Generator(def_id, substs, _) => { - Some(substs.as_generator().variant_range(def_id, tcx)) + Some(substs.as_generator().variant_range(*def_id, tcx)) } _ => None, } @@ -2183,7 +2219,7 @@ impl<'tcx> TyS<'tcx> { tcx: TyCtxt<'tcx>, variant_index: VariantIdx, ) -> Option<Discr<'tcx>> { - match self.kind { + match self.kind() { TyKind::Adt(adt, _) if adt.variants.is_empty() => { bug!("discriminant_for_variant called on zero variant enum"); } @@ -2191,7 +2227,7 @@ impl<'tcx> TyS<'tcx> { Some(adt.discriminant_for_variant(tcx, variant_index)) } TyKind::Generator(def_id, substs, _) => { - Some(substs.as_generator().discriminant_for_variant(def_id, tcx, variant_index)) + Some(substs.as_generator().discriminant_for_variant(*def_id, tcx, variant_index)) } _ => None, } @@ -2199,7 +2235,7 @@ impl<'tcx> TyS<'tcx> { /// Returns the type of the discriminant of this type. pub fn discriminant_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.kind { + match self.kind() { ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx), ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), _ => { @@ -2222,7 +2258,7 @@ impl<'tcx> TyS<'tcx> { /// inferred. Once upvar inference (in `src/librustc_typeck/check/upvar.rs`) /// is complete, that type variable will be unified. pub fn to_opt_closure_kind(&self) -> Option<ty::ClosureKind> { - match self.kind { + match self.kind() { Int(int_ty) => match int_ty { ast::IntTy::I8 => Some(ty::ClosureKind::Fn), ast::IntTy::I16 => Some(ty::ClosureKind::FnMut), @@ -2244,8 +2280,14 @@ impl<'tcx> TyS<'tcx> { /// /// Returning true means the type is known to be sized. Returning /// `false` means nothing -- could be sized, might not be. + /// + /// Note that we could never rely on the fact that a type such as `[_]` is + /// trivially `!Sized` because we could be in a type environment with a + /// bound such as `[_]: Copy`. A function with such a bound obviously never + /// can be called, but that doesn't mean it shouldn't typecheck. This is why + /// this method doesn't return `Option<bool>`. pub fn is_trivially_sized(&self, tcx: TyCtxt<'tcx>) -> bool { - match self.kind { + match self.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) @@ -2280,9 +2322,4 @@ impl<'tcx> TyS<'tcx> { } } } - - /// Is this a zero-sized type? - pub fn is_zst(&'tcx self, tcx: TyCtxt<'tcx>, did: DefId) -> bool { - tcx.layout_of(tcx.param_env(did).and(self)).map(|layout| layout.is_zst()).unwrap_or(false) - } } diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index acd58ab7f96..1bd3bcb6a4d 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -486,7 +486,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { return t; } - match t.kind { + match *t.kind() { ty::Param(p) => self.ty_for_param(p, t), _ => t.super_fold_with(self), } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 86fe3ac3751..9d5b558234b 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -167,7 +167,7 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// Returns a vector containing all impls + /// Returns an iterator containing all impls pub fn all_impls(self, def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx { let TraitImpls { blanket_impls, non_blanket_impls } = self.trait_impls_of(def_id); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 63d4dcca080..4127b6535bc 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -33,7 +33,7 @@ pub struct Discr<'tcx> { impl<'tcx> fmt::Display for Discr<'tcx> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.ty.kind { + match *self.ty.kind() { ty::Int(ity) => { let size = ty::tls::with(|tcx| Integer::from_attr(&tcx, SignedInt(ity)).size()); let x = self.val; @@ -59,7 +59,7 @@ fn unsigned_max(size: Size) -> u128 { } fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) { - let (int, signed) = match ty.kind { + let (int, signed) = match *ty.kind() { Int(ity) => (Integer::from_attr(&tcx, SignedInt(ity)), true), Uint(uty) => (Integer::from_attr(&tcx, UnsignedInt(uty)), false), _ => bug!("non integer discriminant"), @@ -170,14 +170,12 @@ impl<'tcx> TyCtxt<'tcx> { }); hasher.finish() } -} -impl<'tcx> TyCtxt<'tcx> { pub fn has_error_field(self, ty: Ty<'tcx>) -> bool { - if let ty::Adt(def, substs) = ty.kind { + if let ty::Adt(def, substs) = *ty.kind() { for field in def.all_fields() { let field_ty = field.ty(self, substs); - if let Error(_) = field_ty.kind { + if let Error(_) = field_ty.kind() { return true; } } @@ -225,7 +223,7 @@ impl<'tcx> TyCtxt<'tcx> { normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>, ) -> Ty<'tcx> { loop { - match ty.kind { + match *ty.kind() { ty::Adt(def, substs) => { if !def.is_struct() { break; @@ -298,7 +296,7 @@ impl<'tcx> TyCtxt<'tcx> { ) -> (Ty<'tcx>, Ty<'tcx>) { let (mut a, mut b) = (source, target); loop { - match (&a.kind, &b.kind) { + match (&a.kind(), &b.kind()) { (&Adt(a_def, a_substs), &Adt(b_def, b_substs)) if a_def == b_def && a_def.is_struct() => { @@ -401,12 +399,12 @@ impl<'tcx> TyCtxt<'tcx> { // <P1, P2, P0>, and then look up which of the impl substs refer to // parameters marked as pure. - let impl_substs = match self.type_of(impl_def_id).kind { + let impl_substs = match *self.type_of(impl_def_id).kind() { ty::Adt(def_, substs) if def_ == def => substs, _ => bug!(), }; - let item_substs = match self.type_of(def.did).kind { + let item_substs = match *self.type_of(def.did).kind() { ty::Adt(def_, substs) if def_ == def => substs, _ => bug!(), }; @@ -526,22 +524,22 @@ impl<'tcx> TyCtxt<'tcx> { } /// Returns `true` if the node pointed to by `def_id` is a `static` item. - pub fn is_static(&self, def_id: DefId) -> bool { + pub fn is_static(self, def_id: DefId) -> bool { self.static_mutability(def_id).is_some() } /// Returns `true` if this is a `static` item with the `#[thread_local]` attribute. - pub fn is_thread_local_static(&self, def_id: DefId) -> bool { + pub fn is_thread_local_static(self, def_id: DefId) -> bool { self.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) } /// Returns `true` if the node pointed to by `def_id` is a mutable `static` item. - pub fn is_mutable_static(&self, def_id: DefId) -> bool { + pub fn is_mutable_static(self, def_id: DefId) -> bool { self.static_mutability(def_id) == Some(hir::Mutability::Mut) } /// Get the type of the pointer to the static that we use in MIR. - pub fn static_ptr_ty(&self, def_id: DefId) -> Ty<'tcx> { + pub fn static_ptr_ty(self, def_id: DefId) -> Ty<'tcx> { // Make sure that any constants in the static's type are evaluated. let static_ty = self.normalize_erasing_regions(ty::ParamEnv::empty(), self.type_of(def_id)); @@ -640,7 +638,7 @@ impl<'tcx> ty::TyS<'tcx> { /// Returns the maximum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. pub fn numeric_max_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> { - let val = match self.kind { + let val = match self.kind() { ty::Int(_) | ty::Uint(_) => { let (size, signed) = int_size_and_signed(tcx, self); let val = if signed { signed_max(size) as u128 } else { unsigned_max(size) }; @@ -659,7 +657,7 @@ impl<'tcx> ty::TyS<'tcx> { /// Returns the minimum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. pub fn numeric_min_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> { - let val = match self.kind { + let val = match self.kind() { ty::Int(_) | ty::Uint(_) => { let (size, signed) = int_size_and_signed(tcx, self); let val = if signed { truncate(signed_min(size) as u128, size) } else { 0 }; @@ -717,7 +715,7 @@ impl<'tcx> ty::TyS<'tcx> { /// Returning true means the type is known to be `Freeze`. Returning /// `false` means nothing -- could be `Freeze`, might not be. fn is_trivially_freeze(&self) -> bool { - match self.kind { + match self.kind() { ty::Int(_) | ty::Uint(_) | ty::Float(_) @@ -793,7 +791,7 @@ impl<'tcx> ty::TyS<'tcx> { /// down, you will need to use a type visitor. #[inline] pub fn is_structural_eq_shallow(&'tcx self, tcx: TyCtxt<'tcx>) -> bool { - match self.kind { + match self.kind() { // Look for an impl of both `PartialStructuralEq` and `StructuralEq`. Adt(..) => tcx.has_structural_eq_impls(self), @@ -828,7 +826,7 @@ impl<'tcx> ty::TyS<'tcx> { } pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.kind, &b.kind) { + match (&a.kind(), &b.kind()) { (&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => { if did_a != did_b { return false; @@ -860,7 +858,7 @@ impl<'tcx> ty::TyS<'tcx> { representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>, ty: Ty<'tcx>, ) -> Representability { - match ty.kind { + match ty.kind() { Tuple(..) => { // Find non representable fold_repr(ty.tuple_fields().map(|ty| { @@ -909,7 +907,7 @@ impl<'tcx> ty::TyS<'tcx> { } fn same_struct_or_enum<'tcx>(ty: Ty<'tcx>, def: &'tcx ty::AdtDef) -> bool { - match ty.kind { + match *ty.kind() { Adt(ty_def, _) => ty_def == def, _ => false, } @@ -947,7 +945,7 @@ impl<'tcx> ty::TyS<'tcx> { representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>, ty: Ty<'tcx>, ) -> Representability { - match ty.kind { + match ty.kind() { Adt(def, _) => { { // Iterate through stack of previously seen types. @@ -962,7 +960,7 @@ impl<'tcx> ty::TyS<'tcx> { // struct Bar<T> { x: Bar<Foo> } if let Some(&seen_type) = iter.next() { - if same_struct_or_enum(seen_type, def) { + if same_struct_or_enum(seen_type, *def) { debug!("SelfRecursive: {:?} contains {:?}", seen_type, ty); return Representability::SelfRecursive(vec![sp]); } @@ -1024,7 +1022,7 @@ impl<'tcx> ty::TyS<'tcx> { /// - `&'a *const &'b u8 -> *const &'b u8` pub fn peel_refs(&'tcx self) -> Ty<'tcx> { let mut ty = self; - while let Ref(_, inner_ty, _) = ty.kind { + while let Ref(_, inner_ty, _) = ty.kind() { ty = inner_ty; } ty @@ -1070,7 +1068,7 @@ impl<'tcx> ExplicitSelf<'tcx> { { use self::ExplicitSelf::*; - match self_arg_ty.kind { + match *self_arg_ty.kind() { _ if is_self_ty(self_arg_ty) => ByValue, ty::Ref(region, ty, mutbl) if is_self_ty(ty) => ByReference(region, mutbl), ty::RawPtr(ty::TypeAndMut { ty, mutbl }) if is_self_ty(ty) => ByRawPointer(mutbl), @@ -1087,7 +1085,7 @@ pub fn needs_drop_components( ty: Ty<'tcx>, target_layout: &TargetDataLayout, ) -> Result<SmallVec<[Ty<'tcx>; 2]>, AlwaysRequiresDrop> { - match ty.kind { + match ty.kind() { ty::Infer(ty::FreshIntTy(_)) | ty::Infer(ty::FreshFloatTy(_)) | ty::Bool diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 82c649b8f54..80ade7dda4c 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -3,6 +3,7 @@ use crate::ty; use crate::ty::subst::{GenericArg, GenericArgKind}; +use rustc_data_structures::mini_set::MiniSet; use smallvec::{self, SmallVec}; // The TypeWalker's stack is hot enough that it's worth going to some effort to @@ -12,11 +13,20 @@ type TypeWalkerStack<'tcx> = SmallVec<[GenericArg<'tcx>; 8]>; pub struct TypeWalker<'tcx> { stack: TypeWalkerStack<'tcx>, last_subtree: usize, + visited: MiniSet<GenericArg<'tcx>>, } +/// An iterator for walking the type tree. +/// +/// It's very easy to produce a deeply +/// nested type tree with a lot of +/// identical subtrees. In order to work efficiently +/// in this situation walker only visits each type once. +/// It maintains a set of visited types and +/// skips any types that are already there. impl<'tcx> TypeWalker<'tcx> { - pub fn new(root: GenericArg<'tcx>) -> TypeWalker<'tcx> { - TypeWalker { stack: smallvec![root], last_subtree: 1 } + pub fn new(root: GenericArg<'tcx>) -> Self { + Self { stack: smallvec![root], last_subtree: 1, visited: MiniSet::new() } } /// Skips the subtree corresponding to the last type @@ -41,11 +51,15 @@ impl<'tcx> Iterator for TypeWalker<'tcx> { fn next(&mut self) -> Option<GenericArg<'tcx>> { debug!("next(): stack={:?}", self.stack); - let next = self.stack.pop()?; - self.last_subtree = self.stack.len(); - push_inner(&mut self.stack, next); - debug!("next: stack={:?}", self.stack); - Some(next) + loop { + let next = self.stack.pop()?; + self.last_subtree = self.stack.len(); + if self.visited.insert(next) { + push_inner(&mut self.stack, next); + debug!("next: stack={:?}", self.stack); + return Some(next); + } + } } } @@ -55,7 +69,7 @@ impl GenericArg<'tcx> { /// that appear in `self`, it does not descend into the fields of /// structs or variants. For example: /// - /// ```notrust + /// ```text /// isize => { isize } /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize } /// [isize] => { [isize], isize } @@ -67,9 +81,17 @@ impl GenericArg<'tcx> { /// Iterator that walks the immediate children of `self`. Hence /// `Foo<Bar<i32>, u32>` yields the sequence `[Bar<i32>, u32]` /// (but not `i32`, like `walk`). - pub fn walk_shallow(self) -> impl Iterator<Item = GenericArg<'tcx>> { + /// + /// Iterator only walks items once. + /// It accepts visited set, updates it with all visited types + /// and skips any types that are already there. + pub fn walk_shallow( + self, + visited: &mut MiniSet<GenericArg<'tcx>>, + ) -> impl Iterator<Item = GenericArg<'tcx>> { let mut stack = SmallVec::new(); push_inner(&mut stack, self); + stack.retain(|a| visited.insert(*a)); stack.into_iter() } } @@ -80,7 +102,7 @@ impl<'tcx> super::TyS<'tcx> { /// that appear in `self`, it does not descend into the fields of /// structs or variants. For example: /// - /// ```notrust + /// ```text /// isize => { isize } /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize } /// [isize] => { [isize], isize } @@ -98,7 +120,7 @@ impl<'tcx> super::TyS<'tcx> { // types as they are written). fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) { match parent.unpack() { - GenericArgKind::Type(parent_ty) => match parent_ty.kind { + GenericArgKind::Type(parent_ty) => match *parent_ty.kind() { ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml index 6b0412ece7a..a6d22243d6d 100644 --- a/compiler/rustc_mir/Cargo.toml +++ b/compiler/rustc_mir/Cargo.toml @@ -10,10 +10,11 @@ doctest = false [dependencies] either = "1.5.0" rustc_graphviz = { path = "../rustc_graphviz" } -itertools = "0.8" +itertools = "0.9" tracing = "0.1" log_settings = "0.1.1" polonius-engine = "0.12.0" +regex = "1" rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_mir/src/borrow_check/def_use.rs b/compiler/rustc_mir/src/borrow_check/def_use.rs index 6574e584406..689ec249a2f 100644 --- a/compiler/rustc_mir/src/borrow_check/def_use.rs +++ b/compiler/rustc_mir/src/borrow_check/def_use.rs @@ -72,8 +72,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> { PlaceContext::MutatingUse(MutatingUseContext::Drop) => Some(DefUse::Drop), - // Coverage and debug info are neither def nor use. - PlaceContext::NonUse(NonUseContext::Coverage) | + // Debug info is neither def nor use. PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None, } } diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index 9076dbccb52..11122b195c0 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -66,7 +66,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let span = use_spans.args_or_use(); let move_site_vec = self.get_moved_indexes(location, mpi); - debug!("report_use_of_moved_or_uninitialized: move_site_vec={:?}", move_site_vec); + debug!( + "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}", + move_site_vec, use_spans + ); let move_out_indices: Vec<_> = move_site_vec.iter().map(|move_site| move_site.moi).collect(); @@ -229,6 +232,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } + // Deref::deref takes &self, which cannot cause a move + FnSelfUseKind::DerefCoercion { .. } => unreachable!(), } } else { err.span_label( @@ -291,7 +296,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let ty = Place::ty_from(used_place.local, used_place.projection, self.body, self.infcx.tcx) .ty; - let needs_note = match ty.kind { + let needs_note = match ty.kind() { ty::Closure(id, _) => { let tables = self.infcx.tcx.typeck(id.expect_local()); let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local()); @@ -306,7 +311,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let ty = place.ty(self.body, self.infcx.tcx).ty; if is_loop_move { - if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind { + if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { // We have a `&mut` ref, we need to reborrow on each iteration (#62112). err.span_suggestion_verbose( span.shrink_to_lo(), @@ -329,7 +334,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(ref name) => format!("`{}`", name), None => "value".to_owned(), }; - if let ty::Param(param_ty) = ty.kind { + if let ty::Param(param_ty) = ty.kind() { let tcx = self.infcx.tcx; let generics = tcx.generics_of(self.mir_def_id); let param = generics.type_param(¶m_ty, tcx); @@ -355,6 +360,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str); } + if let UseSpans::FnSelfUse { + kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty }, + .. + } = use_spans + { + err.note(&format!( + "{} occurs due to deref coercion to `{}`", + desired_action.as_noun(), + deref_target_ty + )); + + err.span_note(deref_target, "deref defined here"); + } + if let Some((_, mut old_err)) = self.move_error_reported.insert(move_out_indices, (used_place, err)) { @@ -945,7 +964,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { name: &str, borrow: &BorrowData<'tcx>, drop_span: Span, - borrow_spans: UseSpans, + borrow_spans: UseSpans<'tcx>, explanation: BorrowExplanation, ) -> DiagnosticBuilder<'cx> { debug!( @@ -997,7 +1016,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .opt_name(fn_hir_id) .map(|name| format!("function `{}`", name)) .unwrap_or_else(|| { - match &self.infcx.tcx.typeck(self.mir_def_id).node_type(fn_hir_id).kind + match &self + .infcx + .tcx + .typeck(self.mir_def_id) + .node_type(fn_hir_id) + .kind() { ty::Closure(..) => "enclosing closure", ty::Generator(..) => "enclosing generator", @@ -1141,7 +1165,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { location: Location, borrow: &BorrowData<'tcx>, drop_span: Span, - borrow_spans: UseSpans, + borrow_spans: UseSpans<'tcx>, proper_span: Span, explanation: BorrowExplanation, ) -> DiagnosticBuilder<'cx> { @@ -1269,7 +1293,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn report_escaping_closure_capture( &mut self, - use_span: UseSpans, + use_span: UseSpans<'tcx>, var_span: Span, fr_name: &RegionName, category: ConstraintCategory, @@ -1625,7 +1649,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }, ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { let base_ty = Place::ty_from(place.local, proj_base, self.body, tcx).ty; - match base_ty.kind { + match base_ty.kind() { ty::Adt(def, _) if def.has_dtor(tcx) => { // Report the outermost adt with a destructor match base_access { @@ -1689,7 +1713,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None } else { let ty = self.infcx.tcx.type_of(self.mir_def_id); - match ty.kind { + match ty.kind() { ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig( self.mir_def_id.to_def_id(), self.infcx.tcx.fn_sig(self.mir_def_id), @@ -1924,13 +1948,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // 3. The return type is not a reference. In this case, we don't highlight // anything. let return_ty = sig.output(); - match return_ty.skip_binder().kind { + match return_ty.skip_binder().kind() { ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => { // This is case 1 from above, return type is a named reference so we need to // search for relevant arguments. let mut arguments = Vec::new(); for (index, argument) in sig.inputs().skip_binder().iter().enumerate() { - if let ty::Ref(argument_region, _, _) = argument.kind { + if let ty::Ref(argument_region, _, _) = argument.kind() { if argument_region == return_region { // Need to use the `rustc_middle::ty` types to compare against the // `return_region`. Then use the `rustc_hir` type to get only @@ -1976,9 +2000,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Closure arguments are wrapped in a tuple, so we need to get the first // from that. - if let ty::Tuple(elems) = argument_ty.kind { + if let ty::Tuple(elems) = argument_ty.kind() { let argument_ty = elems.first()?.expect_ty(); - if let ty::Ref(_, _, _) = argument_ty.kind { + if let ty::Ref(_, _, _) = argument_ty.kind() { return Some(AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span, @@ -1998,7 +2022,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let return_ty = sig.output().skip_binder(); // We expect the first argument to be a reference. - match argument_ty.kind { + match argument_ty.kind() { ty::Ref(_, _, _) => {} _ => return None, } diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs index b591b938b5a..eccb6168229 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs @@ -102,7 +102,7 @@ impl BorrowExplanation { should_note_order, } => { let local_decl = &body.local_decls[dropped_local]; - let (dtor_desc, type_desc) = match local_decl.ty.kind { + let (dtor_desc, type_desc) = match local_decl.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() => { @@ -501,7 +501,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn later_use_kind( &self, borrow: &BorrowData<'tcx>, - use_spans: UseSpans, + use_spans: UseSpans<'tcx>, location: Location, ) -> (LaterUseKind, Span) { match use_spans { @@ -626,7 +626,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if from == target { debug!("was_captured_by_trait_object: ty={:?}", ty); // Check the type for a trait object. - return match ty.kind { + return match ty.kind() { // `&dyn Trait` ty::Ref(_, ty, _) if ty.is_trait() => true, // `Box<dyn Trait>` diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index dfaa75d9f23..4256f6e39d5 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::{ PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::print::Print; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; +use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt}; use rustc_span::{ hygiene::{DesugaringKind, ForLoopLoc}, symbol::sym, @@ -81,43 +81,41 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let terminator = self.body[location.block].terminator(); debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator); if let TerminatorKind::Call { - func: - Operand::Constant(box Constant { - literal: ty::Const { ty: &ty::TyS { kind: ty::FnDef(id, _), .. }, .. }, - .. - }), + func: Operand::Constant(box Constant { literal: ty::Const { ty: const_ty, .. }, .. }), args, .. } = &terminator.kind { - debug!("add_moved_or_invoked_closure_note: id={:?}", id); - if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() { - let closure = match args.first() { - Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place)) - if target == place.local_or_deref_local() => - { - place.local_or_deref_local().unwrap() - } - _ => return, - }; + if let ty::FnDef(id, _) = *const_ty.kind() { + debug!("add_moved_or_invoked_closure_note: id={:?}", id); + if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() { + let closure = match args.first() { + Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place)) + if target == place.local_or_deref_local() => + { + place.local_or_deref_local().unwrap() + } + _ => return, + }; - debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); - if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind { - let did = did.expect_local(); - let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); + debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); + if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() { + let did = did.expect_local(); + let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); - if let Some((span, name)) = - self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) - { - diag.span_note( - *span, - &format!( - "closure cannot be invoked more than once because it moves the \ - variable `{}` out of its environment", - name, - ), - ); - return; + if let Some((span, name)) = + self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) + { + diag.span_note( + *span, + &format!( + "closure cannot be invoked more than once because it moves the \ + variable `{}` out of its environment", + name, + ), + ); + return; + } } } } @@ -125,7 +123,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Check if we are just moving a closure after it has been invoked. if let Some(target) = target { - if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind { + if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() { let did = did.expect_local(); let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); @@ -152,8 +150,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(mut descr) => { // Surround descr with `backticks`. descr.reserve(2); - descr.insert_str(0, "`"); - descr.push_str("`"); + descr.insert(0, '`'); + descr.push('`'); descr } None => "value".to_string(), @@ -224,7 +222,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if self.upvars[var_index].by_ref { buf.push_str(&name); } else { - buf.push_str(&format!("*{}", &name)); + buf.push('*'); + buf.push_str(&name); } } else { if autoderef { @@ -236,7 +235,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &including_downcast, )?; } else { - buf.push_str(&"*"); + buf.push('*'); self.append_place_to_string( PlaceRef { local, projection: proj_base }, buf, @@ -274,7 +273,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { autoderef, &including_downcast, )?; - buf.push_str(&format!(".{}", field_name)); + buf.push('.'); + buf.push_str(&field_name); } } ProjectionElem::Index(index) => { @@ -286,11 +286,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { autoderef, &including_downcast, )?; - buf.push_str("["); + buf.push('['); if self.append_local_to_string(*index, buf).is_err() { - buf.push_str("_"); + buf.push('_'); } - buf.push_str("]"); + buf.push(']'); } ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { autoderef = true; @@ -303,7 +303,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { autoderef, &including_downcast, )?; - buf.push_str(&"[..]"); + buf.push_str("[..]"); } }; } @@ -365,7 +365,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // If the type is a box, the field is described from the boxed type self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index) } else { - match ty.kind { + match *ty.kind() { ty::Adt(def, _) => { let variant = if let Some(idx) = variant_index { assert!(def.is_enum()); @@ -496,7 +496,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // We need to add synthesized lifetimes where appropriate. We do // this by hooking into the pretty printer and telling it to label the // lifetimes without names with the value `'0`. - match ty.kind { + match ty.kind() { ty::Ref( ty::RegionKind::ReLateBound(_, br) | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }), @@ -516,7 +516,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut s = String::new(); let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS); - let region = match ty.kind { + let region = match ty.kind() { ty::Ref(region, _, _) => { match region { ty::RegionKind::ReLateBound(_, br) @@ -538,7 +538,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// The span(s) associated to a use of a place. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum UseSpans { +pub(super) enum UseSpans<'tcx> { /// The access is caused by capturing a variable for a closure. ClosureUse { /// This is true if the captured variable was from a generator. @@ -558,7 +558,7 @@ pub(super) enum UseSpans { fn_call_span: Span, /// The definition span of the method being called fn_span: Span, - kind: FnSelfUseKind, + kind: FnSelfUseKind<'tcx>, }, /// This access is caused by a `match` or `if let` pattern. PatUse(Span), @@ -567,22 +567,32 @@ pub(super) enum UseSpans { } #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum FnSelfUseKind { +pub(super) enum FnSelfUseKind<'tcx> { /// A normal method call of the form `receiver.foo(a, b, c)` Normal { self_arg: Ident, implicit_into_iter: bool }, /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)` FnOnceCall, /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) Operator { self_arg: Ident }, + DerefCoercion { + /// The `Span` of the `Target` associated type + /// in the `Deref` impl we are using. + deref_target: Span, + /// The type `T::Deref` we are dereferencing to + deref_target_ty: Ty<'tcx>, + }, } -impl UseSpans { +impl UseSpans<'_> { pub(super) fn args_or_use(self) -> Span { match self { UseSpans::ClosureUse { args_span: span, .. } | UseSpans::PatUse(span) - | UseSpans::FnSelfUse { var_span: span, .. } | UseSpans::OtherUse(span) => span, + UseSpans::FnSelfUse { + fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. + } => fn_call_span, + UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -590,8 +600,11 @@ impl UseSpans { match self { UseSpans::ClosureUse { var_span: span, .. } | UseSpans::PatUse(span) - | UseSpans::FnSelfUse { var_span: span, .. } | UseSpans::OtherUse(span) => span, + UseSpans::FnSelfUse { + fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. + } => fn_call_span, + UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -650,7 +663,7 @@ impl UseSpans { " in closure".to_string() } } - _ => "".to_string(), + _ => String::new(), } } @@ -680,7 +693,7 @@ impl BorrowedContentSource<'tcx> { BorrowedContentSource::DerefRawPointer => "a raw pointer".to_string(), BorrowedContentSource::DerefSharedRef => "a shared reference".to_string(), BorrowedContentSource::DerefMutableRef => "a mutable reference".to_string(), - BorrowedContentSource::OverloadedDeref(ty) => match ty.kind { + BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() { ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => { "an `Rc`".to_string() } @@ -712,7 +725,7 @@ impl BorrowedContentSource<'tcx> { BorrowedContentSource::DerefMutableRef => { bug!("describe_for_immutable_place: DerefMutableRef isn't immutable") } - BorrowedContentSource::OverloadedDeref(ty) => match ty.kind { + BorrowedContentSource::OverloadedDeref(ty) => match ty.kind() { ty::Adt(def, _) if tcx.is_diagnostic_item(sym::Rc, def.did) => { "an `Rc`".to_string() } @@ -726,7 +739,7 @@ impl BorrowedContentSource<'tcx> { } fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> { - match func.kind { + match *func.kind() { ty::FnDef(def_id, substs) => { let trait_id = tcx.trait_of_item(def_id)?; @@ -754,7 +767,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, moved_place: PlaceRef<'tcx>, // Could also be an upvar. location: Location, - ) -> UseSpans { + ) -> UseSpans<'tcx> { use self::UseSpans::*; let stmt = match self.body[location.block].statements.get(location.statement_index) { @@ -806,68 +819,79 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { debug!("move_spans: target_temp = {:?}", target_temp); if let Some(Terminator { - kind: TerminatorKind::Call { func, args, fn_span, from_hir_call, .. }, - .. + kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, .. }) = &self.body[location.block].terminator { - let mut method_did = None; - if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { - if let ty::FnDef(def_id, _) = ty.kind { - debug!("move_spans: fn = {:?}", def_id); - if let Some(ty::AssocItem { fn_has_self_parameter, .. }) = - self.infcx.tcx.opt_associated_item(def_id) - { - if *fn_has_self_parameter { - method_did = Some(def_id); - } - } - } - } + let (method_did, method_substs) = if let Some(info) = + crate::util::find_self_call(self.infcx.tcx, &self.body, target_temp, location.block) + { + info + } else { + return normal_ret; + }; let tcx = self.infcx.tcx; - let method_did = if let Some(did) = method_did { did } else { return normal_ret }; - - if let [Operand::Move(self_place), ..] = **args { - if self_place.as_local() == Some(target_temp) { - let parent = tcx.parent(method_did); - let is_fn_once = parent == tcx.lang_items().fn_once_trait(); - let is_operator = !from_hir_call - && parent.map_or(false, |p| { - tcx.lang_items().group(LangItemGroup::Op).contains(&p) - }); - let fn_call_span = *fn_span; - - let self_arg = tcx.fn_arg_names(method_did)[0]; - - let kind = if is_fn_once { - FnSelfUseKind::FnOnceCall - } else if is_operator { - FnSelfUseKind::Operator { self_arg } - } else { - 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)) - ); - FnSelfUseKind::Normal { self_arg, implicit_into_iter } - }; - - return FnSelfUse { - var_span: stmt.source_info.span, - fn_call_span, - fn_span: self - .infcx - .tcx - .sess - .source_map() - .guess_head_span(self.infcx.tcx.def_span(method_did)), - kind, - }; + let parent = tcx.parent(method_did); + let is_fn_once = parent == tcx.lang_items().fn_once_trait(); + let is_operator = !from_hir_call + && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p)); + let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); + let fn_call_span = *fn_span; + + let self_arg = tcx.fn_arg_names(method_did)[0]; + + debug!( + "terminator = {:?} from_hir_call={:?}", + self.body[location.block].terminator, from_hir_call + ); + + // Check for a 'special' use of 'self' - + // an FnOnce call, an operator (e.g. `<<`), or a + // deref coercion. + let kind = if is_fn_once { + Some(FnSelfUseKind::FnOnceCall) + } else if is_operator { + Some(FnSelfUseKind::Operator { self_arg }) + } else if is_deref { + let deref_target = + tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { + Instance::resolve(tcx, self.param_env, deref_target, method_substs) + .transpose() + }); + if let Some(Ok(instance)) = deref_target { + let deref_target_ty = instance.ty(tcx, self.param_env); + Some(FnSelfUseKind::DerefCoercion { + deref_target: tcx.def_span(instance.def_id()), + deref_target_ty, + }) + } else { + None } - } + } else { + None + }; + + 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)) + ); + FnSelfUseKind::Normal { self_arg, implicit_into_iter } + }); + + return FnSelfUse { + var_span: stmt.source_info.span, + fn_call_span, + fn_span: self + .infcx + .tcx + .sess + .source_map() + .guess_head_span(self.infcx.tcx.def_span(method_did)), + kind, + }; } normal_ret } @@ -876,7 +900,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// and its usage of the local assigned at `location`. /// This is done by searching in statements succeeding `location` /// and originating from `maybe_closure_span`. - pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans { + pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> { use self::UseSpans::*; debug!("borrow_spans: use_span={:?} location={:?}", use_span, location); @@ -980,7 +1004,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// Helper to retrieve span(s) of given borrow from the current MIR /// representation - pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans { + pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> { let span = self.body.source_info(borrow.reserve_location).span; self.borrow_spans(span, borrow.reserve_location) } diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs index 1c8da212f10..629e9be9ddd 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs @@ -47,7 +47,7 @@ enum GroupedMoveError<'tcx> { // Everything that isn't from pattern matching. OtherIllegalMove { original_path: Place<'tcx>, - use_spans: UseSpans, + use_spans: UseSpans<'tcx>, kind: IllegalMoveOriginKind<'tcx>, }, } @@ -222,7 +222,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let (mut err, err_span) = { let (span, use_spans, original_path, kind): ( Span, - Option<UseSpans>, + Option<UseSpans<'tcx>>, Place<'tcx>, &IllegalMoveOriginKind<'_>, ) = match error { @@ -291,7 +291,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { move_place: Place<'tcx>, deref_target_place: Place<'tcx>, span: Span, - use_spans: Option<UseSpans>, + use_spans: Option<UseSpans<'tcx>>, ) -> DiagnosticBuilder<'a> { // Inspect the type of the content behind the // borrow to provide feedback about why this @@ -326,7 +326,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } debug!("report: ty={:?}", ty); - let mut err = match ty.kind { + let mut err = match ty.kind() { ty::Array(..) | ty::Slice(..) => { self.cannot_move_out_of_interior_noncopy(span, ty, None) } @@ -385,7 +385,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } }; if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) { - let def_id = match move_place.ty(self.body, self.infcx.tcx).ty.kind { + let def_id = match *move_place.ty(self.body, self.infcx.tcx).ty.kind() { ty::Adt(self_def, _) => self_def.did, ty::Foreign(def_id) | ty::FnDef(def_id, _) @@ -492,8 +492,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { { if let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span) { - if pat_snippet.starts_with('&') { - let pat_snippet = pat_snippet[1..].trim_start(); + if let Some(stripped) = pat_snippet.strip_prefix('&') { + let pat_snippet = stripped.trim_start(); let (suggestion, to_remove) = if pat_snippet.starts_with("mut") && pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace) { diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index d26436ff1de..d4cdf02104a 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -230,7 +230,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // Otherwise, check if the name is the self kewyord - in which case // we have an explicit self. Do the same thing in this case and check // for a `self: &mut Self` to suggest removing the `&mut`. - if let ty::Ref(_, _, hir::Mutability::Mut) = local_decl.ty.kind { + if let ty::Ref(_, _, hir::Mutability::Mut) = local_decl.ty.kind() { true } else { false @@ -509,7 +509,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let def_id = hir.local_def_id(item_id); let tables = self.infcx.tcx.typeck(def_id); if let Some(ty::FnDef(def_id, _)) = - tables.node_type_opt(func.hir_id).as_ref().map(|ty| &ty.kind) + tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind()) { let arg = match hir.get_if_local(*def_id) { Some( @@ -631,9 +631,8 @@ fn suggest_ampmut<'tcx>( let lt_name = &src[1..ws_pos]; let ty = &src[ws_pos..]; return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty)); - } else if src.starts_with('&') { - let borrowed_expr = &src[1..]; - return (assignment_rhs_span, format!("&mut {}", borrowed_expr)); + } else if let Some(stripped) = src.strip_prefix('&') { + return (assignment_rhs_span, format!("&mut {}", stripped)); } } } @@ -687,8 +686,8 @@ fn annotate_struct_field( field: &mir::Field, ) -> Option<(Span, String)> { // Expect our local to be a reference to a struct of some kind. - if let ty::Ref(_, ty, _) = ty.kind { - if let ty::Adt(def, _) = ty.kind { + if let ty::Ref(_, ty, _) = ty.kind() { + if let ty::Adt(def, _) = ty.kind() { let field = def.all_fields().nth(field.index())?; // Use the HIR types to construct the diagnostic message. let hir_id = tcx.hir().local_def_id_to_hir_id(field.did.as_local()?); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs index a775fa59c1b..7505e6e2dd1 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs @@ -115,9 +115,10 @@ impl OutlivesSuggestionBuilder { // should just replace 'a with 'static. // 3) Suggest unifying 'a with 'b if we have both 'a: 'b and 'b: 'a - if outlived.iter().any(|(_, outlived_name)| { - if let RegionNameSource::Static = outlived_name.source { true } else { false } - }) { + if outlived + .iter() + .any(|(_, outlived_name)| matches!(outlived_name.source, RegionNameSource::Static)) + { suggested.push(SuggestedConstraint::Static(fr_name)); } else { // We want to isolate out all lifetimes that should be unified and print out diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs index a0d99ac33c0..eb1f70099fc 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs @@ -364,13 +364,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .struct_span_err(*span, "captured variable cannot escape `FnMut` closure body"); let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty; - if let ty::Opaque(def_id, _) = output_ty.kind { + if let ty::Opaque(def_id, _) = *output_ty.kind() { output_ty = self.infcx.tcx.type_of(def_id) }; debug!("report_fnmut_error: output_ty={:?}", output_ty); - let message = match output_ty.kind { + let message = match output_ty.kind() { ty::Closure(_, _) => { "returns a closure that contains a reference to a captured variable, which then \ escapes the closure body" @@ -387,7 +387,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let ReturnConstraint::ClosureUpvar(upvar) = kind { let def_id = match self.regioncx.universal_regions().defining_ty { DefiningTy::Closure(def_id, _) => def_id, - ty @ _ => bug!("unexpected DefiningTy {:?}", ty), + ty => bug!("unexpected DefiningTy {:?}", ty), }; let upvar_def_span = self.infcx.tcx.hir().span(upvar); @@ -571,13 +571,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let (Some(f), Some(ty::RegionKind::ReStatic)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { - if let Some((&ty::TyS { kind: ty::Opaque(did, substs), .. }, _)) = self + if let Some(&ty::Opaque(did, substs)) = self .infcx .tcx .is_suitable_region(f) .map(|r| r.def_id) - .map(|id| self.infcx.tcx.return_type_impl_trait(id)) - .unwrap_or(None) + .and_then(|id| self.infcx.tcx.return_type_impl_trait(id)) + .map(|(ty, _)| ty.kind()) { // Check whether or not the impl trait return type is intended to capture // data with the static lifetime. diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs index 2603b1e048d..da7bc1564c0 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs @@ -441,7 +441,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty<'_>)> = &mut vec![(ty, hir_ty)]; while let Some((ty, hir_ty)) = search_stack.pop() { - match (&ty.kind, &hir_ty.kind) { + match (&ty.kind(), &hir_ty.kind) { // Check if the `ty` is `&'X ..` where `'X` // is the region we are looking for -- if so, and we have a `&T` // on the RHS, then we want to highlight the `&` like so: diff --git a/compiler/rustc_mir/src/borrow_check/member_constraints.rs b/compiler/rustc_mir/src/borrow_check/member_constraints.rs index d4baa5d809a..baaf6f27ee8 100644 --- a/compiler/rustc_mir/src/borrow_check/member_constraints.rs +++ b/compiler/rustc_mir/src/borrow_check/member_constraints.rs @@ -71,7 +71,7 @@ impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { /// Pushes a member constraint into the set. /// /// The input member constraint `m_c` is in the form produced by - /// the the `rustc_middle::infer` code. + /// the `rustc_middle::infer` code. /// /// The `to_region_vid` callback fn is used to convert the regions /// within into `RegionVid` format -- it typically consults the diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index 86908eaabd1..e4237482f47 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -17,7 +17,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, InstanceDef, RegionVid, TyCtxt}; +use rustc_middle::ty::{self, InstanceDef, ParamEnv, RegionVid, TyCtxt}; use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; use rustc_span::{Span, Symbol, DUMMY_SP}; @@ -205,6 +205,7 @@ fn do_mir_borrowck<'a, 'tcx>( let mut flow_inits = MaybeInitializedPlaces::new(tcx, &body, &mdpe) .into_engine(tcx, &body, def.did.to_def_id()) + .pass_name("borrowck") .iterate_to_fixpoint() .into_results_cursor(&body); @@ -264,12 +265,15 @@ fn do_mir_borrowck<'a, 'tcx>( let flow_borrows = Borrows::new(tcx, &body, regioncx.clone(), &borrow_set) .into_engine(tcx, &body, def.did.to_def_id()) + .pass_name("borrowck") .iterate_to_fixpoint(); let flow_uninits = MaybeUninitializedPlaces::new(tcx, &body, &mdpe) .into_engine(tcx, &body, def.did.to_def_id()) + .pass_name("borrowck") .iterate_to_fixpoint(); let flow_ever_inits = EverInitializedPlaces::new(tcx, &body, &mdpe) .into_engine(tcx, &body, def.did.to_def_id()) + .pass_name("borrowck") .iterate_to_fixpoint(); let movable_generator = match tcx.hir().get(id) { @@ -287,6 +291,7 @@ fn do_mir_borrowck<'a, 'tcx>( if let Err((move_data, move_errors)) = move_data_results { let mut promoted_mbcx = MirBorrowckCtxt { infcx, + param_env, body: promoted_body, mir_def_id: def.did, move_data: &move_data, @@ -320,6 +325,7 @@ fn do_mir_borrowck<'a, 'tcx>( let mut mbcx = MirBorrowckCtxt { infcx, + param_env, body, mir_def_id: def.did, move_data: &mdpe.move_data, @@ -473,6 +479,7 @@ fn do_mir_borrowck<'a, 'tcx>( crate struct MirBorrowckCtxt<'cx, 'tcx> { crate infcx: &'cx InferCtxt<'cx, 'tcx>, + param_env: ParamEnv<'tcx>, body: &'cx Body<'tcx>, mir_def_id: LocalDefId, move_data: &'cx MoveData<'tcx>, @@ -1758,7 +1765,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { { let place_ty = Place::ty_from(place_span.0.local, base_proj, self.body(), self.infcx.tcx); - if let ty::Array(..) = place_ty.ty.kind { + if let ty::Array(..) = place_ty.ty.kind() { let array_place = PlaceRef { local: place_span.0.local, projection: base_proj }; self.check_if_subslice_element_is_moved( location, @@ -1876,7 +1883,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // be already initialized let tcx = self.infcx.tcx; let base_ty = Place::ty_from(place.local, proj_base, self.body(), tcx).ty; - match base_ty.kind { + match base_ty.kind() { ty::Adt(def, _) if def.has_dtor(tcx) => { self.check_if_path_or_subpath_is_moved( location, InitializationRequiringAction::Assignment, @@ -1979,7 +1986,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // of the union - we should error in that case. let tcx = this.infcx.tcx; if let ty::Adt(def, _) = - Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind + Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind() { if def.is_union() { if this.move_data.path_map[mpi].iter().any(|moi| { @@ -2206,7 +2213,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Place::ty_from(place.local, proj_base, self.body(), self.infcx.tcx).ty; // Check the kind of deref to decide - match base_ty.kind { + match base_ty.kind() { ty::Ref(_, _, mutbl) => { match mutbl { // Shared borrowed data is never mutable diff --git a/compiler/rustc_mir/src/borrow_check/place_ext.rs b/compiler/rustc_mir/src/borrow_check/place_ext.rs index cadf1ebf1b7..52fac3e53ee 100644 --- a/compiler/rustc_mir/src/borrow_check/place_ext.rs +++ b/compiler/rustc_mir/src/borrow_check/place_ext.rs @@ -49,7 +49,7 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { if elem == ProjectionElem::Deref { let ty = Place::ty_from(self.local, proj_base, body, tcx).ty; - match ty.kind { + match ty.kind() { ty::Ref(_, _, hir::Mutability::Not) if i == 0 => { // For references to thread-local statics, we do need // to track the borrow. diff --git a/compiler/rustc_mir/src/borrow_check/places_conflict.rs b/compiler/rustc_mir/src/borrow_check/places_conflict.rs index 246e4826e0e..02c7b7dc200 100644 --- a/compiler/rustc_mir/src/borrow_check/places_conflict.rs +++ b/compiler/rustc_mir/src/borrow_check/places_conflict.rs @@ -210,7 +210,7 @@ fn place_components_conflict<'tcx>( let proj_base = &borrow_place.projection[..access_place.projection.len() + i]; let base_ty = Place::ty_from(borrow_local, proj_base, body, tcx).ty; - match (elem, &base_ty.kind, access) { + match (elem, &base_ty.kind(), access) { (_, _, Shallow(Some(ArtificialField::ArrayLength))) | (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => { // The array length is like additional fields on the @@ -330,7 +330,7 @@ fn place_projection_conflict<'tcx>( Overlap::EqualOrDisjoint } else { let ty = Place::ty_from(pi1_local, pi1_proj_base, body, tcx).ty; - match ty.kind { + match ty.kind() { ty::Adt(def, _) if def.is_union() => { // Different fields of a union, we are basically stuck. debug!("place_element_conflict: STUCK-UNION"); diff --git a/compiler/rustc_mir/src/borrow_check/prefixes.rs b/compiler/rustc_mir/src/borrow_check/prefixes.rs index a2475e0ff29..5bfe02ff3b0 100644 --- a/compiler/rustc_mir/src/borrow_check/prefixes.rs +++ b/compiler/rustc_mir/src/borrow_check/prefixes.rs @@ -121,7 +121,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { // reference. let ty = Place::ty_from(cursor.local, proj_base, self.body, self.tcx).ty; - match ty.kind { + match ty.kind() { ty::RawPtr(_) | ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Not) => { // don't continue traversing over derefs of raw pointers or shared // borrows. diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/values.rs b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs index 8a5a600cfdd..f247d07e1f0 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/values.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs @@ -417,7 +417,7 @@ crate fn location_set_str( fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String { let mut result = String::new(); - result.push_str("{"); + result.push('{'); // Set to Some(l1, l2) when we have observed all the locations // from l1..=l2 (inclusive) but not yet printed them. This @@ -478,7 +478,7 @@ fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String push_location_range(&mut result, location1, location2); } - result.push_str("}"); + result.push('}'); return result; diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index 69c4f633770..3ace14610e2 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -386,7 +386,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { } } - if let ty::FnDef(def_id, substs) = constant.literal.ty.kind { + if let ty::FnDef(def_id, substs) = *constant.literal.ty.kind() { let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); self.cx.normalize_and_prove_instantiated_predicates( instantiated_predicates, @@ -412,7 +412,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { // If we have a binding of the form `let ref x: T = ..` // then remove the outermost reference so we can check the // type annotation for the remaining type. - if let ty::Ref(_, rty, _) = local_decl.ty.kind { + if let ty::Ref(_, rty, _) = local_decl.ty.kind() { rty } else { bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty); @@ -646,7 +646,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { })) } ProjectionElem::Subslice { from, to, from_end } => { - PlaceTy::from_ty(match base_ty.kind { + PlaceTy::from_ty(match base_ty.kind() { ty::Array(inner, _) => { assert!(!from_end, "array subslices should not use from_end"); tcx.mk_array(inner, to - from) @@ -658,7 +658,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { _ => span_mirbug_and_err!(self, place, "slice of non-array {:?}", base_ty), }) } - ProjectionElem::Downcast(maybe_name, index) => match base_ty.kind { + ProjectionElem::Downcast(maybe_name, index) => match base_ty.kind() { ty::Adt(adt_def, _substs) if adt_def.is_enum() => { if index.as_usize() >= adt_def.variants.len() { PlaceTy::from_ty(span_mirbug_and_err!( @@ -738,7 +738,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { let tcx = self.tcx(); let (variant, substs) = match base_ty { - PlaceTy { ty, variant_index: Some(variant_index) } => match ty.kind { + PlaceTy { ty, variant_index: Some(variant_index) } => match *ty.kind() { ty::Adt(adt_def, substs) => (&adt_def.variants[variant_index], substs), ty::Generator(def_id, substs, _) => { let mut variants = substs.as_generator().state_tys(def_id, tcx); @@ -757,7 +757,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } _ => bug!("can't have downcast of non-adt non-generator type"), }, - PlaceTy { ty, variant_index: None } => match ty.kind { + PlaceTy { ty, variant_index: None } => match *ty.kind() { ty::Adt(adt_def, substs) if !adt_def.is_enum() => { (&adt_def.variants[VariantIdx::new(0)], substs) } @@ -1140,7 +1140,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { category: ConstraintCategory, ) -> Fallible<()> { if let Err(terr) = self.sub_types(sub, sup, locations, category) { - if let ty::Opaque(..) = sup.kind { + if let ty::Opaque(..) = sup.kind() { // When you have `let x: impl Foo = ...` in a closure, // the resulting inferend values are stored with the // def-id of the base function. @@ -1283,8 +1283,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { for (&opaque_def_id, opaque_decl) in &opaque_type_map { let resolved_ty = infcx.resolve_vars_if_possible(&opaque_decl.concrete_ty); - let concrete_is_opaque = if let ty::Opaque(def_id, _) = resolved_ty.kind { - def_id == opaque_def_id + let concrete_is_opaque = if let ty::Opaque(def_id, _) = resolved_ty.kind() { + *def_id == opaque_def_id } else { false }; @@ -1486,7 +1486,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } StatementKind::SetDiscriminant { ref place, variant_index } => { let place_type = place.ty(body, tcx).ty; - let adt = match place_type.kind { + let adt = match place_type.kind() { ty::Adt(adt, _) if adt.is_enum() => adt, _ => { span_bug!( @@ -1602,7 +1602,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { TerminatorKind::Call { ref func, ref args, ref destination, from_hir_call, .. } => { let func_ty = func.ty(body, tcx); debug!("check_terminator: call, func_ty={:?}", func_ty); - let sig = match func_ty.kind { + let sig = match func_ty.kind() { ty::FnDef(..) | ty::FnPtr(_) => func_ty.fn_sig(tcx), _ => { span_mirbug!(self, term, "call to non-function {:?}", func_ty); @@ -2093,7 +2093,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety)) => { - let sig = match op.ty(body, tcx).kind { + let sig = match op.ty(body, tcx).kind() { ty::Closure(_, substs) => substs.as_closure().sig(), _ => bug!(), }; @@ -2161,7 +2161,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::Pointer(PointerCast::MutToConstPointer) => { - let ty_from = match op.ty(body, tcx).kind { + let ty_from = match op.ty(body, tcx).kind() { ty::RawPtr(ty::TypeAndMut { ty: ty_from, mutbl: hir::Mutability::Mut, @@ -2176,7 +2176,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return; } }; - let ty_to = match ty.kind { + let ty_to = match ty.kind() { ty::RawPtr(ty::TypeAndMut { ty: ty_to, mutbl: hir::Mutability::Not, @@ -2211,11 +2211,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { CastKind::Pointer(PointerCast::ArrayToPointer) => { let ty_from = op.ty(body, tcx); - let opt_ty_elem = match ty_from.kind { + let opt_ty_elem = match ty_from.kind() { ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: array_ty, - }) => match array_ty.kind { + }) => match array_ty.kind() { ty::Array(ty_elem, _) => Some(ty_elem), _ => None, }, @@ -2235,7 +2235,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } }; - let ty_to = match ty.kind { + let ty_to = match ty.kind() { ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: ty_to, @@ -2301,7 +2301,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { right, ) => { let ty_left = left.ty(body, tcx); - match ty_left.kind { + match ty_left.kind() { // Types with regions are comparable if they have a common super-type. ty::RawPtr(_) | ty::FnPtr(_) => { let ty_right = right.ty(body, tcx); @@ -2512,7 +2512,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let base_ty = Place::ty_from(borrowed_place.local, proj_base, body, tcx).ty; debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); - match base_ty.kind { + match base_ty.kind() { ty::Ref(ref_region, _, mutbl) => { constraints.outlives_constraints.push(OutlivesConstraint { sup: ref_region.to_region_vid(), diff --git a/compiler/rustc_mir/src/borrow_check/universal_regions.rs b/compiler/rustc_mir/src/borrow_check/universal_regions.rs index 9dfc67bcf67..4742113b1a5 100644 --- a/compiler/rustc_mir/src/borrow_check/universal_regions.rs +++ b/compiler/rustc_mir/src/borrow_check/universal_regions.rs @@ -524,7 +524,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let defining_ty = self.infcx.replace_free_regions_with_nll_infer_vars(FR, &defining_ty); - match defining_ty.kind { + match *defining_ty.kind() { ty::Closure(def_id, substs) => DefiningTy::Closure(def_id, substs), ty::Generator(def_id, substs, movability) => { DefiningTy::Generator(def_id, substs, movability) @@ -603,7 +603,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { // flattens this tuple. let (&output, tuplized_inputs) = inputs_and_output.split_last().unwrap(); assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs"); - let inputs = match tuplized_inputs[0].kind { + let inputs = match tuplized_inputs[0].kind() { ty::Tuple(inputs) => inputs, _ => bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]), }; diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs index 291b42c12d7..a0ee7fdc072 100644 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -1,8 +1,8 @@ use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr, MemoryExtra}; use crate::interpret::eval_nullary_intrinsic; use crate::interpret::{ - intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, Immediate, InternKind, - InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RawConst, RefTracking, Scalar, + intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, GlobalId, Immediate, + InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, Scalar, ScalarMaybeUninit, StackPopCleanup, }; @@ -10,6 +10,7 @@ use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::traits::Reveal; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, subst::Subst, TyCtxt}; use rustc_span::source_map::Span; use rustc_target::abi::{Abi, LayoutOf}; @@ -33,7 +34,8 @@ fn eval_body_using_ecx<'mir, 'tcx>( assert!(!layout.is_unsized()); let ret = ecx.allocate(layout, MemoryKind::Stack); - let name = ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id())); + let name = + with_no_trimmed_paths(|| ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id()))); let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p)); trace!("eval_body_using_ecx: pushing stack frame for global: {}{}", name, prom); @@ -102,6 +104,8 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>( ) } +/// This function converts an interpreter value into a constant that is meant for use in the +/// type system. pub(super) fn op_to_const<'tcx>( ecx: &CompileTimeEvalContext<'_, 'tcx>, op: OpTy<'tcx>, @@ -115,8 +119,8 @@ pub(super) fn op_to_const<'tcx>( // `Undef` situation. let try_as_immediate = match op.layout.abi { Abi::Scalar(..) => true, - Abi::ScalarPair(..) => match op.layout.ty.kind { - ty::Ref(_, inner, _) => match inner.kind { + Abi::ScalarPair(..) => match op.layout.ty.kind() { + ty::Ref(_, inner, _) => match *inner.kind() { ty::Slice(elem) => elem == ecx.tcx.types.u8, ty::Str => true, _ => false, @@ -180,63 +184,37 @@ pub(super) fn op_to_const<'tcx>( } } -fn validate_and_turn_into_const<'tcx>( +fn turn_into_const_value<'tcx>( tcx: TyCtxt<'tcx>, - constant: RawConst<'tcx>, + constant: ConstAlloc<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, -) -> ::rustc_middle::mir::interpret::ConstEvalResult<'tcx> { +) -> ConstValue<'tcx> { let cid = key.value; let def_id = cid.instance.def.def_id(); let is_static = tcx.is_static(def_id); let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static); - let val = (|| { - let mplace = ecx.raw_const_to_mplace(constant)?; - // FIXME do not validate promoteds until a decision on - // https://github.com/rust-lang/rust/issues/67465 is made - if cid.promoted.is_none() { - let mut ref_tracking = RefTracking::new(mplace); - while let Some((mplace, path)) = ref_tracking.todo.pop() { - ecx.const_validate_operand( - mplace.into(), - path, - &mut ref_tracking, - /*may_ref_to_static*/ ecx.memory.extra.can_access_statics, - )?; - } - } - // Now that we validated, turn this into a proper constant. - // Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides - // whether they become immediates. - if is_static || cid.promoted.is_some() { - let ptr = mplace.ptr.assert_ptr(); - Ok(ConstValue::ByRef { - alloc: ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(), - offset: ptr.offset, - }) - } else { - Ok(op_to_const(&ecx, mplace.into())) - } - })(); - - val.map_err(|error| { - let err = ConstEvalErr::new(&ecx, error, None); - err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| { - diag.note(note_on_undefined_behavior_error()); - diag.emit(); - }) - }) + let mplace = ecx.raw_const_to_mplace(constant).expect( + "can only fail if layout computation failed, \ + which should have given a good error before ever invoking this function", + ); + assert!( + !is_static || cid.promoted.is_some(), + "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead" + ); + // Turn this into a proper constant. + op_to_const(&ecx, mplace.into()) } -pub fn const_eval_validated_provider<'tcx>( +pub fn eval_to_const_value_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, -) -> ::rustc_middle::mir::interpret::ConstEvalResult<'tcx> { +) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { // see comment in const_eval_raw_provider for what we're doing here if key.param_env.reveal() == Reveal::All { let mut key = key; key.param_env = key.param_env.with_user_facing(); - match tcx.const_eval_validated(key) { + match tcx.eval_to_const_value_raw(key) { // try again with reveal all as requested Err(ErrorHandled::TooGeneric) => {} // deduplicate calls @@ -248,7 +226,7 @@ pub fn const_eval_validated_provider<'tcx>( // Catch such calls and evaluate them instead of trying to load a constant's MIR. if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def { let ty = key.value.instance.ty(tcx, key.param_env); - let substs = match ty.kind { + let substs = match ty.kind() { ty::FnDef(_, substs) => substs, _ => bug!("intrinsic with type {:?}", ty), }; @@ -259,13 +237,13 @@ pub fn const_eval_validated_provider<'tcx>( }); } - tcx.const_eval_raw(key).and_then(|val| validate_and_turn_into_const(tcx, val, key)) + tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) } -pub fn const_eval_raw_provider<'tcx>( +pub fn eval_to_allocation_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, -) -> ::rustc_middle::mir::interpret::ConstEvalRawResult<'tcx> { +) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { // Because the constant is computed twice (once per value of `Reveal`), we are at risk of // reporting the same error twice here. To resolve this, we check whether we can evaluate the // constant in the more restrictive `Reveal::UserFacing`, which most likely already was @@ -277,7 +255,7 @@ pub fn const_eval_raw_provider<'tcx>( if key.param_env.reveal() == Reveal::All { let mut key = key; key.param_env = key.param_env.with_user_facing(); - match tcx.const_eval_raw(key) { + match tcx.eval_to_allocation_raw(key) { // try again with reveal all as requested Err(ErrorHandled::TooGeneric) => {} // deduplicate calls @@ -290,7 +268,7 @@ pub fn const_eval_raw_provider<'tcx>( // The next two lines concatenated contain some discussion: // https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/ // subject/anon_const_instance_printing/near/135980032 - let instance = key.value.instance.to_string(); + let instance = with_no_trimmed_paths(|| key.value.instance.to_string()); trace!("const eval: {:?} ({})", key, instance); } @@ -316,9 +294,8 @@ pub fn const_eval_raw_provider<'tcx>( ); let res = ecx.load_mir(cid.instance.def, cid.promoted); - res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) - .map(|place| RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty }) - .map_err(|error| { + match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { + Err(error) => { let err = ConstEvalErr::new(&ecx, error, None); // errors in statics are always emitted as fatal errors if is_static { @@ -340,7 +317,7 @@ pub fn const_eval_raw_provider<'tcx>( ); } - v + Err(v) } else if let Some(def) = def.as_local() { // constant defined in this crate, we can figure out a lint level! match tcx.def_kind(def.did.to_def_id()) { @@ -354,12 +331,12 @@ pub fn const_eval_raw_provider<'tcx>( // compatibility hazard DefKind::Const | DefKind::AssocConst => { let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - err.report_as_lint( + Err(err.report_as_lint( tcx.at(tcx.def_span(def.did)), "any use of this value will cause an error", hir_id, Some(err.span), - ) + )) } // promoting runtime code is only allowed to error if it references broken // constants any other kind of error will be reported to the user as a @@ -368,31 +345,65 @@ pub fn const_eval_raw_provider<'tcx>( if let Some(p) = cid.promoted { let span = tcx.promoted_mir_of_opt_const_arg(def.to_global())[p].span; if let err_inval!(ReferencedConstant) = err.error { - err.report_as_error( + Err(err.report_as_error( tcx.at(span), "evaluation of constant expression failed", - ) + )) } else { - err.report_as_lint( + Err(err.report_as_lint( tcx.at(span), "reaching this expression at runtime will panic or abort", tcx.hir().local_def_id_to_hir_id(def.did), Some(err.span), - ) + )) } // anything else (array lengths, enum initializers, constant patterns) are // reported as hard errors } else { - err.report_as_error( + Err(err.report_as_error( ecx.tcx.at(ecx.cur_span()), "evaluation of constant value failed", - ) + )) } } } } else { // use of broken constant from other crate - err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant") + Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant")) } - }) + } + Ok(mplace) => { + // Since evaluation had no errors, valiate the resulting constant: + let validation = try { + // FIXME do not validate promoteds until a decision on + // https://github.com/rust-lang/rust/issues/67465 is made + if cid.promoted.is_none() { + let mut ref_tracking = RefTracking::new(mplace); + while let Some((mplace, path)) = ref_tracking.todo.pop() { + ecx.const_validate_operand( + mplace.into(), + path, + &mut ref_tracking, + /*may_ref_to_static*/ ecx.memory.extra.can_access_statics, + )?; + } + } + }; + if let Err(error) = validation { + // Validation failed, report an error + let err = ConstEvalErr::new(&ecx, error, None); + Err(err.struct_error( + ecx.tcx, + "it is undefined behavior to use this value", + |mut diag| { + diag.note(note_on_undefined_behavior_error()); + diag.emit(); + }, + )) + } else { + // Convert to raw constant + Ok(ConstAlloc { alloc_id: mplace.ptr.assert_ptr().alloc_id, ty: mplace.layout.ty }) + } + } + } } diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index b0357c508a3..73ca7e0d471 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -11,7 +11,7 @@ use rustc_ast::Mutability; use rustc_hir::def_id::DefId; use rustc_middle::mir::AssertMessage; use rustc_session::Limit; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{sym, Symbol}; use crate::interpret::{ self, compile_time_machine, AllocId, Allocation, Frame, GlobalId, ImmTy, InterpCx, @@ -51,7 +51,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { let gid = GlobalId { instance, promoted: None }; - let place = self.const_eval_raw(gid)?; + let place = self.eval_to_allocation(gid)?; self.copy_op(place.into(), dest)?; @@ -176,6 +176,38 @@ impl interpret::MayLeak for ! { } } +impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { + fn guaranteed_eq(&mut self, a: Scalar, b: Scalar) -> bool { + match (a, b) { + // Comparisons between integers are always known. + (Scalar::Raw { .. }, Scalar::Raw { .. }) => a == b, + // Equality with integers can never be known for sure. + (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false, + // FIXME: return `true` for when both sides are the same pointer, *except* that + // some things (like functions and vtables) do not have stable addresses + // so we need to be careful around them (see e.g. #73722). + (Scalar::Ptr(_), Scalar::Ptr(_)) => false, + } + } + + fn guaranteed_ne(&mut self, a: Scalar, b: Scalar) -> bool { + match (a, b) { + // Comparisons between integers are always known. + (Scalar::Raw { .. }, Scalar::Raw { .. }) => a != b, + // Comparisons of abstract pointers with null pointers are known if the pointer + // is in bounds, because if they are in bounds, the pointer can't be null. + (Scalar::Raw { data: 0, .. }, Scalar::Ptr(ptr)) + | (Scalar::Ptr(ptr), Scalar::Raw { data: 0, .. }) => !self.memory.ptr_may_be_null(ptr), + // Inequality with integers other than null can never be known for sure. + (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false, + // FIXME: return `true` for at least some comparisons where we can reliably + // determine the result of runtime inequality tests at compile-time. + // Examples include comparison of addresses in different static items. + (Scalar::Ptr(_), Scalar::Ptr(_)) => false, + } + } +} + impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> { compile_time_machine!(<'mir, 'tcx>); @@ -234,12 +266,45 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, _unwind: Option<mir::BasicBlock>, ) -> InterpResult<'tcx> { + // Shared intrinsics. if ecx.emulate_intrinsic(instance, args, ret)? { return Ok(()); } - // An intrinsic that we do not support let intrinsic_name = ecx.tcx.item_name(instance.def_id()); - Err(ConstEvalErrKind::NeedsRfc(format!("calling intrinsic `{}`", intrinsic_name)).into()) + + // CTFE-specific intrinsics. + let (dest, ret) = match ret { + None => { + return Err(ConstEvalErrKind::NeedsRfc(format!( + "calling intrinsic `{}`", + intrinsic_name + )) + .into()); + } + Some(p) => p, + }; + match intrinsic_name { + sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { + let a = ecx.read_immediate(args[0])?.to_scalar()?; + let b = ecx.read_immediate(args[1])?.to_scalar()?; + let cmp = if intrinsic_name == sym::ptr_guaranteed_eq { + ecx.guaranteed_eq(a, b) + } else { + ecx.guaranteed_ne(a, b) + }; + ecx.write_scalar(Scalar::from_bool(cmp), dest)?; + } + _ => { + return Err(ConstEvalErrKind::NeedsRfc(format!( + "calling intrinsic `{}`", + intrinsic_name + )) + .into()); + } + } + + ecx.go_to_block(ret); + Ok(()) } fn assert_panic( diff --git a/compiler/rustc_mir/src/const_eval/mod.rs b/compiler/rustc_mir/src/const_eval/mod.rs index e7eeb4b4de4..c93feb5096b 100644 --- a/compiler/rustc_mir/src/const_eval/mod.rs +++ b/compiler/rustc_mir/src/const_eval/mod.rs @@ -44,7 +44,7 @@ pub(crate) fn destructure_const<'tcx>( let op = ecx.const_to_op(val, None).unwrap(); // We go to `usize` as we cannot allocate anything bigger anyway. - let (field_count, variant, down) = match val.ty.kind { + let (field_count, variant, down) = match val.ty.kind() { ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op), ty::Adt(def, _) if def.variants.is_empty() => { return mir::DestructuredConst { variant: None, fields: tcx.arena.alloc_slice(&[]) }; diff --git a/compiler/rustc_mir/src/dataflow/drop_flag_effects.rs b/compiler/rustc_mir/src/dataflow/drop_flag_effects.rs index 707e136678e..d1d507e54ef 100644 --- a/compiler/rustc_mir/src/dataflow/drop_flag_effects.rs +++ b/compiler/rustc_mir/src/dataflow/drop_flag_effects.rs @@ -53,7 +53,7 @@ fn place_contents_drop_state_cannot_differ<'tcx>( place: mir::Place<'tcx>, ) -> bool { let ty = place.ty(body, tcx).ty; - match ty.kind { + match ty.kind() { ty::Array(..) => { debug!( "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false", diff --git a/compiler/rustc_mir/src/dataflow/framework/cursor.rs b/compiler/rustc_mir/src/dataflow/framework/cursor.rs index 4f5930dc3f5..4942bed656c 100644 --- a/compiler/rustc_mir/src/dataflow/framework/cursor.rs +++ b/compiler/rustc_mir/src/dataflow/framework/cursor.rs @@ -4,6 +4,7 @@ use std::borrow::Borrow; use std::cmp::Ordering; use rustc_index::bit_set::BitSet; +use rustc_index::vec::Idx; use rustc_middle::mir::{self, BasicBlock, Location}; use super::{Analysis, Direction, Effect, EffectIndex, Results}; @@ -26,7 +27,7 @@ where { body: &'mir mir::Body<'tcx>, results: R, - state: BitSet<A::Idx>, + state: A::Domain, pos: CursorPosition, @@ -46,17 +47,16 @@ where { /// Returns a new cursor that can inspect `results`. pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { - let bits_per_block = results.borrow().entry_set_for_block(mir::START_BLOCK).domain_size(); - + let bottom_value = results.borrow().analysis.bottom_value(body); ResultsCursor { body, results, - // Initialize to an empty `BitSet` and set `state_needs_reset` to tell the cursor that + // Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that // it needs to reset to block entry before the first seek. The cursor position is // immaterial. state_needs_reset: true, - state: BitSet::new_empty(bits_per_block), + state: bottom_value, pos: CursorPosition::block_entry(mir::START_BLOCK), #[cfg(debug_assertions)] @@ -68,23 +68,21 @@ where self.body } - /// Returns the `Analysis` used to generate the underlying results. + /// Returns the underlying `Results`. + pub fn results(&self) -> &Results<'tcx, A> { + &self.results.borrow() + } + + /// Returns the `Analysis` used to generate the underlying `Results`. pub fn analysis(&self) -> &A { &self.results.borrow().analysis } /// Returns the dataflow state at the current location. - pub fn get(&self) -> &BitSet<A::Idx> { + pub fn get(&self) -> &A::Domain { &self.state } - /// Returns `true` if the dataflow state at the current location contains the given element. - /// - /// Shorthand for `self.get().contains(elem)` - pub fn contains(&self, elem: A::Idx) -> bool { - self.state.contains(elem) - } - /// Resets the cursor to hold the entry set for the given basic block. /// /// For forward dataflow analyses, this is the dataflow state prior to the first statement. @@ -94,7 +92,7 @@ where #[cfg(debug_assertions)] assert!(self.reachable_blocks.contains(block)); - self.state.overwrite(&self.results.borrow().entry_set_for_block(block)); + self.state.clone_from(&self.results.borrow().entry_set_for_block(block)); self.pos = CursorPosition::block_entry(block); self.state_needs_reset = false; } @@ -202,12 +200,23 @@ where /// /// This can be used, e.g., to apply the call return effect directly to the cursor without /// creating an extra copy of the dataflow state. - pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut BitSet<A::Idx>)) { + pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) { f(&self.results.borrow().analysis, &mut self.state); self.state_needs_reset = true; } } +impl<'mir, 'tcx, A, R, T> ResultsCursor<'mir, 'tcx, A, R> +where + A: Analysis<'tcx, Domain = BitSet<T>>, + T: Idx, + R: Borrow<Results<'tcx, A>>, +{ + pub fn contains(&self, elem: T) -> bool { + self.get().contains(elem) + } +} + #[derive(Clone, Copy, Debug)] struct CursorPosition { block: BasicBlock, diff --git a/compiler/rustc_mir/src/dataflow/framework/direction.rs b/compiler/rustc_mir/src/dataflow/framework/direction.rs index 4512ae96c08..76c48100371 100644 --- a/compiler/rustc_mir/src/dataflow/framework/direction.rs +++ b/compiler/rustc_mir/src/dataflow/framework/direction.rs @@ -18,7 +18,7 @@ pub trait Direction { /// `effects.start()` must precede or equal `effects.end()` in this direction. fn apply_effects_in_range<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, effects: RangeInclusive<EffectIndex>, @@ -27,7 +27,7 @@ pub trait Direction { fn apply_effects_in_block<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, ) where @@ -55,9 +55,9 @@ pub trait Direction { tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, dead_unwinds: Option<&BitSet<BasicBlock>>, - exit_state: &mut BitSet<A::Idx>, + exit_state: &mut A::Domain, block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>), - propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>), + propagate: impl FnMut(BasicBlock, &A::Domain), ) where A: Analysis<'tcx>; } @@ -72,7 +72,7 @@ impl Direction for Backward { fn apply_effects_in_block<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, ) where @@ -112,7 +112,7 @@ impl Direction for Backward { fn apply_effects_in_range<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, effects: RangeInclusive<EffectIndex>, @@ -224,9 +224,9 @@ impl Direction for Backward { _tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, dead_unwinds: Option<&BitSet<BasicBlock>>, - exit_state: &mut BitSet<A::Idx>, + exit_state: &mut A::Domain, (bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>), - mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>), + mut propagate: impl FnMut(BasicBlock, &A::Domain), ) where A: Analysis<'tcx>, { @@ -281,7 +281,7 @@ impl Direction for Forward { fn apply_effects_in_block<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, ) where @@ -321,7 +321,7 @@ impl Direction for Forward { fn apply_effects_in_range<A>( analysis: &A, - state: &mut BitSet<A::Idx>, + state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, effects: RangeInclusive<EffectIndex>, @@ -428,9 +428,9 @@ impl Direction for Forward { tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, dead_unwinds: Option<&BitSet<BasicBlock>>, - exit_state: &mut BitSet<A::Idx>, + exit_state: &mut A::Domain, (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>), - mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>), + mut propagate: impl FnMut(BasicBlock, &A::Domain), ) where A: Analysis<'tcx>, { @@ -499,7 +499,7 @@ impl Direction for Forward { // MIR building adds discriminants to the `values` array in the same order as they // are yielded by `AdtDef::discriminants`. We rely on this to match each // discriminant in `values` to its corresponding variant in linear time. - let mut tmp = BitSet::new_empty(exit_state.domain_size()); + let mut tmp = analysis.bottom_value(body); let mut discriminants = enum_def.discriminants(tcx); for (value, target) in values.iter().zip(targets.iter().copied()) { let (variant_idx, _) = @@ -508,7 +508,7 @@ impl Direction for Forward { from that of `SwitchInt::values`", ); - tmp.overwrite(exit_state); + tmp.clone_from(exit_state); analysis.apply_discriminant_switch_effect( &mut tmp, bb, @@ -559,7 +559,7 @@ fn switch_on_enum_discriminant( Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated)))) if *lhs == switch_on => { - match &discriminated.ty(body, tcx).ty.kind { + match &discriminated.ty(body, tcx).ty.kind() { ty::Adt(def, _) => Some((*discriminated, def)), // `Rvalue::Discriminant` is also used to get the active yield point for a diff --git a/compiler/rustc_mir/src/dataflow/framework/engine.rs b/compiler/rustc_mir/src/dataflow/framework/engine.rs index b703852b1de..f39c78f503d 100644 --- a/compiler/rustc_mir/src/dataflow/framework/engine.rs +++ b/compiler/rustc_mir/src/dataflow/framework/engine.rs @@ -1,5 +1,6 @@ //! A solver for dataflow problems. +use std::borrow::BorrowMut; use std::ffi::OsString; use std::fs; use std::path::PathBuf; @@ -9,14 +10,16 @@ use rustc_data_structures::work_queue::WorkQueue; use rustc_graphviz as dot; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; +use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::{self, traversal, BasicBlock}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; +use super::fmt::DebugWithContext; use super::graphviz; use super::{ - visit_results, Analysis, Direction, GenKillAnalysis, GenKillSet, ResultsCursor, ResultsVisitor, + visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice, + ResultsCursor, ResultsVisitor, }; use crate::util::pretty::dump_enabled; @@ -26,7 +29,7 @@ where A: Analysis<'tcx>, { pub analysis: A, - pub(super) entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>, + pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>, } impl<A> Results<'tcx, A> @@ -39,7 +42,7 @@ where } /// Gets the dataflow state for the given block. - pub fn entry_set_for_block(&self, block: BasicBlock) -> &BitSet<A::Idx> { + pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain { &self.entry_sets[block] } @@ -47,7 +50,7 @@ where &self, body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator<Item = BasicBlock>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, ) { visit_results(body, blocks, self, vis) } @@ -55,7 +58,7 @@ where pub fn visit_reachable_with( &self, body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, ) { let blocks = mir::traversal::reachable(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) @@ -64,7 +67,7 @@ where pub fn visit_in_rpo_with( &self, body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, ) { let blocks = mir::traversal::reverse_postorder(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) @@ -76,21 +79,28 @@ pub struct Engine<'a, 'tcx, A> where A: Analysis<'tcx>, { - bits_per_block: usize, tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, def_id: DefId, dead_unwinds: Option<&'a BitSet<BasicBlock>>, - entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>, + entry_sets: IndexVec<BasicBlock, A::Domain>, + pass_name: Option<&'static str>, analysis: A, /// Cached, cumulative transfer functions for each block. - trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>, + // + // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for + // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade + // performance in practice. I've tried a few ways to avoid this, but they have downsides. See + // the message for the commit that added this FIXME for more information. + apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>, } -impl<A> Engine<'a, 'tcx, A> +impl<A, D, T> Engine<'a, 'tcx, A> where - A: GenKillAnalysis<'tcx>, + A: GenKillAnalysis<'tcx, Idx = T, Domain = D>, + D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>, + T: Idx, { /// Creates a new `Engine` to solve a gen-kill dataflow problem. pub fn new_gen_kill( @@ -109,22 +119,26 @@ where // Otherwise, compute and store the cumulative transfer function for each block. - let bits_per_block = analysis.bits_per_block(body); - let mut trans_for_block = - IndexVec::from_elem(GenKillSet::identity(bits_per_block), body.basic_blocks()); + let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size()); + let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks()); for (block, block_data) in body.basic_blocks().iter_enumerated() { let trans = &mut trans_for_block[block]; A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data); } - Self::new(tcx, body, def_id, analysis, Some(trans_for_block)) + let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| { + trans_for_block[bb].apply(state.borrow_mut()); + }); + + Self::new(tcx, body, def_id, analysis, Some(apply_trans as Box<_>)) } } -impl<A> Engine<'a, 'tcx, A> +impl<A, D> Engine<'a, 'tcx, A> where - A: Analysis<'tcx>, + A: Analysis<'tcx, Domain = D>, + D: Clone + JoinSemiLattice, { /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer /// function. @@ -145,32 +159,25 @@ where body: &'a mir::Body<'tcx>, def_id: DefId, analysis: A, - trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>, + apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>, ) -> Self { - let bits_per_block = analysis.bits_per_block(body); - - let bottom_value_set = if A::BOTTOM_VALUE { - BitSet::new_filled(bits_per_block) - } else { - BitSet::new_empty(bits_per_block) - }; - - let mut entry_sets = IndexVec::from_elem(bottom_value_set.clone(), body.basic_blocks()); + let bottom_value = analysis.bottom_value(body); + let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks()); analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]); - if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value_set { + if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value { bug!("`initialize_start_block` is not yet supported for backward dataflow analyses"); } Engine { analysis, - bits_per_block, tcx, body, def_id, dead_unwinds: None, + pass_name: None, entry_sets, - trans_for_block, + apply_trans_for_block, } } @@ -184,17 +191,29 @@ where self } + /// Adds an identifier to the graphviz output for this particular run of a dataflow analysis. + /// + /// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name` + /// to differentiate them. Otherwise, only the results for the latest run will be saved. + pub fn pass_name(mut self, name: &'static str) -> Self { + self.pass_name = Some(name); + self + } + /// Computes the fixpoint for this dataflow problem and returns it. - pub fn iterate_to_fixpoint(self) -> Results<'tcx, A> { + pub fn iterate_to_fixpoint(self) -> Results<'tcx, A> + where + A::Domain: DebugWithContext<A>, + { let Engine { analysis, - bits_per_block, body, dead_unwinds, def_id, mut entry_sets, tcx, - trans_for_block, + apply_trans_for_block, + pass_name, .. } = self; @@ -213,14 +232,14 @@ where } } - let mut state = BitSet::new_empty(bits_per_block); + let mut state = analysis.bottom_value(body); while let Some(bb) = dirty_queue.pop() { let bb_data = &body[bb]; // Apply the block transfer function, using the cached one if it exists. - state.overwrite(&entry_sets[bb]); - match &trans_for_block { - Some(trans_for_block) => trans_for_block[bb].apply(&mut state), + state.clone_from(&entry_sets[bb]); + match &apply_trans_for_block { + Some(apply) => apply(bb, &mut state), None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data), } @@ -231,8 +250,8 @@ where dead_unwinds, &mut state, (bb, bb_data), - |target: BasicBlock, state: &BitSet<A::Idx>| { - let set_changed = analysis.join(&mut entry_sets[target], state); + |target: BasicBlock, state: &A::Domain| { + let set_changed = entry_sets[target].join(state); if set_changed { dirty_queue.insert(target); } @@ -242,7 +261,7 @@ where let results = Results { analysis, entry_sets }; - let res = write_graphviz_results(tcx, def_id, &body, &results, trans_for_block); + let res = write_graphviz_results(tcx, def_id, &body, &results, pass_name); if let Err(e) = res { warn!("Failed to write graphviz dataflow results: {}", e); } @@ -260,10 +279,11 @@ fn write_graphviz_results<A>( def_id: DefId, body: &mir::Body<'tcx>, results: &Results<'tcx, A>, - block_transfer_functions: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>, + pass_name: Option<&'static str>, ) -> std::io::Result<()> where A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, { let attrs = match RustcMirAttrs::parse(tcx, def_id) { Ok(attrs) => attrs, @@ -278,39 +298,38 @@ where None if tcx.sess.opts.debugging_opts.dump_mir_dataflow && dump_enabled(tcx, A::NAME, def_id) => { + // FIXME: Use some variant of `pretty::dump_path` for this let mut path = PathBuf::from(&tcx.sess.opts.debugging_opts.dump_mir_dir); + let crate_name = tcx.crate_name(def_id.krate); let item_name = ty::print::with_forced_impl_filename_line(|| { tcx.def_path(def_id).to_filename_friendly_no_crate() }); - path.push(format!("rustc.{}.{}.dot", item_name, A::NAME)); + + let pass_name = pass_name.map(|s| format!(".{}", s)).unwrap_or_default(); + + path.push(format!("{}.{}.{}{}.dot", crate_name, item_name, A::NAME, pass_name)); path } None => return Ok(()), }; - let bits_per_block = results.analysis.bits_per_block(body); - - let mut formatter: Box<dyn graphviz::StateFormatter<'tcx, _>> = match attrs.formatter { - Some(sym::two_phase) => Box::new(graphviz::TwoPhaseDiff::new(bits_per_block)), - Some(sym::gen_kill) => { - if let Some(trans_for_block) = block_transfer_functions { - Box::new(graphviz::BlockTransferFunc::new(body, trans_for_block)) - } else { - Box::new(graphviz::SimpleDiff::new(body, &results)) - } - } - - // Default to the `SimpleDiff` output style. - _ => Box::new(graphviz::SimpleDiff::new(body, &results)), + let style = match attrs.formatter { + Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter, + _ => graphviz::OutputStyle::AfterOnly, }; debug!("printing dataflow results for {:?} to {}", def_id, path.display()); let mut buf = Vec::new(); - let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter); - dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?; + let graphviz = graphviz::Formatter::new(body, def_id, results, style); + let mut render_opts = + vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())]; + if tcx.sess.opts.debugging_opts.graphviz_dark_mode { + render_opts.push(dot::RenderOption::DarkTheme); + } + dot::render_opts(&graphviz, &mut buf, &render_opts)?; if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; diff --git a/compiler/rustc_mir/src/dataflow/framework/fmt.rs b/compiler/rustc_mir/src/dataflow/framework/fmt.rs new file mode 100644 index 00000000000..0140a750544 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/fmt.rs @@ -0,0 +1,172 @@ +//! Custom formatting traits used when outputting Graphviz diagrams with the results of a dataflow +//! analysis. + +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_index::vec::Idx; +use std::fmt; + +/// An extension to `fmt::Debug` for data that can be better printed with some auxiliary data `C`. +pub trait DebugWithContext<C>: Eq + fmt::Debug { + fn fmt_with(&self, _ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } + + /// Print the difference between `self` and `old`. + /// + /// This should print nothing if `self == old`. + /// + /// `+` and `-` are typically used to indicate differences. However, these characters are + /// fairly common and may be needed to print a types representation. If using them to indicate + /// a diff, prefix them with the "Unit Separator" control character (␟ U+001F). + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self == old { + return Ok(()); + } + + write!(f, "\u{001f}+")?; + self.fmt_with(ctxt, f)?; + + if f.alternate() { + write!(f, "\n")?; + } else { + write!(f, "\t")?; + } + + write!(f, "\u{001f}-")?; + self.fmt_with(ctxt, f) + } +} + +/// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_with`. +pub struct DebugWithAdapter<'a, T, C> { + pub this: T, + pub ctxt: &'a C, +} + +impl<T, C> fmt::Debug for DebugWithAdapter<'_, T, C> +where + T: DebugWithContext<C>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.this.fmt_with(self.ctxt, f) + } +} + +/// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_diff_with`. +pub struct DebugDiffWithAdapter<'a, T, C> { + pub new: T, + pub old: T, + pub ctxt: &'a C, +} + +impl<T, C> fmt::Debug for DebugDiffWithAdapter<'_, T, C> +where + T: DebugWithContext<C>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.new.fmt_diff_with(&self.old, self.ctxt, f) + } +} + +// Impls + +impl<T, C> DebugWithContext<C> for BitSet<T> +where + T: Idx + DebugWithContext<C>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter().map(|i| DebugWithAdapter { this: i, ctxt })).finish() + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let size = self.domain_size(); + assert_eq!(size, old.domain_size()); + + let mut set_in_self = HybridBitSet::new_empty(size); + let mut cleared_in_self = HybridBitSet::new_empty(size); + + for i in (0..size).map(T::new) { + match (self.contains(i), old.contains(i)) { + (true, false) => set_in_self.insert(i), + (false, true) => cleared_in_self.insert(i), + _ => continue, + }; + } + + let mut first = true; + for idx in set_in_self.iter() { + let delim = if first { + "\u{001f}+" + } else if f.alternate() { + "\n\u{001f}+" + } else { + ", " + }; + + write!(f, "{}", delim)?; + idx.fmt_with(ctxt, f)?; + first = false; + } + + if !f.alternate() { + first = true; + if !set_in_self.is_empty() && !cleared_in_self.is_empty() { + write!(f, "\t")?; + } + } + + for idx in cleared_in_self.iter() { + let delim = if first { + "\u{001f}-" + } else if f.alternate() { + "\n\u{001f}-" + } else { + ", " + }; + + write!(f, "{}", delim)?; + idx.fmt_with(ctxt, f)?; + first = false; + } + + Ok(()) + } +} + +impl<T, C> DebugWithContext<C> for &'_ T +where + T: DebugWithContext<C>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (*self).fmt_with(ctxt, f) + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (*self).fmt_diff_with(*old, ctxt, f) + } +} + +impl<C> DebugWithContext<C> for rustc_middle::mir::Local {} +impl<C> DebugWithContext<C> for crate::dataflow::move_paths::InitIndex {} + +impl<'tcx, C> DebugWithContext<C> for crate::dataflow::move_paths::MovePathIndex +where + C: crate::dataflow::move_paths::HasMoveData<'tcx>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", ctxt.move_data().move_paths[*self]) + } +} + +impl<T, C> DebugWithContext<C> for crate::dataflow::lattice::Dual<T> +where + T: DebugWithContext<C>, +{ + fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (self.0).fmt_with(ctxt, f) + } + + fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (self.0).fmt_diff_with(&old.0, ctxt, f) + } +} diff --git a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs index 896616a2175..5d4c4251961 100644 --- a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs +++ b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs @@ -1,26 +1,41 @@ //! A helpful diagram for debugging dataflow problems. -use std::cell::RefCell; +use std::borrow::Cow; +use std::lazy::SyncOnceCell; use std::{io, ops, str}; +use regex::Regex; use rustc_graphviz as dot; use rustc_hir::def_id::DefId; -use rustc_index::bit_set::{BitSet, HybridBitSet}; -use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::{self, BasicBlock, Body, Location}; -use super::{Analysis, Direction, GenKillSet, Results, ResultsRefCursor}; +use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; +use super::{Analysis, Direction, Results, ResultsRefCursor, ResultsVisitor}; use crate::util::graphviz_safe_def_name; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum OutputStyle { + AfterOnly, + BeforeAndAfter, +} + +impl OutputStyle { + fn num_state_columns(&self) -> usize { + match self { + Self::AfterOnly => 1, + Self::BeforeAndAfter => 2, + } + } +} + pub struct Formatter<'a, 'tcx, A> where A: Analysis<'tcx>, { body: &'a Body<'tcx>, def_id: DefId, - - // This must be behind a `RefCell` because `dot::Labeller` takes `&self`. - block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>, + results: &'a Results<'tcx, A>, + style: OutputStyle, } impl<A> Formatter<'a, 'tcx, A> @@ -31,15 +46,9 @@ where body: &'a Body<'tcx>, def_id: DefId, results: &'a Results<'tcx, A>, - state_formatter: &'a mut dyn StateFormatter<'tcx, A>, + style: OutputStyle, ) -> Self { - let block_formatter = BlockFormatter { - bg: Background::Light, - results: ResultsRefCursor::new(body, results), - state_formatter, - }; - - Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) } + Formatter { body, def_id, results, style } } } @@ -62,6 +71,7 @@ fn dataflow_successors(body: &Body<'tcx>, bb: BasicBlock) -> Vec<CfgEdge> { impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A> where A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, { type Node = BasicBlock; type Edge = CfgEdge; @@ -77,7 +87,13 @@ where fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { let mut label = Vec::new(); - self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap(); + let mut fmt = BlockFormatter { + results: ResultsRefCursor::new(self.body, self.results), + style: self.style, + bg: Background::Light, + }; + + fmt.write_node_label(&mut label, self.body, *block).unwrap(); dot::LabelText::html(String::from_utf8(label).unwrap()) } @@ -126,19 +142,16 @@ where { results: ResultsRefCursor<'a, 'a, 'tcx, A>, bg: Background, - state_formatter: &'a mut dyn StateFormatter<'tcx, A>, + style: OutputStyle, } impl<A> BlockFormatter<'a, 'tcx, A> where A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, { const HEADER_COLOR: &'static str = "#a0a0a0"; - fn num_state_columns(&self) -> usize { - std::cmp::max(1, self.state_formatter.column_names().len()) - } - fn toggle_background(&mut self) -> Background { let bg = self.bg; self.bg = !bg; @@ -187,40 +200,30 @@ where write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?; // A + B: Block header - if self.state_formatter.column_names().is_empty() { - self.write_block_header_simple(w, block)?; - } else { - self.write_block_header_with_state_columns(w, block)?; + match self.style { + OutputStyle::AfterOnly => self.write_block_header_simple(w, block)?, + OutputStyle::BeforeAndAfter => { + self.write_block_header_with_state_columns(w, block, &["BEFORE", "AFTER"])? + } } // C: State at start of block self.bg = Background::Light; self.results.seek_to_block_start(block); - let block_entry_state = self.results.get().clone(); - + let block_start_state = self.results.get().clone(); self.write_row_with_full_state(w, "", "(on start)")?; - // D: Statement transfer functions - for (i, statement) in body[block].statements.iter().enumerate() { - let location = Location { block, statement_index: i }; - let statement_str = format!("{:?}", statement); - self.write_row_for_location(w, &i.to_string(), &statement_str, location)?; - } - - // E: Terminator transfer function - let terminator = body[block].terminator(); - let terminator_loc = body.terminator_loc(block); - let mut terminator_str = String::new(); - terminator.kind.fmt_head(&mut terminator_str).unwrap(); - - self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?; + // D + E: Statement and terminator transfer functions + self.write_statements_and_terminator(w, body, block)?; // F: State at end of block + let terminator = body[block].terminator(); + // Write the full dataflow state immediately after the terminator if it differs from the // state at block entry. self.results.seek_to_block_end(block); - if self.results.get() != &block_entry_state || A::Direction::is_backward() { + if self.results.get() != &block_start_state || A::Direction::is_backward() { let after_terminator_name = match terminator.kind { mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)", _ => "(on end)", @@ -229,8 +232,11 @@ where self.write_row_with_full_state(w, "", after_terminator_name)?; } - // Write any changes caused by terminator-specific effects - let num_state_columns = self.num_state_columns(); + // Write any changes caused by terminator-specific effects. + // + // FIXME: These should really be printed as part of each outgoing edge rather than the node + // for the basic block itself. That way, we could display terminator-specific effects for + // backward dataflow analyses as well as effects for `SwitchInt` terminators. match terminator.kind { mir::TerminatorKind::Call { destination: Some((return_place, _)), @@ -239,44 +245,43 @@ where .. } => { self.write_row(w, "", "(on successful return)", |this, w, fmt| { - write!( - w, - r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#, - colspan = num_state_columns, - fmt = fmt, - )?; - let state_on_unwind = this.results.get().clone(); this.results.apply_custom_effect(|analysis, state| { analysis.apply_call_return_effect(state, block, func, args, return_place); }); - write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?; - write!(w, "</td>") + write!( + w, + r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#, + colspan = this.style.num_state_columns(), + fmt = fmt, + diff = diff_pretty( + this.results.get(), + &state_on_unwind, + this.results.analysis() + ), + ) })?; } mir::TerminatorKind::Yield { resume, resume_arg, .. } => { self.write_row(w, "", "(on yield resume)", |this, w, fmt| { - write!( - w, - r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#, - colspan = num_state_columns, - fmt = fmt, - )?; - let state_on_generator_drop = this.results.get().clone(); this.results.apply_custom_effect(|analysis, state| { analysis.apply_yield_resume_effect(state, resume, resume_arg); }); - write_diff( + write!( w, - this.results.analysis(), - &state_on_generator_drop, - this.results.get(), - )?; - write!(w, "</td>") + r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#, + colspan = this.style.num_state_columns(), + fmt = fmt, + diff = diff_pretty( + this.results.get(), + &state_on_generator_drop, + this.results.analysis() + ), + ) })?; } @@ -322,6 +327,7 @@ where &mut self, w: &mut impl io::Write, block: BasicBlock, + state_column_names: &[&str], ) -> io::Result<()> { // +------------------------------------+-------------+ // A | bb4 | STATE | @@ -330,8 +336,6 @@ where // +-+----------------------------------+------+------+ // | | ... | | | - let state_column_names = self.state_formatter.column_names(); - // A write!( w, @@ -357,6 +361,56 @@ where write!(w, "</tr>") } + fn write_statements_and_terminator( + &mut self, + w: &mut impl io::Write, + body: &'a Body<'tcx>, + block: BasicBlock, + ) -> io::Result<()> { + let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style); + + let mut befores = diffs.before.map(|v| v.into_iter()); + let mut afters = diffs.after.into_iter(); + + let next_in_dataflow_order = |it: &mut std::vec::IntoIter<_>| { + if A::Direction::is_forward() { it.next().unwrap() } else { it.next_back().unwrap() } + }; + + for (i, statement) in body[block].statements.iter().enumerate() { + let statement_str = format!("{:?}", statement); + let index_str = format!("{}", i); + + let after = next_in_dataflow_order(&mut afters); + let before = befores.as_mut().map(next_in_dataflow_order); + + self.write_row(w, &index_str, &statement_str, |_this, w, fmt| { + if let Some(before) = before { + write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?; + } + + write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after) + })?; + } + + let after = next_in_dataflow_order(&mut afters); + let before = befores.as_mut().map(next_in_dataflow_order); + + assert!(afters.is_empty()); + assert!(befores.as_ref().map_or(true, ExactSizeIterator::is_empty)); + + let terminator = body[block].terminator(); + let mut terminator_str = String::new(); + terminator.kind.fmt_head(&mut terminator_str).unwrap(); + + self.write_row(w, "T", &terminator_str, |_this, w, fmt| { + if let Some(before) = before { + write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?; + } + + write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after) + }) + } + /// Write a row with the given index and MIR, using the function argument to fill in the /// "STATE" column(s). fn write_row<W: io::Write>( @@ -397,319 +451,176 @@ where let state = this.results.get(); let analysis = this.results.analysis(); + // FIXME: The full state vector can be quite long. It would be nice to split on commas + // and use some text wrapping algorithm. write!( w, - r#"<td colspan="{colspan}" {fmt} align="left">{{"#, - colspan = this.num_state_columns(), + r#"<td colspan="{colspan}" {fmt} align="left">{state}</td>"#, + colspan = this.style.num_state_columns(), fmt = fmt, - )?; - pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, "}}</td>") - }) - } - - fn write_row_for_location( - &mut self, - w: &mut impl io::Write, - i: &str, - mir: &str, - location: Location, - ) -> io::Result<()> { - self.write_row(w, i, mir, |this, w, fmt| { - this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location) + state = format!("{:?}", DebugWithAdapter { this: state, ctxt: analysis }), + ) }) } } -/// Controls what gets printed under the `STATE` header. -pub trait StateFormatter<'tcx, A> +struct StateDiffCollector<'a, 'tcx, A> where A: Analysis<'tcx>, { - /// The columns that will get printed under `STATE`. - fn column_names(&self) -> &[&str]; - - fn write_state_for_location( - &mut self, - w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()>; + analysis: &'a A, + prev_state: A::Domain, + before: Option<Vec<String>>, + after: Vec<String>, } -/// Prints a single column containing the state vector immediately *after* each statement. -pub struct SimpleDiff<'a, 'tcx, A> +impl<A> StateDiffCollector<'a, 'tcx, A> where A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, { - prev_state: ResultsRefCursor<'a, 'a, 'tcx, A>, -} + fn run( + body: &'a mir::Body<'tcx>, + block: BasicBlock, + results: &'a Results<'tcx, A>, + style: OutputStyle, + ) -> Self { + let mut collector = StateDiffCollector { + analysis: &results.analysis, + prev_state: results.analysis.bottom_value(body), + after: vec![], + before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]), + }; -impl<A> SimpleDiff<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>) -> Self { - SimpleDiff { prev_state: ResultsRefCursor::new(body, results) } + results.visit_with(body, std::iter::once(block), &mut collector); + collector } } -impl<A> StateFormatter<'tcx, A> for SimpleDiff<'_, 'tcx, A> +impl<A> ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A> where A: Analysis<'tcx>, + A::Domain: DebugWithContext<A>, { - fn column_names(&self) -> &[&str] { - &[] - } + type FlowState = A::Domain; - fn write_state_for_location( + fn visit_block_start( &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { + state: &Self::FlowState, + _block_data: &'mir mir::BasicBlockData<'tcx>, + _block: BasicBlock, + ) { if A::Direction::is_forward() { - if location.statement_index == 0 { - self.prev_state.seek_to_block_start(location.block); - } else { - self.prev_state.seek_after_primary_effect(Location { - statement_index: location.statement_index - 1, - ..location - }); - } - } else { - if location == results.body().terminator_loc(location.block) { - self.prev_state.seek_to_block_end(location.block); - } else { - self.prev_state.seek_after_primary_effect(location.successor_within_block()); - } + self.prev_state.clone_from(state); } - - write!(w, r#"<td {fmt} balign="left" align="left">"#, fmt = fmt)?; - results.seek_after_primary_effect(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), self.prev_state.get(), curr_state)?; - write!(w, "</td>") } -} -/// Prints two state columns, one containing only the "before" effect of each statement and one -/// containing the full effect. -pub struct TwoPhaseDiff<T: Idx> { - prev_state: BitSet<T>, - prev_loc: Location, -} + fn visit_block_end( + &mut self, + state: &Self::FlowState, + _block_data: &'mir mir::BasicBlockData<'tcx>, + _block: BasicBlock, + ) { + if A::Direction::is_backward() { + self.prev_state.clone_from(state); + } + } -impl<T: Idx> TwoPhaseDiff<T> { - pub fn new(bits_per_block: usize) -> Self { - TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START } + fn visit_statement_before_primary_effect( + &mut self, + state: &Self::FlowState, + _statement: &'mir mir::Statement<'tcx>, + _location: Location, + ) { + if let Some(before) = self.before.as_mut() { + before.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) + } } -} -impl<A> StateFormatter<'tcx, A> for TwoPhaseDiff<A::Idx> -where - A: Analysis<'tcx>, -{ - fn column_names(&self) -> &[&str] { - &["BEFORE", " AFTER"] + fn visit_statement_after_primary_effect( + &mut self, + state: &Self::FlowState, + _statement: &'mir mir::Statement<'tcx>, + _location: Location, + ) { + self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) } - fn write_state_for_location( + fn visit_terminator_before_primary_effect( &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { - if location.statement_index == 0 { - results.seek_to_block_entry(location.block); - self.prev_state.overwrite(results.get()); - } else { - // Ensure that we are visiting statements in order, so `prev_state` is correct. - assert_eq!(self.prev_loc.successor_within_block(), location); + state: &Self::FlowState, + _terminator: &'mir mir::Terminator<'tcx>, + _location: Location, + ) { + if let Some(before) = self.before.as_mut() { + before.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) } - - self.prev_loc = location; - - // Before - - write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?; - results.seek_before_primary_effect(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; - self.prev_state.overwrite(curr_state); - write!(w, "</td>")?; - - // After - - write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?; - results.seek_after_primary_effect(location); - let curr_state = results.get(); - write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; - self.prev_state.overwrite(curr_state); - write!(w, "</td>") } -} -/// Prints the gen/kill set for the entire block. -pub struct BlockTransferFunc<'a, 'tcx, T: Idx> { - body: &'a mir::Body<'tcx>, - trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>, + fn visit_terminator_after_primary_effect( + &mut self, + state: &Self::FlowState, + _terminator: &'mir mir::Terminator<'tcx>, + _location: Location, + ) { + self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.prev_state.clone_from(state) + } } -impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> { - pub fn new( - body: &'mir mir::Body<'tcx>, - trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>, - ) -> Self { - BlockTransferFunc { body, trans_for_block } - } +macro_rules! regex { + ($re:literal $(,)?) => {{ + static RE: SyncOnceCell<regex::Regex> = SyncOnceCell::new(); + RE.get_or_init(|| Regex::new($re).unwrap()) + }}; } -impl<A> StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx> +fn diff_pretty<T, C>(new: T, old: T, ctxt: &C) -> String where - A: Analysis<'tcx>, + T: DebugWithContext<C>, { - fn column_names(&self) -> &[&str] { - &["GEN", "KILL"] + if new == old { + return String::new(); } - fn write_state_for_location( - &mut self, - mut w: &mut dyn io::Write, - fmt: &str, - results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, - location: Location, - ) -> io::Result<()> { - // Only print a single row. - if location.statement_index != 0 { - return Ok(()); - } + let re = regex!("\t?\u{001f}([+-])"); - let block_trans = &self.trans_for_block[location.block]; - let rowspan = self.body.basic_blocks()[location.block].statements.len(); + let raw_diff = format!("{:#?}", DebugDiffWithAdapter { new, old, ctxt }); - for set in &[&block_trans.gen, &block_trans.kill] { - write!( - w, - r#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#, - fmt = fmt, - rowspan = rowspan - )?; + // Replace newlines in the `Debug` output with `<br/>` + let raw_diff = raw_diff.replace('\n', r#"<br align="left"/>"#); - pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?; - write!(w, "</td>")?; + let mut inside_font_tag = false; + let html_diff = re.replace_all(&raw_diff, |captures: ®ex::Captures<'_>| { + let mut ret = String::new(); + if inside_font_tag { + ret.push_str(r#"</font>"#); } - Ok(()) - } -} - -/// Writes two lines, one containing the added bits and one the removed bits. -fn write_diff<A: Analysis<'tcx>>( - w: &mut impl io::Write, - analysis: &A, - from: &BitSet<A::Idx>, - to: &BitSet<A::Idx>, -) -> io::Result<()> { - assert_eq!(from.domain_size(), to.domain_size()); - let len = from.domain_size(); - - let mut set = HybridBitSet::new_empty(len); - let mut clear = HybridBitSet::new_empty(len); - - // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets. - for i in (0..len).map(A::Idx::new) { - match (from.contains(i), to.contains(i)) { - (false, true) => set.insert(i), - (true, false) => clear.insert(i), - _ => continue, + let tag = match &captures[1] { + "+" => r#"<font color="darkgreen">+"#, + "-" => r#"<font color="red">-"#, + _ => unreachable!(), }; - } - - if !set.is_empty() { - write!(w, r#"<font color="darkgreen">+"#)?; - pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, r#"</font>"#)?; - } - - if !set.is_empty() && !clear.is_empty() { - write!(w, "{}", BR_LEFT)?; - } - - if !clear.is_empty() { - write!(w, r#"<font color="red">-"#)?; - pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?; - write!(w, r#"</font>"#)?; - } - - Ok(()) -} -const BR_LEFT: &str = r#"<br align="left"/>"#; -const BR_LEFT_SPACE: &str = r#"<br align="left"/> "#; + inside_font_tag = true; + ret.push_str(tag); + ret + }); -/// Line break policy that breaks at 40 characters and starts the next line with a single space. -const LIMIT_30_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 }); - -struct LineBreak { - sequence: &'static str, - limit: usize, -} - -/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given -/// separator (`sep`). -/// -/// Optionally, it will break lines using the given character sequence (usually `<br/>`) and -/// character limit. -fn pretty_print_state_elems<A>( - w: &mut impl io::Write, - analysis: &A, - elems: impl Iterator<Item = A::Idx>, - sep: &str, - line_break: Option<LineBreak>, -) -> io::Result<bool> -where - A: Analysis<'tcx>, -{ - let sep_width = sep.chars().count(); - - let mut buf = Vec::new(); - - let mut first = true; - let mut curr_line_width = 0; - let mut line_break_inserted = false; - - for idx in elems { - buf.clear(); - analysis.pretty_print_idx(&mut buf, idx)?; - let idx_str = - str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8"); - let escaped = dot::escape_html(idx_str); - let escaped_width = escaped.chars().count(); - - if first { - first = false; - } else { - write!(w, "{}", sep)?; - curr_line_width += sep_width; - - if let Some(line_break) = &line_break { - if curr_line_width + sep_width + escaped_width > line_break.limit { - write!(w, "{}", line_break.sequence)?; - line_break_inserted = true; - curr_line_width = 0; - } - } - } + let mut html_diff = match html_diff { + Cow::Borrowed(_) => return raw_diff, + Cow::Owned(s) => s, + }; - write!(w, "{}", escaped)?; - curr_line_width += escaped_width; + if inside_font_tag { + html_diff.push_str("</font>"); } - Ok(line_break_inserted) + html_diff } /// The background color used for zebra-striping the table. diff --git a/compiler/rustc_mir/src/dataflow/framework/lattice.rs b/compiler/rustc_mir/src/dataflow/framework/lattice.rs new file mode 100644 index 00000000000..e7ef9267db5 --- /dev/null +++ b/compiler/rustc_mir/src/dataflow/framework/lattice.rs @@ -0,0 +1,230 @@ +//! Traits used to represent [lattices] for use as the domain of a dataflow analysis. +//! +//! # Overview +//! +//! The most common lattice is a powerset of some set `S`, ordered by [set inclusion]. The [Hasse +//! diagram] for the powerset of a set with two elements (`X` and `Y`) is shown below. Note that +//! distinct elements at the same height in a Hasse diagram (e.g. `{X}` and `{Y}`) are +//! *incomparable*, not equal. +//! +//! ```text +//! {X, Y} <- top +//! / \ +//! {X} {Y} +//! \ / +//! {} <- bottom +//! +//! ``` +//! +//! The defining characteristic of a lattice—the one that differentiates it from a [partially +//! ordered set][poset]—is the existence of a *unique* least upper and greatest lower bound for +//! every pair of elements. The lattice join operator (`∨`) returns the least upper bound, and the +//! lattice meet operator (`∧`) returns the greatest lower bound. Types that implement one operator +//! but not the other are known as semilattices. Dataflow analysis only uses the join operator and +//! will work with any join-semilattice, but both should be specified when possible. +//! +//! ## `PartialOrd` +//! +//! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`] +//! and [`MeetSemiLattice`] do not have [`PartialOrd`][std::cmp::PartialOrd] as a supertrait. This +//! is because most standard library types use lexicographic ordering instead of set inclusion for +//! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a +//! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The +//! only benefit would be the ability to check that the least upper (or greatest lower) bound +//! returned by the lattice join (or meet) operator was in fact greater (or lower) than the inputs. +//! +//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) +//! [set inclusion]: https://en.wikipedia.org/wiki/Subset +//! [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram +//! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set + +use rustc_index::bit_set::BitSet; +use rustc_index::vec::{Idx, IndexVec}; + +/// A [partially ordered set][poset] that has a [least upper bound][lub] for any pair of elements +/// in the set. +/// +/// [lub]: https://en.wikipedia.org/wiki/Infimum_and_supremum +/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set +pub trait JoinSemiLattice: Eq { + /// Computes the least upper bound of two elements, storing the result in `self` and returning + /// `true` if `self` has changed. + /// + /// The lattice join operator is abbreviated as `∨`. + fn join(&mut self, other: &Self) -> bool; +} + +/// A [partially ordered set][poset] that has a [greatest lower bound][glb] for any pair of +/// elements in the set. +/// +/// Dataflow analyses only require that their domains implement [`JoinSemiLattice`], not +/// `MeetSemiLattice`. However, types that will be used as dataflow domains should implement both +/// so that they can be used with [`Dual`]. +/// +/// [glb]: https://en.wikipedia.org/wiki/Infimum_and_supremum +/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set +pub trait MeetSemiLattice: Eq { + /// Computes the greatest lower bound of two elements, storing the result in `self` and + /// returning `true` if `self` has changed. + /// + /// The lattice meet operator is abbreviated as `∧`. + fn meet(&mut self, other: &Self) -> bool; +} + +/// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom: +/// +/// ```text +/// true +/// | +/// false +/// ``` +impl JoinSemiLattice for bool { + fn join(&mut self, other: &Self) -> bool { + if let (false, true) = (*self, *other) { + *self = true; + return true; + } + + false + } +} + +impl MeetSemiLattice for bool { + fn meet(&mut self, other: &Self) -> bool { + if let (true, false) = (*self, *other) { + *self = false; + return true; + } + + false + } +} + +/// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation +/// of the least upper bounds of each element of the tuple (or list). +/// +/// In other words: +/// (A₀, A₁, ..., Aₙ) ∨ (B₀, B₁, ..., Bₙ) = (A₀∨B₀, A₁∨B₁, ..., Aₙ∨Bₙ) +impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> { + fn join(&mut self, other: &Self) -> bool { + assert_eq!(self.len(), other.len()); + + let mut changed = false; + for (a, b) in self.iter_mut().zip(other.iter()) { + changed |= a.join(b); + } + changed + } +} + +impl<I: Idx, T: MeetSemiLattice> MeetSemiLattice for IndexVec<I, T> { + fn meet(&mut self, other: &Self) -> bool { + assert_eq!(self.len(), other.len()); + + let mut changed = false; + for (a, b) in self.iter_mut().zip(other.iter()) { + changed |= a.meet(b); + } + changed + } +} + +/// A `BitSet` represents the lattice formed by the powerset of all possible values of +/// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices, +/// one for each possible value of `T`. +impl<T: Idx> JoinSemiLattice for BitSet<T> { + fn join(&mut self, other: &Self) -> bool { + self.union(other) + } +} + +impl<T: Idx> MeetSemiLattice for BitSet<T> { + fn meet(&mut self, other: &Self) -> bool { + self.intersect(other) + } +} + +/// The counterpart of a given semilattice `T` using the [inverse order]. +/// +/// The dual of a join-semilattice is a meet-semilattice and vice versa. For example, the dual of a +/// powerset has the empty set as its top element and the full set as its bottom element and uses +/// set *intersection* as its join operator. +/// +/// [inverse order]: https://en.wikipedia.org/wiki/Duality_(order_theory) +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Dual<T>(pub T); + +impl<T> std::borrow::Borrow<T> for Dual<T> { + fn borrow(&self) -> &T { + &self.0 + } +} + +impl<T> std::borrow::BorrowMut<T> for Dual<T> { + fn borrow_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +impl<T: MeetSemiLattice> JoinSemiLattice for Dual<T> { + fn join(&mut self, other: &Self) -> bool { + self.0.meet(&other.0) + } +} + +impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> { + fn meet(&mut self, other: &Self) -> bool { + self.0.join(&other.0) + } +} + +/// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no +/// value of `T` is comparable with any other. A flat set has the following [Hasse diagram]: +/// +/// ```text +/// top +/// / / \ \ +/// all possible values of `T` +/// \ \ / / +/// bottom +/// ``` +/// +/// [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FlatSet<T> { + Bottom, + Elem(T), + Top, +} + +impl<T: Clone + Eq> JoinSemiLattice for FlatSet<T> { + fn join(&mut self, other: &Self) -> bool { + let result = match (&*self, other) { + (Self::Top, _) | (_, Self::Bottom) => return false, + (Self::Elem(a), Self::Elem(b)) if a == b => return false, + + (Self::Bottom, Self::Elem(x)) => Self::Elem(x.clone()), + + _ => Self::Top, + }; + + *self = result; + true + } +} + +impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> { + fn meet(&mut self, other: &Self) -> bool { + let result = match (&*self, other) { + (Self::Bottom, _) | (_, Self::Top) => return false, + (Self::Elem(ref a), Self::Elem(ref b)) if a == b => return false, + + (Self::Top, Self::Elem(ref x)) => Self::Elem(x.clone()), + + _ => Self::Bottom, + }; + + *self = result; + true + } +} diff --git a/compiler/rustc_mir/src/dataflow/framework/mod.rs b/compiler/rustc_mir/src/dataflow/framework/mod.rs index a21bbacb467..eefa1395a62 100644 --- a/compiler/rustc_mir/src/dataflow/framework/mod.rs +++ b/compiler/rustc_mir/src/dataflow/framework/mod.rs @@ -30,8 +30,8 @@ //! //! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems +use std::borrow::BorrowMut; use std::cmp::Ordering; -use std::io; use rustc_hir::def_id::DefId; use rustc_index::bit_set::{BitSet, HybridBitSet}; @@ -43,69 +43,27 @@ use rustc_target::abi::VariantIdx; mod cursor; mod direction; mod engine; +pub mod fmt; mod graphviz; +pub mod lattice; mod visitor; pub use self::cursor::{ResultsCursor, ResultsRefCursor}; pub use self::direction::{Backward, Direction, Forward}; pub use self::engine::{Engine, Results}; +pub use self::lattice::{JoinSemiLattice, MeetSemiLattice}; pub use self::visitor::{visit_results, ResultsVisitor}; pub use self::visitor::{BorrowckFlowState, BorrowckResults}; -/// Parameterization for the precise form of data flow that is used. -/// -/// `BottomValue` determines whether the initial entry set for each basic block is empty or full. -/// This also determines the semantics of the lattice `join` operator used to merge dataflow -/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed -/// point. -/// -/// This means, for propagation across the graph, that you either want to start at all-zeroes and -/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect -/// as your merge when propagating. -pub trait BottomValue { - /// Specifies the initial value for each bit in the entry set for each basic block. - const BOTTOM_VALUE: bool; - - /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed. - /// - /// It is almost certainly wrong to override this, since it automatically applies - /// * `inout_set & in_set` if `BOTTOM_VALUE == true` - /// * `inout_set | in_set` if `BOTTOM_VALUE == false` - /// - /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks. - /// For clarity, the above statement again from a different perspective: - /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is - /// `!BOTTOM_VALUE`. - /// - /// There are situations where you want the opposite behaviour: propagate only if *all* - /// predecessor blocks's value is `!BOTTOM_VALUE`. - /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This - /// means that all code paths leading to the location must have set the bit, instead of any - /// code path leading there. - /// - /// If you want this kind of "definitely set" analysis, you need to - /// 1. Invert `BOTTOM_VALUE` - /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE` - /// 3. Override `join` to do the opposite from what it's doing now. - #[inline] - fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool { - if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) } - } -} - /// Define the domain of a dataflow problem. /// -/// This trait specifies the lattice on which this analysis operates. For now, this must be a -/// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet` -/// and referred to as the state vector. -/// -/// This trait also defines the initial value for the dataflow state upon entry to the -/// `START_BLOCK`, as well as some names used to refer to this analysis when debugging. -pub trait AnalysisDomain<'tcx>: BottomValue { - /// The type of the elements in the state vector. - type Idx: Idx; +/// This trait specifies the lattice on which this analysis operates (the domain) as well as its +/// initial value at the entry point of each basic block. +pub trait AnalysisDomain<'tcx> { + /// The type that holds the dataflow state at any given point in the program. + type Domain: Clone + JoinSemiLattice; - /// The direction of this analyis. Either `Forward` or `Backward`. + /// The direction of this analysis. Either `Forward` or `Backward`. type Direction: Direction = Forward; /// A descriptive name for this analysis. Used only for debugging. @@ -114,11 +72,10 @@ pub trait AnalysisDomain<'tcx>: BottomValue { /// suitable as part of a filename. const NAME: &'static str; - /// The size of the state vector. - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize; + /// The initial value of the dataflow state upon entry to each basic block. + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain; - /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow - /// analysis. + /// Mutates the initial value of the dataflow state upon entry to the `START_BLOCK`. /// /// For backward analyses, initial state besides the bottom value is not yet supported. Trying /// to mutate the initial state will result in a panic. @@ -126,20 +83,30 @@ pub trait AnalysisDomain<'tcx>: BottomValue { // FIXME: For backward dataflow analyses, the initial state should be applied to every basic // block where control flow could exit the MIR body (e.g., those terminated with `return` or // `resume`). It's not obvious how to handle `yield` points in generators, however. - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>); - - /// Prints an element in the state vector for debugging. - fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> { - write!(w, "{:?}", idx) - } + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain); } /// A dataflow problem with an arbitrarily complex transfer function. +/// +/// # Convergence +/// +/// When implementing this trait directly (not via [`GenKillAnalysis`]), it's possible to choose a +/// transfer function such that the analysis does not reach fixpoint. To guarantee convergence, +/// your transfer functions must maintain the following invariant: +/// +/// > If the dataflow state **before** some point in the program changes to be greater +/// than the prior state **before** that point, the dataflow state **after** that point must +/// also change to be greater than the prior state **after** that point. +/// +/// This invariant guarantees that the dataflow state at a given point in the program increases +/// monotonically until fixpoint is reached. Note that this monotonicity requirement only applies +/// to the same point in the program at different points in time. The dataflow state at a given +/// point in the program may or may not be greater than the state at any preceding point. pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// Updates the current dataflow state with the effect of evaluating a statement. fn apply_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, ); @@ -152,7 +119,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// analyses should not implement this without implementing `apply_statement_effect`. fn apply_before_statement_effect( &self, - _state: &mut BitSet<Self::Idx>, + _state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, _location: Location, ) { @@ -166,7 +133,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// initialized here. fn apply_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, ); @@ -179,7 +146,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// analyses should not implement this without implementing `apply_terminator_effect`. fn apply_before_terminator_effect( &self, - _state: &mut BitSet<Self::Idx>, + _state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, ) { @@ -192,7 +159,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// edges. fn apply_call_return_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, block: BasicBlock, func: &mir::Operand<'tcx>, args: &[mir::Operand<'tcx>], @@ -207,7 +174,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// By default, no effects happen. fn apply_yield_resume_effect( &self, - _state: &mut BitSet<Self::Idx>, + _state: &mut Self::Domain, _resume_block: BasicBlock, _resume_place: mir::Place<'tcx>, ) { @@ -222,7 +189,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// FIXME: This class of effects is not supported for backward dataflow analyses. fn apply_discriminant_switch_effect( &self, - _state: &mut BitSet<Self::Idx>, + _state: &mut Self::Domain, _block: BasicBlock, _enum_place: mir::Place<'tcx>, _adt: &ty::AdtDef, @@ -264,6 +231,8 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// /// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`. pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { + type Idx: Idx; + /// See `Analysis::apply_statement_effect`. fn statement_effect( &self, @@ -332,10 +301,11 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { impl<A> Analysis<'tcx> for A where A: GenKillAnalysis<'tcx>, + A::Domain: GenKill<A::Idx> + BorrowMut<BitSet<A::Idx>>, { fn apply_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, statement: &mir::Statement<'tcx>, location: Location, ) { @@ -344,7 +314,7 @@ where fn apply_before_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, statement: &mir::Statement<'tcx>, location: Location, ) { @@ -353,7 +323,7 @@ where fn apply_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -362,7 +332,7 @@ where fn apply_before_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -371,7 +341,7 @@ where fn apply_call_return_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, block: BasicBlock, func: &mir::Operand<'tcx>, args: &[mir::Operand<'tcx>], @@ -382,7 +352,7 @@ where fn apply_yield_resume_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, resume_block: BasicBlock, resume_place: mir::Place<'tcx>, ) { @@ -391,7 +361,7 @@ where fn apply_discriminant_switch_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut A::Domain, block: BasicBlock, enum_place: mir::Place<'tcx>, adt: &ty::AdtDef, @@ -450,7 +420,7 @@ pub trait GenKill<T> { /// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for /// the same element, the most recent one takes precedence. #[derive(Clone)] -pub struct GenKillSet<T: Idx> { +pub struct GenKillSet<T> { gen: HybridBitSet<T>, kill: HybridBitSet<T>, } @@ -464,7 +434,6 @@ impl<T: Idx> GenKillSet<T> { } } - /// Applies this transfer function to the given state vector. pub fn apply(&self, state: &mut BitSet<T>) { state.union(&self.gen); state.subtract(&self.kill); @@ -493,6 +462,16 @@ impl<T: Idx> GenKill<T> for BitSet<T> { } } +impl<T: Idx> GenKill<T> for lattice::Dual<BitSet<T>> { + fn gen(&mut self, elem: T) { + self.0.insert(elem); + } + + fn kill(&mut self, elem: T) { + self.0.remove(elem); + } +} + // NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Effect { diff --git a/compiler/rustc_mir/src/dataflow/framework/tests.rs b/compiler/rustc_mir/src/dataflow/framework/tests.rs index 9349f5133a5..a5989121679 100644 --- a/compiler/rustc_mir/src/dataflow/framework/tests.rs +++ b/compiler/rustc_mir/src/dataflow/framework/tests.rs @@ -9,7 +9,6 @@ use rustc_middle::ty; use rustc_span::DUMMY_SP; use super::*; -use crate::dataflow::BottomValue; /// Creates a `mir::Body` with a few disconnected basic blocks. /// @@ -92,13 +91,13 @@ impl<D: Direction> MockAnalysis<'tcx, D> { /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to /// avoid colliding with the statement/terminator effects. fn mock_entry_set(&self, bb: BasicBlock) -> BitSet<usize> { - let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); + let mut ret = self.bottom_value(self.body); ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index()); ret } fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> { - let empty = BitSet::new_empty(self.bits_per_block(self.body)); + let empty = self.bottom_value(self.body); let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks()); for (bb, _) in self.body.basic_blocks().iter_enumerated() { @@ -130,7 +129,7 @@ impl<D: Direction> MockAnalysis<'tcx, D> { /// would be `[102, 0, 1, 2, 3, 4]`. fn expected_state_at_target(&self, target: SeekTarget) -> BitSet<usize> { let block = target.block(); - let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); + let mut ret = self.bottom_value(self.body); ret.insert(Self::BASIC_BLOCK_OFFSET + block.index()); let target = match target { @@ -161,21 +160,17 @@ impl<D: Direction> MockAnalysis<'tcx, D> { } } -impl<D: Direction> BottomValue for MockAnalysis<'tcx, D> { - const BOTTOM_VALUE: bool = false; -} - impl<D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { - type Idx = usize; + type Domain = BitSet<usize>; type Direction = D; const NAME: &'static str = "mock"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + BitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len()) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint"); } } @@ -183,7 +178,7 @@ impl<D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, ) { @@ -193,7 +188,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_before_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, ) { @@ -203,7 +198,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -213,7 +208,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_before_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -223,7 +218,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_call_return_effect( &self, - _state: &mut BitSet<Self::Idx>, + _state: &mut Self::Domain, _block: BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], diff --git a/compiler/rustc_mir/src/dataflow/framework/visitor.rs b/compiler/rustc_mir/src/dataflow/framework/visitor.rs index 257f3cb9a6d..82eb734ed06 100644 --- a/compiler/rustc_mir/src/dataflow/framework/visitor.rs +++ b/compiler/rustc_mir/src/dataflow/framework/visitor.rs @@ -1,4 +1,3 @@ -use rustc_index::bit_set::BitSet; use rustc_middle::mir::{self, BasicBlock, Location}; use super::{Analysis, Direction, Results}; @@ -139,16 +138,16 @@ impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A> where A: Analysis<'tcx>, { - type FlowState = BitSet<A::Idx>; + type FlowState = A::Domain; type Direction = A::Direction; fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { - BitSet::new_empty(self.analysis.bits_per_block(body)) + self.analysis.bottom_value(body) } fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) { - state.overwrite(&self.entry_set_for_block(block)); + state.clone_from(&self.entry_set_for_block(block)); } fn reconstruct_before_statement_effect( @@ -217,11 +216,11 @@ macro_rules! impl_visitable { $( $A: Analysis<'tcx, Direction = D>, )* { type Direction = D; - type FlowState = $T<$( BitSet<$A::Idx> ),*>; + type FlowState = $T<$( $A::Domain ),*>; fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState { $T { - $( $field: BitSet::new_empty(self.$field.analysis.bits_per_block(body)) ),* + $( $field: self.$field.analysis.bottom_value(body) ),* } } @@ -230,7 +229,7 @@ macro_rules! impl_visitable { state: &mut Self::FlowState, block: BasicBlock, ) { - $( state.$field.overwrite(&self.$field.entry_set_for_block(block)); )* + $( state.$field.clone_from(&self.$field.entry_set_for_block(block)); )* } fn reconstruct_before_statement_effect( diff --git a/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs b/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs index a3fc51cad65..65e04ed6831 100644 --- a/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs +++ b/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs @@ -82,15 +82,15 @@ impl<K> AnalysisDomain<'tcx> for MaybeBorrowedLocals<K> where K: BorrowAnalysisKind<'tcx>, { - type Idx = Local; - + type Domain = BitSet<Local>; const NAME: &'static str = K::ANALYSIS_NAME; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls().len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = unborrowed + BitSet::new_empty(body.local_decls().len()) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { // No locals are aliased on function entry } } @@ -99,6 +99,8 @@ impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K> where K: BorrowAnalysisKind<'tcx>, { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -128,11 +130,6 @@ where } } -impl<K> BottomValue for MaybeBorrowedLocals<K> { - // bottom = unborrowed - const BOTTOM_VALUE: bool = false; -} - /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`. struct TransferFunction<'a, T, K> { trans: &'a mut T, diff --git a/compiler/rustc_mir/src/dataflow/impls/borrows.rs b/compiler/rustc_mir/src/dataflow/impls/borrows.rs index aeb7ffe3e3b..0be13b6ba81 100644 --- a/compiler/rustc_mir/src/dataflow/impls/borrows.rs +++ b/compiler/rustc_mir/src/dataflow/impls/borrows.rs @@ -8,9 +8,9 @@ use rustc_index::bit_set::BitSet; use crate::borrow_check::{ places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid, }; -use crate::dataflow::BottomValue; -use crate::dataflow::{self, GenKill}; +use crate::dataflow::{self, fmt::DebugWithContext, GenKill}; +use std::fmt; use std::rc::Rc; rustc_index::newtype_index! { @@ -227,25 +227,24 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } impl<'tcx> dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> { - type Idx = BorrowIndex; + type Domain = BitSet<BorrowIndex>; const NAME: &'static str = "borrows"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.borrow_set.len() * 2 + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = nothing is reserved or activated yet; + BitSet::new_empty(self.borrow_set.len() * 2) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { // no borrows of code region_scopes have been taken prior to // function execution, so this method has no effect. } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, idx: Self::Idx) -> std::io::Result<()> { - write!(w, "{:?}", self.location(idx)) - } } impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { + type Idx = BorrowIndex; + fn before_statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -344,7 +343,8 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } } -impl<'a, 'tcx> BottomValue for Borrows<'a, 'tcx> { - /// bottom = nothing is reserved or activated yet; - const BOTTOM_VALUE: bool = false; +impl DebugWithContext<Borrows<'_, '_>> for BorrowIndex { + fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", ctxt.location(*self)) + } } diff --git a/compiler/rustc_mir/src/dataflow/impls/init_locals.rs b/compiler/rustc_mir/src/dataflow/impls/init_locals.rs index 0e7cd1bb0e4..bb7292cd033 100644 --- a/compiler/rustc_mir/src/dataflow/impls/init_locals.rs +++ b/compiler/rustc_mir/src/dataflow/impls/init_locals.rs @@ -2,7 +2,7 @@ //! //! A local will be maybe initialized if *any* projections of that local might be initialized. -use crate::dataflow::{self, BottomValue, GenKill}; +use crate::dataflow::{self, GenKill}; use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{PlaceContext, Visitor}; @@ -10,21 +10,17 @@ use rustc_middle::mir::{self, BasicBlock, Local, Location}; pub struct MaybeInitializedLocals; -impl BottomValue for MaybeInitializedLocals { - /// bottom = uninit - const BOTTOM_VALUE: bool = false; -} - impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals { - type Idx = Local; + type Domain = BitSet<Local>; const NAME: &'static str = "maybe_init_locals"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = uninit + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut Self::Domain) { // Function arguments are initialized to begin with. for arg in body.args_iter() { entry_set.insert(arg); @@ -33,6 +29,8 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals { } impl dataflow::GenKillAnalysis<'tcx> for MaybeInitializedLocals { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -99,7 +97,6 @@ where PlaceContext::NonUse( NonUseContext::StorageLive | NonUseContext::AscribeUserTy - | NonUseContext::Coverage | NonUseContext::VarDebugInfo, ) | PlaceContext::NonMutatingUse( diff --git a/compiler/rustc_mir/src/dataflow/impls/liveness.rs b/compiler/rustc_mir/src/dataflow/impls/liveness.rs index 784b0bd9293..b0da28156d1 100644 --- a/compiler/rustc_mir/src/dataflow/impls/liveness.rs +++ b/compiler/rustc_mir/src/dataflow/impls/liveness.rs @@ -2,7 +2,7 @@ use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, Local, Location}; -use crate::dataflow::{AnalysisDomain, Backward, BottomValue, GenKill, GenKillAnalysis}; +use crate::dataflow::{AnalysisDomain, Backward, GenKill, GenKillAnalysis}; /// A [live-variable dataflow analysis][liveness]. /// @@ -22,27 +22,25 @@ impl MaybeLiveLocals { } } -impl BottomValue for MaybeLiveLocals { - // bottom = not live - const BOTTOM_VALUE: bool = false; -} - impl AnalysisDomain<'tcx> for MaybeLiveLocals { - type Idx = Local; + type Domain = BitSet<Local>; type Direction = Backward; const NAME: &'static str = "liveness"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = not live + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { // No variables are live until we observe a use } } impl GenKillAnalysis<'tcx> for MaybeLiveLocals { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, diff --git a/compiler/rustc_mir/src/dataflow/impls/mod.rs b/compiler/rustc_mir/src/dataflow/impls/mod.rs index 8975faec487..1769feaf7a5 100644 --- a/compiler/rustc_mir/src/dataflow/impls/mod.rs +++ b/compiler/rustc_mir/src/dataflow/impls/mod.rs @@ -13,7 +13,7 @@ use super::MoveDataParamEnv; use crate::util::elaborate_drops::DropFlagState; use super::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex}; -use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis}; +use super::{lattice, AnalysisDomain, GenKill, GenKillAnalysis}; use super::drop_flag_effects_for_function_entry; use super::drop_flag_effects_for_location; @@ -204,7 +204,7 @@ impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { /// `EverInitializedPlaces` tracks all places that might have ever been /// initialized upon reaching a particular point in the control flow -/// for a function, without an intervening `Storage Dead`. +/// for a function, without an intervening `StorageDead`. /// /// This dataflow is used to determine if an immutable local variable may /// be assigned to. @@ -290,27 +290,25 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { } impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { - type Idx = MovePathIndex; - + type Domain = BitSet<MovePathIndex>; const NAME: &'static str = "maybe_init"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().move_paths.len() + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = uninitialized + BitSet::new_empty(self.move_data().move_paths.len()) } - fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { assert!(s == DropFlagState::Present); state.insert(path); }); } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> { - write!(w, "{}", self.move_data().move_paths[mpi]) - } } impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { + type Idx = MovePathIndex; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -376,18 +374,18 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { - type Idx = MovePathIndex; + type Domain = BitSet<MovePathIndex>; const NAME: &'static str = "maybe_uninit"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().move_paths.len() + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = initialized (start_block_effect counters this at outset) + BitSet::new_empty(self.move_data().move_paths.len()) } // sets on_entry bits for Arg places - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { // set all bits to 1 (uninit) before gathering counterevidence - assert!(self.bits_per_block(body) == state.domain_size()); state.insert_all(); drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { @@ -395,13 +393,11 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { state.remove(path); }); } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> { - write!(w, "{}", self.move_data().move_paths[mpi]) - } } impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { + type Idx = MovePathIndex; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -471,30 +467,30 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { - type Idx = MovePathIndex; + /// Use set intersection as the join operator. + type Domain = lattice::Dual<BitSet<MovePathIndex>>; const NAME: &'static str = "definite_init"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().move_paths.len() + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = initialized (start_block_effect counters this at outset) + lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len())) } // sets on_entry bits for Arg places - fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { - state.clear(); + fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) { + state.0.clear(); drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| { assert!(s == DropFlagState::Present); - state.insert(path); + state.0.insert(path); }); } - - fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> { - write!(w, "{}", self.move_data().move_paths[mpi]) - } } impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { + type Idx = MovePathIndex; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -540,15 +536,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { - type Idx = InitIndex; + type Domain = BitSet<InitIndex>; const NAME: &'static str = "ever_init"; - fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize { - self.move_data().inits.len() + fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { + // bottom = no initialized variables by default + BitSet::new_empty(self.move_data().inits.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { for arg_init in 0..body.arg_count { state.insert(InitIndex::new(arg_init)); } @@ -556,6 +553,8 @@ impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { } impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { + type Idx = InitIndex; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -625,23 +624,3 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { } } } - -impl<'a, 'tcx> BottomValue for MaybeInitializedPlaces<'a, 'tcx> { - /// bottom = uninitialized - const BOTTOM_VALUE: bool = false; -} - -impl<'a, 'tcx> BottomValue for MaybeUninitializedPlaces<'a, 'tcx> { - /// bottom = initialized (start_block_effect counters this at outset) - const BOTTOM_VALUE: bool = false; -} - -impl<'a, 'tcx> BottomValue for DefinitelyInitializedPlaces<'a, 'tcx> { - /// bottom = initialized (start_block_effect counters this at outset) - const BOTTOM_VALUE: bool = true; -} - -impl<'a, 'tcx> BottomValue for EverInitializedPlaces<'a, 'tcx> { - /// bottom = no initialized variables by default - const BOTTOM_VALUE: bool = false; -} diff --git a/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs index 21623e3cad5..9250cd40847 100644 --- a/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs +++ b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs @@ -1,6 +1,5 @@ pub use super::*; -use crate::dataflow::BottomValue; use crate::dataflow::{self, GenKill, Results, ResultsRefCursor}; use crate::util::storage::AlwaysLiveLocals; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; @@ -19,15 +18,16 @@ impl MaybeStorageLive { } impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive { - type Idx = Local; + type Domain = BitSet<Local>; const NAME: &'static str = "maybe_storage_live"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) { assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size()); for local in self.always_live_locals.iter() { on_entry.insert(local); @@ -40,6 +40,8 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive { } impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive { + type Idx = Local; + fn statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -74,11 +76,6 @@ impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive { } } -impl BottomValue for MaybeStorageLive { - /// bottom = dead - const BOTTOM_VALUE: bool = false; -} - type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>; /// Dataflow analysis that determines whether each local requires storage at a @@ -101,15 +98,16 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { } impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { - type Idx = Local; + type Domain = BitSet<Local>; const NAME: &'static str = "requires_storage"; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) { // The resume argument is live on function entry (we don't care about // the `self` argument) for arg in body.args_iter().skip(1) { @@ -119,6 +117,8 @@ impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, ' } impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { + type Idx = Local; + fn before_statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, @@ -285,11 +285,6 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { } } -impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> { - /// bottom = dead - const BOTTOM_VALUE: bool = false; -} - struct MoveVisitor<'a, 'mir, 'tcx, T> { borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>, trans: &'a mut T, diff --git a/compiler/rustc_mir/src/dataflow/mod.rs b/compiler/rustc_mir/src/dataflow/mod.rs index a0c24636059..5575a97982f 100644 --- a/compiler/rustc_mir/src/dataflow/mod.rs +++ b/compiler/rustc_mir/src/dataflow/mod.rs @@ -5,9 +5,9 @@ use rustc_span::symbol::{sym, Symbol}; pub(crate) use self::drop_flag_effects::*; pub use self::framework::{ - visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState, BorrowckResults, - BottomValue, Engine, Forward, GenKill, GenKillAnalysis, Results, ResultsCursor, - ResultsRefCursor, ResultsVisitor, + fmt, lattice, visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState, + BorrowckResults, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, + ResultsCursor, ResultsRefCursor, ResultsVisitor, }; use self::move_paths::MoveData; diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs index e088dc6a954..5c3e3538401 100644 --- a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs +++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs @@ -4,7 +4,6 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use smallvec::{smallvec, SmallVec}; -use std::convert::TryInto; use std::mem; use super::abs_domain::Lift; @@ -110,7 +109,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { let body = self.builder.body; let tcx = self.builder.tcx; let place_ty = Place::ty_from(place.local, proj_base, body, tcx).ty; - match place_ty.kind { + match place_ty.kind() { ty::Ref(..) | ty::RawPtr(..) => { let proj = &place.projection[..i + 1]; return Err(MoveError::cannot_move_out_of( @@ -480,13 +479,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { } }; let base_ty = base_place.ty(self.builder.body, self.builder.tcx).ty; - let len: u64 = match base_ty.kind { - ty::Array(_, size) => { - let length = size.eval_usize(self.builder.tcx, self.builder.param_env); - length - .try_into() - .expect("slice pattern of array with more than u32::MAX elements") - } + let len: u64 = match base_ty.kind() { + ty::Array(_, size) => size.eval_usize(self.builder.tcx, self.builder.param_env), _ => bug!("from_end: false slice pattern of non-array type"), }; for offset in from..to { @@ -525,7 +519,9 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { // of the union so it is marked as initialized again. if let [proj_base @ .., ProjectionElem::Field(_, _)] = place.projection { if let ty::Adt(def, _) = - Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx).ty.kind + Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx) + .ty + .kind() { if def.is_union() { place = PlaceRef { local: place.local, projection: proj_base } diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs index 501a5bcddb3..0e16b0caefa 100644 --- a/compiler/rustc_mir/src/interpret/cast.rs +++ b/compiler/rustc_mir/src/interpret/cast.rs @@ -47,7 +47,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Pointer(PointerCast::ReifyFnPointer) => { // The src operand does not matter, just its type - match src.layout.ty.kind { + match *src.layout.ty.kind() { ty::FnDef(def_id, substs) => { // All reifications must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; @@ -76,7 +76,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Pointer(PointerCast::UnsafeFnPointer) => { let src = self.read_immediate(src)?; - match cast_ty.kind { + match cast_ty.kind() { ty::FnPtr(_) => { // No change to value self.write_immediate(*src, dest)?; @@ -87,7 +87,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Pointer(PointerCast::ClosureFnPointer(_)) => { // The src operand does not matter, just its type - match src.layout.ty.kind { + match *src.layout.ty.kind() { ty::Closure(def_id, substs) => { // All reifications must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; @@ -116,7 +116,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { use rustc_middle::ty::TyKind::*; trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty); - match src.layout.ty.kind { + match src.layout.ty.kind() { // Floating point Float(FloatTy::F32) => { return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, cast_ty).into()); @@ -196,9 +196,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let v = if signed { self.sign_extend(v, src_layout) } else { v }; trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty); use rustc_middle::ty::TyKind::*; - match cast_ty.kind { + match *cast_ty.kind() { Int(_) | Uint(_) | RawPtr(_) => { - let size = match cast_ty.kind { + let size = match *cast_ty.kind() { Int(t) => Integer::from_attr(self, attr::IntType::SignedInt(t)).size(), Uint(t) => Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(), RawPtr(_) => self.pointer_size(), @@ -228,7 +228,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>, { use rustc_middle::ty::TyKind::*; - match dest_ty.kind { + match *dest_ty.kind() { // float -> uint Uint(t) => { let size = Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(); @@ -267,7 +267,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env); - match (&src_pointee_ty.kind, &dest_pointee_ty.kind) { + match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) { (&ty::Array(_, length), &ty::Slice(_)) => { let ptr = self.read_immediate(src)?.to_scalar()?; // u64 cast is from usize to u64, which is always good @@ -303,7 +303,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { dest: PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty); - match (&src.layout.ty.kind, &cast_ty.ty.kind) { + match (&src.layout.ty.kind(), &cast_ty.ty.kind()) { (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. })) | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => { self.unsize_into_ptr(src, dest, s, c) diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index 525da87463a..f97096984fa 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -20,7 +20,7 @@ use rustc_span::{Pos, Span}; use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size, TargetDataLayout}; use super::{ - Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, OpTy, Operand, Place, PlaceTy, + Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, Operand, Place, PlaceTy, ScalarMaybeUninit, StackPopJump, }; use crate::transform::validate::equal_up_to_regions; @@ -482,13 +482,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// The `substs` are assumed to already be in our interpreter "universe" (param_env). pub(super) fn resolve( &self, - def_id: DefId, + def: ty::WithOptConstParam<DefId>, substs: SubstsRef<'tcx>, ) -> InterpResult<'tcx, ty::Instance<'tcx>> { - trace!("resolve: {:?}, {:#?}", def_id, substs); + trace!("resolve: {:?}, {:#?}", def, substs); trace!("param_env: {:#?}", self.param_env); trace!("substs: {:#?}", substs); - match ty::Instance::resolve(*self.tcx, self.param_env, def_id, substs) { + match ty::Instance::resolve_opt_const_arg(*self.tcx, self.param_env, def, substs) { Ok(Some(instance)) => Ok(instance), Ok(None) => throw_inval!(TooGeneric), @@ -534,7 +534,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if !layout.is_unsized() { return Ok(Some((layout.size, layout.align.abi))); } - match layout.ty.kind { + match layout.ty.kind() { ty::Adt(..) | ty::Tuple(..) => { // First get the size of all statically known fields. // Don't use type_of::sizing_type_of because that expects t to be sized, @@ -875,32 +875,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } - pub(super) fn const_eval( - &self, - gid: GlobalId<'tcx>, - ty: Ty<'tcx>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - // For statics we pick `ParamEnv::reveal_all`, because statics don't have generics - // and thus don't care about the parameter environment. While we could just use - // `self.param_env`, that would mean we invoke the query to evaluate the static - // with different parameter environments, thus causing the static to be evaluated - // multiple times. - let param_env = if self.tcx.is_static(gid.instance.def_id()) { - ty::ParamEnv::reveal_all() - } else { - self.param_env - }; - let val = self.tcx.const_eval_global_id(param_env, gid, Some(self.tcx.span))?; - - // Even though `ecx.const_eval` is called from `const_to_op` we can never have a - // recursion deeper than one level, because the `tcx.const_eval` above is guaranteed to not - // return `ConstValue::Unevaluated`, which is the only way that `const_to_op` will call - // `ecx.const_eval`. - let const_ = ty::Const { val: ty::ConstKind::Value(val), ty }; - self.const_to_op(&const_, None) - } - - pub fn const_eval_raw( + pub fn eval_to_allocation( &self, gid: GlobalId<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { @@ -914,14 +889,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } else { self.param_env }; - // We use `const_eval_raw` here, and get an unvalidated result. That is okay: - // Our result will later be validated anyway, and there seems no good reason - // to have to fail early here. This is also more consistent with - // `Memory::get_static_alloc` which has to use `const_eval_raw` to avoid cycles. - // FIXME: We can hit delay_span_bug if this is an invalid const, interning finds - // that problem, but we never run validation to show an error. Can we ensure - // this does not happen? - let val = self.tcx.const_eval_raw(param_env.and(gid))?; + let val = self.tcx.eval_to_allocation_raw(param_env.and(gid))?; self.raw_const_to_mplace(val) } diff --git a/compiler/rustc_mir/src/interpret/intern.rs b/compiler/rustc_mir/src/interpret/intern.rs index 606be7cad2b..dd5e9c99774 100644 --- a/compiler/rustc_mir/src/interpret/intern.rs +++ b/compiler/rustc_mir/src/interpret/intern.rs @@ -195,13 +195,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir // Raw pointers (and boxes) are handled by the `leftover_relocations` logic. let tcx = self.ecx.tcx; let ty = mplace.layout.ty; - if let ty::Ref(_, referenced_ty, ref_mutability) = ty.kind { + if let ty::Ref(_, referenced_ty, ref_mutability) = *ty.kind() { let value = self.ecx.read_immediate(mplace.into())?; let mplace = self.ecx.ref_to_mplace(value)?; assert_eq!(mplace.layout.ty, referenced_ty); // Handle trait object vtables. if let ty::Dynamic(..) = - tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind + tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind() { // Validation will error (with a better message) on an invalid vtable pointer // so we can safely not do anything if this is not a real pointer. @@ -253,7 +253,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir // This helps to prevent users from accidentally exploiting UB that they // caused (by somehow getting a mutable reference in a `const`). if ref_mutability == Mutability::Mut { - match referenced_ty.kind { + match referenced_ty.kind() { ty::Array(_, n) if n.eval_usize(*tcx, self.ecx.param_env) == 0 => {} ty::Slice(_) if mplace.meta.unwrap_meta().to_machine_usize(self.ecx)? diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index b37dcd42f4c..0664f25e409 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -76,7 +76,7 @@ crate fn eval_nullary_intrinsic<'tcx>( ConstValue::from_u64(tcx.type_id_hash(tp_ty)) } sym::variant_count => { - if let ty::Adt(ref adt, _) = tp_ty.kind { + if let ty::Adt(ref adt, _) = tp_ty.kind() { ConstValue::from_machine_usize(adt.variants.len() as u64, &tcx) } else { ConstValue::from_machine_usize(0u64, &tcx) @@ -88,6 +88,8 @@ crate fn eval_nullary_intrinsic<'tcx>( impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Returns `true` if emulation happened. + /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own + /// intrinsic handling. pub fn emulate_intrinsic( &mut self, instance: ty::Instance<'tcx>, @@ -150,7 +152,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::type_name => self.tcx.mk_static_str(), _ => bug!("already checked for nullary intrinsics"), }; - let val = self.const_eval(gid, ty)?; + let val = + self.tcx.const_eval_global_id(self.param_env, gid, Some(self.tcx.span))?; + let const_ = ty::Const { val: ty::ConstKind::Value(val), ty }; + let val = self.const_to_op(&const_, None)?; self.copy_op(val, dest)?; } @@ -328,16 +333,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self); self.write_scalar(offset_ptr, dest)?; } - sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { - let a = self.read_immediate(args[0])?.to_scalar()?; - let b = self.read_immediate(args[1])?.to_scalar()?; - let cmp = if intrinsic_name == sym::ptr_guaranteed_eq { - self.guaranteed_eq(a, b) - } else { - self.guaranteed_ne(a, b) - }; - self.write_scalar(Scalar::from_bool(cmp), dest)?; - } sym::ptr_offset_from => { let a = self.read_immediate(args[0])?.to_scalar()?; let b = self.read_immediate(args[1])?.to_scalar()?; @@ -448,37 +443,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(true) } - fn guaranteed_eq(&mut self, a: Scalar<M::PointerTag>, b: Scalar<M::PointerTag>) -> bool { - match (a, b) { - // Comparisons between integers are always known. - (Scalar::Raw { .. }, Scalar::Raw { .. }) => a == b, - // Equality with integers can never be known for sure. - (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false, - // FIXME: return `true` for when both sides are the same pointer, *except* that - // some things (like functions and vtables) do not have stable addresses - // so we need to be careful around them. - (Scalar::Ptr(_), Scalar::Ptr(_)) => false, - } - } - - fn guaranteed_ne(&mut self, a: Scalar<M::PointerTag>, b: Scalar<M::PointerTag>) -> bool { - match (a, b) { - // Comparisons between integers are always known. - (Scalar::Raw { .. }, Scalar::Raw { .. }) => a != b, - // Comparisons of abstract pointers with null pointers are known if the pointer - // is in bounds, because if they are in bounds, the pointer can't be null. - (Scalar::Raw { data: 0, .. }, Scalar::Ptr(ptr)) - | (Scalar::Ptr(ptr), Scalar::Raw { data: 0, .. }) => !self.memory.ptr_may_be_null(ptr), - // Inequality with integers other than null can never be known for sure. - (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false, - // FIXME: return `true` for at least some comparisons where we can reliably - // determine the result of runtime inequality tests at compile-time. - // Examples include comparison of addresses in static items, for these we can - // give reliable results. - (Scalar::Ptr(_), Scalar::Ptr(_)) => false, - } - } - pub fn exact_div( &mut self, a: ImmTy<'tcx, M::PointerTag>, diff --git a/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs index 379117f3b84..8c0014e10d0 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs @@ -32,7 +32,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { } fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> { - match ty.kind { + match *ty.kind() { // Types without identity. ty::Bool | ty::Char diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs index d4be2ce0568..f3e373813ca 100644 --- a/compiler/rustc_mir/src/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -285,9 +285,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { None => { // Deallocating global memory -- always an error return Err(match self.tcx.get_global_alloc(ptr.alloc_id) { - Some(GlobalAlloc::Function(..)) => err_ub_format!("deallocating a function"), + Some(GlobalAlloc::Function(..)) => { + err_ub_format!("deallocating {}, which is a function", ptr.alloc_id) + } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { - err_ub_format!("deallocating static memory") + err_ub_format!("deallocating {}, which is static memory", ptr.alloc_id) } None => err_ub!(PointerUseAfterFree(ptr.alloc_id)), } @@ -297,7 +299,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if alloc_kind != kind { throw_ub_format!( - "deallocating {} memory using {} deallocation operation", + "deallocating {}, which is {} memory, using {} deallocation operation", + ptr.alloc_id, alloc_kind, kind ); @@ -305,7 +308,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if let Some((size, align)) = old_size_and_align { if size != alloc.size || align != alloc.align { throw_ub_format!( - "incorrect layout on deallocation: allocation has size {} and alignment {}, but gave size {} and alignment {}", + "incorrect layout on deallocation: {} has size {} and alignment {}, but gave size {} and alignment {}", + ptr.alloc_id, alloc.size.bytes(), alloc.align.bytes(), size.bytes(), @@ -469,7 +473,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // Notice that every static has two `AllocId` that will resolve to the same // thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID, // and the other one is maps to `GlobalAlloc::Memory`, this is returned by - // `const_eval_raw` and it is the "resolved" ID. + // `eval_static_initializer` and it is the "resolved" ID. // The resolved ID is never used by the interpreted program, it is hidden. // This is relied upon for soundness of const-patterns; a pointer to the resolved // ID would "sidestep" the checks that make sure consts do not point to statics! diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index 0b58caef54d..735f890a33b 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -549,21 +549,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // Early-return cases. let val_val = match val.val { - ty::ConstKind::Param(_) => throw_inval!(TooGeneric), + ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric), ty::ConstKind::Error(_) => throw_inval!(TypeckError(ErrorReported)), ty::ConstKind::Unevaluated(def, substs, promoted) => { - let instance = self.resolve(def.did, substs)?; - // We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation. - // The reason we use `const_eval_raw` everywhere else is to prevent cycles during - // validation, because validation automatically reads through any references, thus - // potentially requiring the current static to be evaluated again. This is not a - // problem here, because we are building an operand which means an actual read is - // happening. - return Ok(self.const_eval(GlobalId { instance, promoted }, val.ty)?); + let instance = self.resolve(def, substs)?; + return Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into()); } - ty::ConstKind::Infer(..) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Placeholder(..) => { + ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => { span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val) } ty::ConstKind::Value(val_val) => val_val, @@ -662,7 +654,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty); let discr_bits = discr_val.assert_bits(discr_layout.size); // Convert discriminant to variant index, and catch invalid discriminants. - let index = match op.layout.ty.kind { + let index = match *op.layout.ty.kind() { ty::Adt(adt, _) => { adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits) } diff --git a/compiler/rustc_mir/src/interpret/operator.rs b/compiler/rustc_mir/src/interpret/operator.rs index 30c40b8fde9..fc266fa74bf 100644 --- a/compiler/rustc_mir/src/interpret/operator.rs +++ b/compiler/rustc_mir/src/interpret/operator.rs @@ -282,7 +282,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { right.layout.ty ); - match left.layout.ty.kind { + match left.layout.ty.kind() { ty::Char => { assert_eq!(left.layout.ty, right.layout.ty); let left = left.to_scalar()?; @@ -368,7 +368,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = val.to_scalar()?; trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty); - match layout.ty.kind { + match layout.ty.kind() { ty::Bool => { let val = val.to_bool()?; let res = match un_op { diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index 6ba6103b311..72551b23370 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -13,9 +13,9 @@ use rustc_target::abi::{Abi, Align, FieldsShape, TagEncoding}; use rustc_target::abi::{HasDataLayout, LayoutOf, Size, VariantIdx, Variants}; use super::{ - mir_assign_valid_types, truncate, AllocId, AllocMap, Allocation, AllocationExtra, ImmTy, - Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer, - PointerArithmetic, RawConst, Scalar, ScalarMaybeUninit, + mir_assign_valid_types, truncate, AllocId, AllocMap, Allocation, AllocationExtra, ConstAlloc, + ImmTy, Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, + Pointer, PointerArithmetic, Scalar, ScalarMaybeUninit, }; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] @@ -202,7 +202,7 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { pub(super) fn len(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { if self.layout.is_unsized() { // We need to consult `meta` metadata - match self.layout.ty.kind { + match self.layout.ty.kind() { ty::Slice(..) | ty::Str => self.mplace.meta.unwrap_meta().to_machine_usize(cx), _ => bug!("len not supported on unsized type {:?}", self.layout.ty), } @@ -218,7 +218,7 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { #[inline] pub(super) fn vtable(self) -> Scalar<Tag> { - match self.layout.ty.kind { + match self.layout.ty.kind() { ty::Dynamic(..) => self.mplace.meta.unwrap_meta(), _ => bug!("vtable not supported on type {:?}", self.layout.ty), } @@ -498,7 +498,7 @@ where // Compute meta and new layout let inner_len = actual_to.checked_sub(from).unwrap(); - let (meta, ty) = match base.layout.ty.kind { + let (meta, ty) = match base.layout.ty.kind() { // It is not nice to match on the type, but that seems to be the only way to // implement this. ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(inner, inner_len)), @@ -551,7 +551,7 @@ where let n = base.len(self)?; if n < min_length { // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len: min_length.into(), index: n }); + throw_ub!(BoundsCheckFailed { len: min_length, index: n }); } let index = if from_end { @@ -565,9 +565,7 @@ where self.mplace_index(base, index)? } - Subslice { from, to, from_end } => { - self.mplace_subslice(base, u64::from(from), u64::from(to), from_end)? - } + Subslice { from, to, from_end } => self.mplace_subslice(base, from, to, from_end)?, }) } @@ -1122,7 +1120,7 @@ where pub fn raw_const_to_mplace( &self, - raw: RawConst<'tcx>, + raw: ConstAlloc<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { // This must be an allocation in `tcx` let _ = self.tcx.global_alloc(raw.alloc_id); diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index 9a036a0f299..b789cb76e9f 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -55,7 +55,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let old_stack = self.frame_idx(); let old_loc = self.frame().loc; let func = self.eval_operand(func, None)?; - let (fn_val, abi) = match func.layout.ty.kind { + let (fn_val, abi) = match *func.layout.ty.kind() { ty::FnPtr(sig) => { let caller_abi = sig.abi(); let fn_ptr = self.read_scalar(func)?.check_init()?; @@ -64,7 +64,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } ty::FnDef(def_id, substs) => { let sig = func.layout.ty.fn_sig(*self.tcx); - (FnVal::Instance(self.resolve(def_id, substs)?), sig.abi()) + ( + FnVal::Instance( + self.resolve(ty::WithOptConstParam::unknown(def_id), substs)?, + ), + sig.abi(), + ) } _ => span_bug!( terminator.source_info.span, @@ -222,7 +227,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { { let callee_abi = { let instance_ty = instance.ty(*self.tcx, self.param_env); - match instance_ty.kind { + match instance_ty.kind() { ty::FnDef(..) => instance_ty.fn_sig(*self.tcx).abi(), ty::Closure(..) => Abi::RustCall, ty::Generator(..) => Abi::Rust, @@ -431,7 +436,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // implementation fail -- a problem shared by rustc. let place = self.force_allocation(place)?; - let (instance, place) = match place.layout.ty.kind { + let (instance, place) = match place.layout.ty.kind() { ty::Dynamic(..) => { // Dropping a trait object. self.unpack_dyn_trait(place)? diff --git a/compiler/rustc_mir/src/interpret/util.rs b/compiler/rustc_mir/src/interpret/util.rs index 57c5fc59cc0..fc5a25ffbf2 100644 --- a/compiler/rustc_mir/src/interpret/util.rs +++ b/compiler/rustc_mir/src/interpret/util.rs @@ -33,7 +33,7 @@ where return false; } - match ty.kind { + match *ty.kind() { ty::Param(_) => true, ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) @@ -59,7 +59,7 @@ where // `ty::Param`/`ty::ConstKind::Param`. (false, true) if cfg!(debug_assertions) => match subst.unpack() { ty::subst::GenericArgKind::Type(ty) => { - assert!(matches!(ty.kind, ty::Param(_))) + assert!(matches!(ty.kind(), ty::Param(_))) } ty::subst::GenericArgKind::Const(ct) => { assert!(matches!(ct.val, ty::ConstKind::Param(_))) diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 9cd20340138..2b83e1c8134 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -26,18 +26,22 @@ use super::{ macro_rules! throw_validation_failure { ($where:expr, { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )?) => {{ - let mut msg = String::new(); - msg.push_str("encountered "); - write!(&mut msg, $($what_fmt),+).unwrap(); - let where_ = &$where; - if !where_.is_empty() { - msg.push_str(" at "); - write_path(&mut msg, where_); - } - $( - msg.push_str(", but expected "); - write!(&mut msg, $($expected_fmt),+).unwrap(); - )? + let msg = rustc_middle::ty::print::with_no_trimmed_paths(|| { + let mut msg = String::new(); + msg.push_str("encountered "); + write!(&mut msg, $($what_fmt),+).unwrap(); + let where_ = &$where; + if !where_.is_empty() { + msg.push_str(" at "); + write_path(&mut msg, where_); + } + $( + msg.push_str(", but expected "); + write!(&mut msg, $($expected_fmt),+).unwrap(); + )? + + msg + }); throw_ub!(ValidationFailure(msg)) }}; } @@ -210,7 +214,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' match layout.variants { Variants::Multiple { tag_field, .. } => { if tag_field == field { - return match layout.ty.kind { + return match layout.ty.kind() { ty::Adt(def, ..) if def.is_enum() => PathElem::EnumTag, ty::Generator(..) => PathElem::GeneratorTag, _ => bug!("non-variant type {:?}", layout.ty), @@ -221,7 +225,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } // Now we know we are projecting to a field, so figure out which one. - match layout.ty.kind { + match layout.ty.kind() { // generators and closures. ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { let mut name = None; @@ -299,7 +303,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' pointee: TyAndLayout<'tcx>, ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); - match tail.kind { + match tail.kind() { ty::Dynamic(..) => { let vtable = meta.unwrap_meta(); // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines. @@ -421,26 +425,28 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' let alloc_kind = self.ecx.tcx.get_global_alloc(ptr.alloc_id); if let Some(GlobalAlloc::Static(did)) = alloc_kind { assert!(!self.ecx.tcx.is_thread_local_static(did)); - // See const_eval::machine::MemoryExtra::can_access_statics for why - // this check is so important. - // This check is reachable when the const just referenced the static, - // but never read it (so we never entered `before_access_global`). - // We also need to do it here instead of going on to avoid running - // into the `before_access_global` check during validation. - if !self.may_ref_to_static && self.ecx.tcx.is_static(did) { + assert!(self.ecx.tcx.is_static(did)); + if self.may_ref_to_static { + // We skip checking other statics. These statics must be sound by + // themselves, and the only way to get broken statics here is by using + // unsafe code. + // The reasons we don't check other statics is twofold. For one, in all + // sound cases, the static was already validated on its own, and second, we + // trigger cycle errors if we try to compute the value of the other static + // and that static refers back to us. + // We might miss const-invalid data, + // but things are still sound otherwise (in particular re: consts + // referring to statics). + return Ok(()); + } else { + // See const_eval::machine::MemoryExtra::can_access_statics for why + // this check is so important. + // This check is reachable when the const just referenced the static, + // but never read it (so we never entered `before_access_global`). throw_validation_failure!(self.path, { "a {} pointing to a static variable", kind } ); } - // `extern static` cannot be validated as they have no body. - // FIXME: Statics from other crates are also skipped. - // They might be checked at a different type, but for now we - // want to avoid recursing too deeply. We might miss const-invalid data, - // but things are still sound otherwise (in particular re: consts - // referring to statics). - if !did.is_local() || self.ecx.tcx.is_foreign_item(did) { - return Ok(()); - } } } // Proceed recursively even for ZST, no reason to skip them! @@ -473,7 +479,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ) -> InterpResult<'tcx, bool> { // Go over all the primitive types let ty = value.layout.ty; - match ty.kind { + match ty.kind() { ty::Bool => { let value = self.ecx.read_scalar(value)?; try_validation!( @@ -688,7 +694,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> variant_id: VariantIdx, new_op: OpTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { - let name = match old_op.layout.ty.kind { + let name = match old_op.layout.ty.kind() { ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name), // Generators also have variants ty::Generator(..) => PathElem::GeneratorState(variant_id), @@ -758,7 +764,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> op: OpTy<'tcx, M::PointerTag>, fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>, ) -> InterpResult<'tcx> { - match op.layout.ty.kind { + match op.layout.ty.kind() { ty::Str => { let mplace = op.assert_mem_place(self.ecx); // strings are never immediate let len = mplace.len(self.ecx)?; @@ -775,7 +781,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // FIXME(wesleywiser) This logic could be extended further to arbitrary structs // or tuples made up of integer/floating point types or inhabited ZSTs with no // padding. - match tys.kind { + match tys.kind() { ty::Int(..) | ty::Uint(..) | ty::Float(..) => true, _ => false, } diff --git a/compiler/rustc_mir/src/interpret/visitor.rs b/compiler/rustc_mir/src/interpret/visitor.rs index 6c53df40a7c..097b9ae6ca1 100644 --- a/compiler/rustc_mir/src/interpret/visitor.rs +++ b/compiler/rustc_mir/src/interpret/visitor.rs @@ -203,7 +203,7 @@ macro_rules! make_value_visitor { trace!("walk_value: type: {}", v.layout().ty); // Special treatment for special types, where the (static) layout is not sufficient. - match v.layout().ty.kind { + match *v.layout().ty.kind() { // If it is a trait object, switch to the real type that was used to create it. ty::Dynamic(..) => { // immediate trait objects are not a thing diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs index 2e3b5084635..a356ae1709d 100644 --- a/compiler/rustc_mir/src/lib.rs +++ b/compiler/rustc_mir/src/lib.rs @@ -6,6 +6,8 @@ Rust MIR: a lowered representation of Rust. #![feature(nll)] #![feature(in_band_lifetimes)] +#![feature(array_windows)] +#![feature(bindings_after_at)] #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] @@ -13,19 +15,18 @@ Rust MIR: a lowered representation of Rust. #![feature(const_panic)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] -#![feature(drain_filter)] +#![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] -#![feature(iter_order_by)] #![feature(never_type)] #![feature(min_specialization)] #![feature(trusted_len)] #![feature(try_blocks)] -#![feature(associated_type_bounds)] #![feature(associated_type_defaults)] #![feature(stmt_expr_attributes)] #![feature(trait_alias)] #![feature(option_expect_none)] #![feature(or_patterns)] +#![feature(once_cell)] #![recursion_limit = "256"] #[macro_use] @@ -51,8 +52,8 @@ pub fn provide(providers: &mut Providers) { transform::provide(providers); monomorphize::partitioning::provide(providers); monomorphize::polymorphize::provide(providers); - providers.const_eval_validated = const_eval::const_eval_validated_provider; - providers.const_eval_raw = const_eval::const_eval_raw_provider; + providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; + providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; providers.const_caller_location = const_eval::const_caller_location; providers.destructure_const = |tcx, param_env_and_value| { let (param_env, value) = param_env_and_value.into_parts(); diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index d379f4ef428..7e12cc9176e 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -191,13 +191,13 @@ use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::mir::visit::Visitor as MirVisitor; use rustc_middle::mir::{self, Local, Location}; use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast}; -use rustc_middle::ty::print::obsolete::DefPathBasedNames; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_session::config::EntryFnType; use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP}; use smallvec::SmallVec; use std::iter; +use std::path::PathBuf; #[derive(PartialEq)] pub enum MonoItemCollectionMode { @@ -348,7 +348,7 @@ fn collect_items_rec<'tcx>( // We've been here already, no need to search again. return; } - debug!("BEGIN collect_items_rec({})", starting_point.node.to_string(tcx, true)); + debug!("BEGIN collect_items_rec({})", starting_point.node); let mut neighbors = Vec::new(); let recursion_depth_reset; @@ -365,8 +365,10 @@ fn collect_items_rec<'tcx>( recursion_depth_reset = None; - if let Ok(val) = tcx.const_eval_poly(def_id) { - collect_const_value(tcx, val, &mut neighbors); + if let Ok(alloc) = tcx.eval_static_initializer(def_id) { + for &((), id) in alloc.relocations().values() { + collect_miri(tcx, id, &mut neighbors); + } } } MonoItem::Fn(instance) => { @@ -397,7 +399,7 @@ fn collect_items_rec<'tcx>( recursion_depths.insert(def_id, depth); } - debug!("END collect_items_rec({})", starting_point.node.to_string(tcx, true)); + debug!("END collect_items_rec({})", starting_point.node); } fn record_accesses<'a, 'tcx: 'a>( @@ -419,6 +421,40 @@ fn record_accesses<'a, 'tcx: 'a>( inlining_map.lock_mut().record_accesses(caller, &accesses); } +/// Format instance name that is already known to be too long for rustc. +/// Show only the first and last 32 characters to avoid blasting +/// the user's terminal with thousands of lines of type-name. +/// +/// If the type name is longer than before+after, it will be written to a file. +fn shrunk_instance_name( + tcx: TyCtxt<'tcx>, + instance: &Instance<'tcx>, + before: usize, + after: usize, +) -> (String, Option<PathBuf>) { + let s = instance.to_string(); + + // Only use the shrunk version if it's really shorter. + // This also avoids the case where before and after slices overlap. + if s.chars().nth(before + after + 1).is_some() { + // An iterator of all byte positions including the end of the string. + let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len())); + + let shrunk = format!( + "{before}...{after}", + before = &s[..positions().nth(before).unwrap_or(s.len())], + after = &s[positions().rev().nth(after).unwrap_or(0)..], + ); + + let path = tcx.output_filenames(LOCAL_CRATE).temp_path_ext("long-type.txt", None); + let written_to_path = std::fs::write(&path, s).ok().map(|_| path); + + (shrunk, written_to_path) + } else { + (s, None) + } +} + fn check_recursion_limit<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, @@ -441,12 +477,16 @@ fn check_recursion_limit<'tcx>( // more than the recursion limit is assumed to be causing an // infinite expansion. if !tcx.sess.recursion_limit().value_within_limit(adjusted_recursion_depth) { - let error = format!("reached the recursion limit while instantiating `{}`", instance); + let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32); + let error = format!("reached the recursion limit while instantiating `{}`", shrunk); let mut err = tcx.sess.struct_span_fatal(span, &error); err.span_note( tcx.def_span(def_id), &format!("`{}` defined here", tcx.def_path_str(def_id)), ); + if let Some(path) = written_to_path { + err.note(&format!("the full type name has been written to '{}'", path.display())); + } err.emit(); FatalError.raise(); } @@ -475,29 +515,13 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { // // Bail out in these cases to avoid that bad user experience. if !tcx.sess.type_length_limit().value_within_limit(type_length) { - // The instance name is already known to be too long for rustc. - // Show only the first and last 32 characters to avoid blasting - // the user's terminal with thousands of lines of type-name. - let shrink = |s: String, before: usize, after: usize| { - // An iterator of all byte positions including the end of the string. - let positions = || s.char_indices().map(|(i, _)| i).chain(iter::once(s.len())); - - let shrunk = format!( - "{before}...{after}", - before = &s[..positions().nth(before).unwrap_or(s.len())], - after = &s[positions().rev().nth(after).unwrap_or(0)..], - ); - - // Only use the shrunk version if it's really shorter. - // This also avoids the case where before and after slices overlap. - if shrunk.len() < s.len() { shrunk } else { s } - }; - let msg = format!( - "reached the type-length limit while instantiating `{}`", - shrink(instance.to_string(), 32, 32) - ); + let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32); + let msg = format!("reached the type-length limit while instantiating `{}`", shrunk); let mut diag = tcx.sess.struct_span_fatal(tcx.def_span(instance.def_id()), &msg); - diag.note(&format!( + if let Some(path) = written_to_path { + diag.note(&format!("the full type name has been written to '{}'", path.display())); + } + diag.help(&format!( "consider adding a `#![type_length_limit=\"{}\"]` attribute to your crate", type_length )); @@ -576,7 +600,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { ) => { let source_ty = operand.ty(self.body, self.tcx); let source_ty = self.monomorphize(source_ty); - match source_ty.kind { + match *source_ty.kind() { ty::Closure(def_id, substs) => { let instance = Instance::resolve_closure( self.tcx, @@ -717,7 +741,7 @@ fn visit_fn_use<'tcx>( source: Span, output: &mut Vec<Spanned<MonoItem<'tcx>>>, ) { - if let ty::FnDef(def_id, substs) = ty.kind { + if let ty::FnDef(def_id, substs) = *ty.kind() { let instance = if is_direct_call { ty::Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() } else { @@ -854,7 +878,7 @@ fn find_vtable_types_for_unsizing<'tcx>( return false; } let tail = tcx.struct_tail_erasing_lifetimes(ty, param_env); - match tail.kind { + match tail.kind() { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, _ => bug!("unexpected unsized tail: {:?}", tail), @@ -867,7 +891,7 @@ fn find_vtable_types_for_unsizing<'tcx>( } }; - match (&source_ty.kind, &target_ty.kind) { + match (&source_ty.kind(), &target_ty.kind()) { (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { ptr_vtable(a, b) @@ -923,7 +947,7 @@ fn create_mono_items_for_vtable_methods<'tcx>( ) { assert!(!trait_ty.has_escaping_bound_vars() && !impl_ty.has_escaping_bound_vars()); - if let ty::Dynamic(ref trait_ty, ..) = trait_ty.kind { + if let ty::Dynamic(ref trait_ty, ..) = trait_ty.kind() { if let Some(principal) = trait_ty.principal() { let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); assert!(!poly_trait_ref.has_escaping_bound_vars()); @@ -992,7 +1016,7 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { let def_id = self.tcx.hir().local_def_id(item.hir_id); debug!( "RootCollector: ADT drop-glue for {}", - def_id_to_string(self.tcx, def_id) + self.tcx.def_path_str(def_id.to_def_id()) ); let ty = Instance::new(def_id.to_def_id(), InternalSubsts::empty()) @@ -1004,14 +1028,14 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { hir::ItemKind::GlobalAsm(..) => { debug!( "RootCollector: ItemKind::GlobalAsm({})", - def_id_to_string(self.tcx, self.tcx.hir().local_def_id(item.hir_id)) + self.tcx.def_path_str(self.tcx.hir().local_def_id(item.hir_id).to_def_id()) ); self.output.push(dummy_spanned(MonoItem::GlobalAsm(item.hir_id))); } hir::ItemKind::Static(..) => { - let def_id = self.tcx.hir().local_def_id(item.hir_id); - debug!("RootCollector: ItemKind::Static({})", def_id_to_string(self.tcx, def_id)); - self.output.push(dummy_spanned(MonoItem::Static(def_id.to_def_id()))); + let def_id = self.tcx.hir().local_def_id(item.hir_id).to_def_id(); + debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id)); + self.output.push(dummy_spanned(MonoItem::Static(def_id))); } hir::ItemKind::Const(..) => { // const items only generate mono items if they are @@ -1134,7 +1158,7 @@ fn create_mono_items_for_default_impls<'tcx>( debug!( "create_mono_items_for_default_impls(item={})", - def_id_to_string(tcx, impl_def_id) + tcx.def_path_str(impl_def_id.to_def_id()) ); if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) { @@ -1218,13 +1242,6 @@ fn collect_neighbours<'tcx>( MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body); } -fn def_id_to_string(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String { - let mut output = String::new(); - let printer = DefPathBasedNames::new(tcx, false, false); - printer.push_def_path(def_id.to_def_id(), &mut output); - output -} - fn collect_const_value<'tcx>( tcx: TyCtxt<'tcx>, value: ConstValue<'tcx>, diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs index b48bae83787..827d037f319 100644 --- a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs +++ b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs @@ -11,6 +11,7 @@ use rustc_middle::ty::print::characteristic_def_id_of_type; use rustc_middle::ty::{self, DefIdTree, InstanceDef, TyCtxt}; use rustc_span::symbol::Symbol; +use super::PartitioningCx; use crate::monomorphize::collector::InliningMap; use crate::monomorphize::partitioning::merging; use crate::monomorphize::partitioning::{ @@ -22,35 +23,36 @@ pub struct DefaultPartitioning; impl<'tcx> Partitioner<'tcx> for DefaultPartitioning { fn place_root_mono_items( &mut self, - tcx: TyCtxt<'tcx>, + cx: &PartitioningCx<'_, 'tcx>, mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>, ) -> PreInliningPartitioning<'tcx> { let mut roots = FxHashSet::default(); let mut codegen_units = FxHashMap::default(); - let is_incremental_build = tcx.sess.opts.incremental.is_some(); + let is_incremental_build = cx.tcx.sess.opts.incremental.is_some(); let mut internalization_candidates = FxHashSet::default(); // Determine if monomorphizations instantiated in this crate will be made // available to downstream crates. This depends on whether we are in // share-generics mode and whether the current crate can even have // downstream crates. - let export_generics = tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics(); + let export_generics = + cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics(); - let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx); let cgu_name_cache = &mut FxHashMap::default(); for mono_item in mono_items { - match mono_item.instantiation_mode(tcx) { + match mono_item.instantiation_mode(cx.tcx) { InstantiationMode::GloballyShared { .. } => {} InstantiationMode::LocalCopy => continue, } - let characteristic_def_id = characteristic_def_id_of_mono_item(tcx, mono_item); + let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item); let is_volatile = is_incremental_build && mono_item.is_generic_fn(); let codegen_unit_name = match characteristic_def_id { Some(def_id) => compute_codegen_unit_name( - tcx, + cx.tcx, cgu_name_builder, def_id, is_volatile, @@ -65,7 +67,7 @@ impl<'tcx> Partitioner<'tcx> for DefaultPartitioning { let mut can_be_internalized = true; let (linkage, visibility) = mono_item_linkage_and_visibility( - tcx, + cx.tcx, &mono_item, &mut can_be_internalized, export_generics, @@ -97,17 +99,16 @@ impl<'tcx> Partitioner<'tcx> for DefaultPartitioning { fn merge_codegen_units( &mut self, - tcx: TyCtxt<'tcx>, + cx: &PartitioningCx<'_, 'tcx>, initial_partitioning: &mut PreInliningPartitioning<'tcx>, - target_cgu_count: usize, ) { - merging::merge_codegen_units(tcx, initial_partitioning, target_cgu_count); + merging::merge_codegen_units(cx, initial_partitioning); } fn place_inlined_mono_items( &mut self, + cx: &PartitioningCx<'_, 'tcx>, initial_partitioning: PreInliningPartitioning<'tcx>, - inlining_map: &InliningMap<'tcx>, ) -> PostInliningPartitioning<'tcx> { let mut new_partitioning = Vec::new(); let mut mono_item_placements = FxHashMap::default(); @@ -124,7 +125,7 @@ impl<'tcx> Partitioner<'tcx> for DefaultPartitioning { // Collect all items that need to be available in this codegen unit. let mut reachable = FxHashSet::default(); for root in old_codegen_unit.items().keys() { - follow_inlining(*root, inlining_map, &mut reachable); + follow_inlining(*root, cx.inlining_map, &mut reachable); } let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name()); @@ -198,9 +199,8 @@ impl<'tcx> Partitioner<'tcx> for DefaultPartitioning { fn internalize_symbols( &mut self, - _tcx: TyCtxt<'tcx>, + cx: &PartitioningCx<'_, 'tcx>, partitioning: &mut PostInliningPartitioning<'tcx>, - inlining_map: &InliningMap<'tcx>, ) { if partitioning.codegen_units.len() == 1 { // Fast path for when there is only one codegen unit. In this case we @@ -218,7 +218,7 @@ impl<'tcx> Partitioner<'tcx> for DefaultPartitioning { // Build a map from every monomorphization to all the monomorphizations that // reference it. let mut accessor_map: FxHashMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>> = Default::default(); - inlining_map.iter_accesses(|accessor, accessees| { + cx.inlining_map.iter_accesses(|accessor, accessees| { for accessee in accessees { accessor_map.entry(*accessee).or_default().push(accessor); } diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs b/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs index 1787e6df1b9..5107e697263 100644 --- a/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs +++ b/compiler/rustc_mir/src/monomorphize/partitioning/merging.rs @@ -3,17 +3,16 @@ use std::cmp; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder}; -use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{Symbol, SymbolStr}; +use super::PartitioningCx; use crate::monomorphize::partitioning::PreInliningPartitioning; pub fn merge_codegen_units<'tcx>( - tcx: TyCtxt<'tcx>, + cx: &PartitioningCx<'_, 'tcx>, initial_partitioning: &mut PreInliningPartitioning<'tcx>, - target_cgu_count: usize, ) { - assert!(target_cgu_count >= 1); + assert!(cx.target_cgu_count >= 1); let codegen_units = &mut initial_partitioning.codegen_units; // Note that at this point in time the `codegen_units` here may not be in a @@ -32,7 +31,7 @@ pub fn merge_codegen_units<'tcx>( codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name().as_str()])).collect(); // Merge the two smallest codegen units until the target size is reached. - while codegen_units.len() > target_cgu_count { + while codegen_units.len() > cx.target_cgu_count { // Sort small cgus to the back codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); let mut smallest = codegen_units.pop().unwrap(); @@ -56,9 +55,9 @@ pub fn merge_codegen_units<'tcx>( ); } - let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx); - if tcx.sess.opts.incremental.is_some() { + if cx.tcx.sess.opts.incremental.is_some() { // If we are doing incremental compilation, we want CGU names to // reflect the path of the source level module they correspond to. // For CGUs that contain the code of multiple modules because of the @@ -74,7 +73,9 @@ pub fn merge_codegen_units<'tcx>( // Sort the names, so things are deterministic and easy to // predict. - cgu_contents.sort(); + + // We are sorting primitive &strs here so we can use unstable sort + cgu_contents.sort_unstable(); (current_cgu_name, cgu_contents.join("--")) }) @@ -82,7 +83,7 @@ pub fn merge_codegen_units<'tcx>( for cgu in codegen_units.iter_mut() { if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) { - if tcx.sess.opts.debugging_opts.human_readable_cgu_names { + if cx.tcx.sess.opts.debugging_opts.human_readable_cgu_names { cgu.set_name(Symbol::intern(&new_cgu_name)); } else { // If we don't require CGU names to be human-readable, we diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs index 9dfbd65e1b1..db6d3b2d912 100644 --- a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs +++ b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs @@ -100,6 +100,7 @@ use rustc_data_structures::sync; use rustc_hir::def_id::{CrateNum, DefIdSet, LOCAL_CRATE}; use rustc_middle::mir::mono::MonoItem; use rustc_middle::mir::mono::{CodegenUnit, Linkage}; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; @@ -107,31 +108,35 @@ use rustc_span::symbol::Symbol; use crate::monomorphize::collector::InliningMap; use crate::monomorphize::collector::{self, MonoItemCollectionMode}; +pub struct PartitioningCx<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + target_cgu_count: usize, + inlining_map: &'a InliningMap<'tcx>, +} + trait Partitioner<'tcx> { fn place_root_mono_items( &mut self, - tcx: TyCtxt<'tcx>, + cx: &PartitioningCx<'_, 'tcx>, mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>, ) -> PreInliningPartitioning<'tcx>; fn merge_codegen_units( &mut self, - tcx: TyCtxt<'tcx>, + cx: &PartitioningCx<'_, 'tcx>, initial_partitioning: &mut PreInliningPartitioning<'tcx>, - target_cgu_count: usize, ); fn place_inlined_mono_items( &mut self, + cx: &PartitioningCx<'_, 'tcx>, initial_partitioning: PreInliningPartitioning<'tcx>, - inlining_map: &InliningMap<'tcx>, ) -> PostInliningPartitioning<'tcx>; fn internalize_symbols( &mut self, - tcx: TyCtxt<'tcx>, + cx: &PartitioningCx<'_, 'tcx>, partitioning: &mut PostInliningPartitioning<'tcx>, - inlining_map: &InliningMap<'tcx>, ); } @@ -156,12 +161,13 @@ pub fn partition<'tcx>( let _prof_timer = tcx.prof.generic_activity("cgu_partitioning"); let mut partitioner = get_partitioner(tcx); + let cx = &PartitioningCx { tcx, target_cgu_count: max_cgu_count, inlining_map }; // In the first step, we place all regular monomorphizations into their // respective 'home' codegen unit. Regular monomorphizations are all // functions and statics defined in the local crate. let mut initial_partitioning = { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots"); - partitioner.place_root_mono_items(tcx, mono_items) + partitioner.place_root_mono_items(cx, mono_items) }; initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx)); @@ -171,7 +177,7 @@ pub fn partition<'tcx>( // Merge until we have at most `max_cgu_count` codegen units. { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus"); - partitioner.merge_codegen_units(tcx, &mut initial_partitioning, max_cgu_count); + partitioner.merge_codegen_units(cx, &mut initial_partitioning); debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter()); } @@ -181,7 +187,7 @@ pub fn partition<'tcx>( // local functions the definition of which is marked with `#[inline]`. let mut post_inlining = { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items"); - partitioner.place_inlined_mono_items(initial_partitioning, inlining_map) + partitioner.place_inlined_mono_items(cx, initial_partitioning) }; post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx)); @@ -190,9 +196,9 @@ pub fn partition<'tcx>( // Next we try to make as many symbols "internal" as possible, so LLVM has // more freedom to optimize. - if tcx.sess.opts.cg.link_dead_code != Some(true) { + if !tcx.sess.link_dead_code() { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols"); - partitioner.internalize_symbols(tcx, &mut post_inlining, inlining_map); + partitioner.internalize_symbols(cx, &mut post_inlining); } // Finally, sort by codegen unit name, so that we get deterministic results. @@ -246,7 +252,7 @@ where debug!( " - {} [{:?}] [{}] estimated size {}", - mono_item.to_string(tcx, true), + mono_item, linkage, symbol_hash, mono_item.size_estimate(tcx) @@ -271,14 +277,8 @@ where symbols.sort_by_key(|sym| sym.1); - for pair in symbols.windows(2) { - let sym1 = &pair[0].1; - let sym2 = &pair[1].1; - + for &[(mono_item1, ref sym1), (mono_item2, ref sym2)] in symbols.array_windows() { if sym1 == sym2 { - let mono_item1 = pair[0].0; - let mono_item2 = pair[1].0; - let span1 = mono_item1.local_span(tcx); let span2 = mono_item2.local_span(tcx); @@ -327,7 +327,7 @@ fn collect_and_partition_mono_items<'tcx>( } } None => { - if tcx.sess.opts.cg.link_dead_code == Some(true) { + if tcx.sess.link_dead_code() { MonoItemCollectionMode::Eager } else { MonoItemCollectionMode::Lazy @@ -374,14 +374,14 @@ fn collect_and_partition_mono_items<'tcx>( let mut item_keys: Vec<_> = items .iter() .map(|i| { - let mut output = i.to_string(tcx, false); + let mut output = with_no_trimmed_paths(|| i.to_string()); output.push_str(" @@"); let mut empty = Vec::new(); let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); cgus.sort_by_key(|(name, _)| *name); cgus.dedup(); for &(ref cgu_name, (linkage, _)) in cgus.iter() { - output.push_str(" "); + output.push(' '); output.push_str(&cgu_name.as_str()); let linkage_abbrev = match linkage { @@ -398,9 +398,9 @@ fn collect_and_partition_mono_items<'tcx>( Linkage::Common => "Common", }; - output.push_str("["); + output.push('['); output.push_str(linkage_abbrev); - output.push_str("]"); + output.push(']'); } output }) diff --git a/compiler/rustc_mir/src/monomorphize/polymorphize.rs b/compiler/rustc_mir/src/monomorphize/polymorphize.rs index 69f3288ee39..3f6f117acdc 100644 --- a/compiler/rustc_mir/src/monomorphize/polymorphize.rs +++ b/compiler/rustc_mir/src/monomorphize/polymorphize.rs @@ -288,7 +288,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { return false; } - match ty.kind { + match *ty.kind() { ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => { debug!("visit_ty: def_id={:?}", def_id); // Avoid cycle errors with generators. @@ -337,7 +337,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> { return false; } - match ty.kind { + match ty.kind() { ty::Param(param) => !self.unused_parameters.contains(param.index).unwrap_or(false), _ => ty.super_visit_with(self), } diff --git a/compiler/rustc_mir/src/shim.rs b/compiler/rustc_mir/src/shim.rs index 479b6c2a6ca..7e4d189f0b7 100644 --- a/compiler/rustc_mir/src/shim.rs +++ b/compiler/rustc_mir/src/shim.rs @@ -33,7 +33,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' let mut result = match instance { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), ty::InstanceDef::VtableShim(def_id) => { - build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id), None) + build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id)) } ty::InstanceDef::FnPtrShim(def_id, ty) => { let trait_ = tcx.trait_of_item(def_id).unwrap(); @@ -42,16 +42,8 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' Some(ty::ClosureKind::FnMut | ty::ClosureKind::Fn) => Adjustment::Deref, None => bug!("fn pointer {:?} is not an fn", ty), }; - // HACK: we need the "real" argument types for the MIR, - // but because our substs are (Self, Args), where Args - // is a tuple, we must include the *concrete* argument - // types in the MIR. They will be substituted again with - // the param-substs, but because they are concrete, this - // will not do any harm. - let sig = tcx.erase_late_bound_regions(&ty.fn_sig(tcx)); - let arg_tys = sig.inputs(); - - build_call_shim(tcx, instance, Some(adjustment), CallKind::Indirect(ty), Some(arg_tys)) + + build_call_shim(tcx, instance, Some(adjustment), CallKind::Indirect(ty)) } // We are generating a call back to our def-id, which the // codegen backend knows to turn to an actual call, be it @@ -59,7 +51,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' // indirect calls must be codegen'd differently than direct ones // (such as `#[track_caller]`). ty::InstanceDef::ReifyShim(def_id) => { - build_call_shim(tcx, instance, None, CallKind::Direct(def_id), None) + build_call_shim(tcx, instance, None, CallKind::Direct(def_id)) } ty::InstanceDef::ClosureOnceShim { call_once: _ } => { let fn_mut = tcx.require_lang_item(LangItem::FnMut, None); @@ -70,13 +62,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' .unwrap() .def_id; - build_call_shim( - tcx, - instance, - Some(Adjustment::RefMut), - CallKind::Direct(call_mut), - None, - ) + build_call_shim(tcx, instance, Some(Adjustment::RefMut), CallKind::Direct(call_mut)) } ty::InstanceDef::DropGlue(def_id, ty) => build_drop_shim(tcx, def_id, ty), ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), @@ -149,7 +135,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); // Check if this is a generator, if so, return the drop glue for it - if let Some(&ty::TyS { kind: ty::Generator(gen_def_id, substs, _), .. }) = ty { + if let Some(&ty::Generator(gen_def_id, substs, _)) = ty.map(|ty| ty.kind()) { let body = &**tcx.optimized_mir(gen_def_id).generator_drop.as_ref().unwrap(); return body.subst(tcx, substs); } @@ -312,7 +298,7 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) - let dest = Place::return_place(); let src = tcx.mk_place_deref(Place::from(Local::new(1 + 0))); - match self_ty.kind { + match self_ty.kind() { _ if is_copy => builder.copy_shim(), ty::Array(ty, len) => { let len = len.eval_usize(tcx, param_env); @@ -641,29 +627,45 @@ impl CloneShimBuilder<'tcx> { } } -/// Builds a "call" shim for `instance`. The shim calls the -/// function specified by `call_kind`, first adjusting its first -/// argument according to `rcvr_adjustment`. -/// -/// If `untuple_args` is a vec of types, the second argument of the -/// function will be untupled as these types. +/// Builds a "call" shim for `instance`. The shim calls the function specified by `call_kind`, +/// first adjusting its first argument according to `rcvr_adjustment`. fn build_call_shim<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>, rcvr_adjustment: Option<Adjustment>, call_kind: CallKind<'tcx>, - untuple_args: Option<&[Ty<'tcx>]>, ) -> Body<'tcx> { debug!( - "build_call_shim(instance={:?}, rcvr_adjustment={:?}, \ - call_kind={:?}, untuple_args={:?})", - instance, rcvr_adjustment, call_kind, untuple_args + "build_call_shim(instance={:?}, rcvr_adjustment={:?}, call_kind={:?})", + instance, rcvr_adjustment, call_kind ); + // `FnPtrShim` contains the fn pointer type that a call shim is being built for - this is used + // to substitute into the signature of the shim. It is not necessary for users of this + // MIR body to perform further substitutions (see `InstanceDef::has_polymorphic_mir_body`). + let (sig_substs, untuple_args) = if let ty::InstanceDef::FnPtrShim(_, ty) = instance { + let sig = tcx.erase_late_bound_regions(&ty.fn_sig(tcx)); + + let untuple_args = sig.inputs(); + + // Create substitutions for the `Self` and `Args` generic parameters of the shim body. + let arg_tup = tcx.mk_tup(untuple_args.iter()); + let sig_substs = tcx.mk_substs_trait(ty, &[ty::subst::GenericArg::from(arg_tup)]); + + (Some(sig_substs), Some(untuple_args)) + } else { + (None, None) + }; + let def_id = instance.def_id(); let sig = tcx.fn_sig(def_id); let mut sig = tcx.erase_late_bound_regions(&sig); + assert_eq!(sig_substs.is_some(), !instance.has_polymorphic_mir_body()); + if let Some(sig_substs) = sig_substs { + sig = sig.subst(tcx, sig_substs); + } + if let CallKind::Indirect(fnty) = call_kind { // `sig` determines our local decls, and thus the callee type in the `Call` terminator. This // can only be an `FnDef` or `FnPtr`, but currently will be `Self` since the types come from @@ -853,7 +855,7 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { let sig = tcx.fn_sig(ctor_id).no_bound_vars().expect("LBR in ADT constructor signature"); let sig = tcx.normalize_erasing_regions(param_env, sig); - let (adt_def, substs) = match sig.output().kind { + let (adt_def, substs) = match sig.output().kind() { ty::Adt(adt_def, substs) => (adt_def, substs), _ => bug!("unexpected type for ADT ctor {:?}", sig.output()), }; diff --git a/compiler/rustc_mir/src/transform/add_retag.rs b/compiler/rustc_mir/src/transform/add_retag.rs index 324289166b9..0c596ba7154 100644 --- a/compiler/rustc_mir/src/transform/add_retag.rs +++ b/compiler/rustc_mir/src/transform/add_retag.rs @@ -35,7 +35,7 @@ fn is_stable(place: PlaceRef<'_>) -> bool { /// Determine whether this type may be a reference (or box), and thus needs retagging. fn may_be_reference(ty: Ty<'tcx>) -> bool { - match ty.kind { + match ty.kind() { // Primitive types that are not references ty::Bool | ty::Char diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs new file mode 100644 index 00000000000..70c1aed0957 --- /dev/null +++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs @@ -0,0 +1,114 @@ +use rustc_errors::DiagnosticBuilder; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint::builtin::CONST_ITEM_MUTATION; +use rustc_span::def_id::DefId; + +use crate::transform::{MirPass, MirSource}; + +pub struct CheckConstItemMutation; + +impl<'tcx> MirPass<'tcx> for CheckConstItemMutation { + fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { + let mut checker = ConstMutationChecker { body, tcx, target_local: None }; + checker.visit_body(&body); + } +} + +struct ConstMutationChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + target_local: Option<Local>, +} + +impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> { + fn is_const_item(&self, local: Local) -> Option<DefId> { + if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info { + Some(def_id) + } else { + None + } + } + fn lint_const_item_usage( + &self, + const_item: DefId, + location: Location, + decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>, + ) { + let source_info = self.body.source_info(location); + let lint_root = self.body.source_scopes[source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + + self.tcx.struct_span_lint_hir(CONST_ITEM_MUTATION, lint_root, source_info.span, |lint| { + decorate(lint) + .span_note(self.tcx.def_span(const_item), "`const` item defined here") + .emit() + }); + } +} + +impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> { + fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) { + if let StatementKind::Assign(box (lhs, _)) = &stmt.kind { + // Check for assignment to fields of a constant + // Assigning directly to a constant (e.g. `FOO = true;`) is a hard error, + // so emitting a lint would be redundant. + if !lhs.projection.is_empty() { + if let Some(def_id) = self.is_const_item(lhs.local) { + self.lint_const_item_usage(def_id, loc, |lint| { + let mut lint = lint.build("attempting to modify a `const` item"); + lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified"); + lint + }) + } + } + // We are looking for MIR of the form: + // + // ``` + // _1 = const FOO; + // _2 = &mut _1; + // method_call(_2, ..) + // ``` + // + // Record our current LHS, so that we can detect this + // pattern in `visit_rvalue` + self.target_local = lhs.as_local(); + } + self.super_statement(stmt, loc); + self.target_local = None; + } + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, loc: Location) { + if let Rvalue::Ref(_, BorrowKind::Mut { .. }, place) = rvalue { + let local = place.local; + if let Some(def_id) = self.is_const_item(local) { + // If this Rvalue is being used as the right-hand side of a + // `StatementKind::Assign`, see if it ends up getting used as + // the `self` parameter of a method call (as the terminator of our current + // BasicBlock). If so, we emit a more specific lint. + let method_did = self.target_local.and_then(|target_local| { + crate::util::find_self_call(self.tcx, &self.body, target_local, loc.block) + }); + let lint_loc = + if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc }; + self.lint_const_item_usage(def_id, lint_loc, |lint| { + let mut lint = lint.build("taking a mutable reference to a `const` item"); + lint + .note("each usage of a `const` item creates a new temporary") + .note("the mutable reference will refer to this temporary, not the original `const` item"); + + if let Some((method_did, _substs)) = method_did { + lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method"); + } + + lint + }); + } + } + self.super_rvalue(rvalue, loc); + } +} diff --git a/compiler/rustc_mir/src/transform/check_consts/mod.rs b/compiler/rustc_mir/src/transform/check_consts/mod.rs index 81c1b0b5bd4..8d4efd8ae80 100644 --- a/compiler/rustc_mir/src/transform/check_consts/mod.rs +++ b/compiler/rustc_mir/src/transform/check_consts/mod.rs @@ -4,10 +4,12 @@ //! has interior mutability or needs to be dropped, as well as the visitor that emits errors when //! it finds operations that are invalid in a certain context. +use rustc_attr as attr; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::mir; use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::Symbol; pub use self::qualifs::Qualif; @@ -49,9 +51,55 @@ impl ConstCx<'mir, 'tcx> { pub fn const_kind(&self) -> hir::ConstContext { self.const_kind.expect("`const_kind` must not be called on a non-const fn") } + + pub fn is_const_stable_const_fn(&self) -> bool { + self.const_kind == Some(hir::ConstContext::ConstFn) + && self.tcx.features().staged_api + && is_const_stable_const_fn(self.tcx, self.def_id.to_def_id()) + } } /// 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().begin_panic_fn() } + +pub fn allow_internal_unstable(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool { + let attrs = tcx.get_attrs(def_id); + attr::allow_internal_unstable(&tcx.sess, attrs) + .map_or(false, |mut features| features.any(|name| name == feature_gate)) +} + +// Returns `true` if the given `const fn` is "const-stable". +// +// Panics if the given `DefId` does not refer to a `const fn`. +// +// Const-stability is only relevant for `const fn` within a `staged_api` crate. Only "const-stable" +// functions can be called in a const-context by users of the stable compiler. "const-stable" +// functions are subject to more stringent restrictions than "const-unstable" functions: They +// cannot use unstable features and can only call other "const-stable" functions. +pub fn is_const_stable_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { + use attr::{ConstStability, Stability, StabilityLevel}; + + // Const-stability is only relevant for `const fn`. + assert!(tcx.is_const_fn_raw(def_id)); + + // Functions with `#[rustc_const_unstable]` are const-unstable. + match tcx.lookup_const_stability(def_id) { + Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. }) => return false, + Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => return true, + None => {} + } + + // Functions with `#[unstable]` are const-unstable. + // + // FIXME(ecstaticmorse): We should keep const-stability attributes wholly separate from normal stability + // attributes. `#[unstable]` should be irrelevant. + if let Some(Stability { level: StabilityLevel::Unstable { .. }, .. }) = + tcx.lookup_stability(def_id) + { + return false; + } + + true +} diff --git a/compiler/rustc_mir/src/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs index ea025f208e4..496e620dd9d 100644 --- a/compiler/rustc_mir/src/transform/check_consts/ops.rs +++ b/compiler/rustc_mir/src/transform/check_consts/ops.rs @@ -1,6 +1,6 @@ //! Concrete error types for all operations which may be invalid in a certain const context. -use rustc_errors::struct_span_err; +use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_session::config::nightly_options; @@ -10,39 +10,63 @@ use rustc_span::{Span, Symbol}; use super::ConstCx; -/// Emits an error if `op` is not allowed in the given const context. -pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) { +/// Emits an error and returns `true` if `op` is not allowed in the given const context. +pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) -> bool { debug!("illegal_op: op={:?}", op); - if op.is_allowed_in_item(ccx) { - return; - } + let gate = match op.status_in_item(ccx) { + Status::Allowed => return false, + + Status::Unstable(gate) if ccx.tcx.features().enabled(gate) => { + let unstable_in_stable = ccx.is_const_stable_const_fn() + && !super::allow_internal_unstable(ccx.tcx, ccx.def_id.to_def_id(), gate); + + if unstable_in_stable { + ccx.tcx.sess + .struct_span_err( + span, + &format!("const-stable function cannot use `#[feature({})]`", gate.as_str()), + ) + .span_suggestion( + ccx.body.span, + "if it is not part of the public API, make this function unstably const", + concat!(r#"#[rustc_const_unstable(feature = "...", issue = "...")]"#, '\n').to_owned(), + Applicability::HasPlaceholders, + ) + .note("otherwise `#[allow_internal_unstable]` can be used to bypass stability checks") + .emit(); + } + + return unstable_in_stable; + } + + Status::Unstable(gate) => Some(gate), + Status::Forbidden => None, + }; if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { - ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate()); - return; + ccx.tcx.sess.miri_unleashed_feature(span, gate); + return false; } op.emit_error(ccx, span); + true +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Status { + Allowed, + Unstable(Symbol), + Forbidden, } /// An operation that is not *always* allowed in a const context. pub trait NonConstOp: std::fmt::Debug { - /// Returns the `Symbol` corresponding to the feature gate that would enable this operation, - /// or `None` if such a feature gate does not exist. - fn feature_gate() -> Option<Symbol> { - None - } + const STOPS_CONST_CHECKING: bool = false; - /// Returns `true` if this operation is allowed in the given item. - /// - /// This check should assume that we are not in a non-const `fn`, where all operations are - /// legal. - /// - /// By default, it returns `true` if and only if this operation has a corresponding feature - /// gate and that gate is enabled. - fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { - Self::feature_gate().map_or(false, |gate| ccx.tcx.features().enabled(gate)) + /// Returns an enum indicating whether this operation is allowed within the given item. + fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { + Status::Forbidden } fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -53,9 +77,13 @@ pub trait NonConstOp: std::fmt::Debug { "{} contains unimplemented expression type", ccx.const_kind() ); - if let Some(feat) = Self::feature_gate() { - err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feat)); + + if let Status::Unstable(gate) = self.status_in_item(ccx) { + if !ccx.tcx.features().enabled(gate) && nightly_options::is_nightly_build() { + err.help(&format!("add `#![feature({})]` to the crate attributes to enable", gate)); + } } + if ccx.tcx.sess.teach(&err.get_code().unwrap()) { err.note( "A function call isn't allowed in the const's initialization expression \ @@ -70,6 +98,34 @@ pub trait NonConstOp: std::fmt::Debug { } } +#[derive(Debug)] +pub struct Abort; +impl NonConstOp for Abort { + const STOPS_CONST_CHECKING: bool = true; + + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + mcf_status_in_item(ccx) + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + mcf_emit_error(ccx, span, "abort is not stable in const fn") + } +} + +#[derive(Debug)] +pub struct NonPrimitiveOp; +impl NonConstOp for NonPrimitiveOp { + const STOPS_CONST_CHECKING: bool = true; + + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + mcf_status_in_item(ccx) + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + mcf_emit_error(ccx, span, "only int, `bool` and `char` operations are stable in const fn") + } +} + /// A function call where the callee is a pointer. #[derive(Debug)] pub struct FnCallIndirect; @@ -102,7 +158,8 @@ impl NonConstOp for FnCallNonConst { /// /// Contains the name of the feature that would allow the use of this function. #[derive(Debug)] -pub struct FnCallUnstable(pub DefId, pub Symbol); +pub struct FnCallUnstable(pub DefId, pub Option<Symbol>); + impl NonConstOp for FnCallUnstable { fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { let FnCallUnstable(def_id, feature) = *self; @@ -111,14 +168,52 @@ impl NonConstOp for FnCallUnstable { span, &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)), ); - if nightly_options::is_nightly_build() { - err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature)); + + if ccx.is_const_stable_const_fn() { + err.help("Const-stable functions can only call other const-stable functions"); + } else if nightly_options::is_nightly_build() { + if let Some(feature) = feature { + err.help(&format!( + "add `#![feature({})]` to the crate attributes to enable", + feature + )); + } } err.emit(); } } #[derive(Debug)] +pub struct FnPtrCast; +impl NonConstOp for FnPtrCast { + const STOPS_CONST_CHECKING: bool = true; + + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + mcf_status_in_item(ccx) + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + mcf_emit_error(ccx, span, "function pointer casts are not allowed in const fn"); + } +} + +#[derive(Debug)] +pub struct Generator; +impl NonConstOp for Generator { + const STOPS_CONST_CHECKING: bool = true; + + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + // FIXME: This means generator-only MIR is only forbidden in const fn. This is for + // compatibility with the old code. Such MIR should be forbidden everywhere. + mcf_status_in_item(ccx) + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + mcf_emit_error(ccx, span, "const fn generators are unstable"); + } +} + +#[derive(Debug)] pub struct HeapAllocation; impl NonConstOp for HeapAllocation { fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -147,7 +242,9 @@ pub struct InlineAsm; impl NonConstOp for InlineAsm {} #[derive(Debug)] -pub struct LiveDrop(pub Option<Span>); +pub struct LiveDrop { + pub dropped_at: Option<Span>, +} impl NonConstOp for LiveDrop { fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { let mut diagnostic = struct_span_err!( @@ -157,7 +254,7 @@ impl NonConstOp for LiveDrop { "destructors cannot be evaluated at compile-time" ); diagnostic.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind())); - if let Some(span) = self.0 { + if let Some(span) = self.dropped_at { diagnostic.span_label(span, "value is dropped here"); } diagnostic.emit(); @@ -182,14 +279,13 @@ impl NonConstOp for CellBorrow { #[derive(Debug)] pub struct MutBorrow; impl NonConstOp for MutBorrow { - fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { - // Forbid everywhere except in const fn - ccx.const_kind() == hir::ConstContext::ConstFn - && ccx.tcx.features().enabled(Self::feature_gate().unwrap()) - } - - fn feature_gate() -> Option<Symbol> { - Some(sym::const_mut_refs) + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + // Forbid everywhere except in const fn with a feature gate + if ccx.const_kind() == hir::ConstContext::ConstFn { + Status::Unstable(sym::const_mut_refs) + } else { + Status::Forbidden + } } fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -201,15 +297,16 @@ impl NonConstOp for MutBorrow { &format!("mutable references are not allowed in {}s", ccx.const_kind()), ) } else { - struct_span_err!( + let mut err = struct_span_err!( ccx.tcx.sess, span, E0764, "mutable references are not allowed in {}s", ccx.const_kind(), - ) + ); + err.span_label(span, format!("`&mut` is only allowed in `const fn`")); + err }; - err.span_label(span, "`&mut` is only allowed in `const fn`".to_string()); if ccx.tcx.sess.teach(&err.get_code().unwrap()) { err.note( "References in statics and constants may only refer \ @@ -226,11 +323,17 @@ impl NonConstOp for MutBorrow { } } +// FIXME(ecstaticmorse): Unify this with `MutBorrow`. It has basically the same issues. #[derive(Debug)] pub struct MutAddressOf; impl NonConstOp for MutAddressOf { - fn feature_gate() -> Option<Symbol> { - Some(sym::const_mut_refs) + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + // Forbid everywhere except in const fn with a feature gate + if ccx.const_kind() == hir::ConstContext::ConstFn { + Status::Unstable(sym::const_mut_refs) + } else { + Status::Forbidden + } } fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -247,16 +350,16 @@ impl NonConstOp for MutAddressOf { #[derive(Debug)] pub struct MutDeref; impl NonConstOp for MutDeref { - fn feature_gate() -> Option<Symbol> { - Some(sym::const_mut_refs) + fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + Status::Unstable(sym::const_mut_refs) } } #[derive(Debug)] pub struct Panic; impl NonConstOp for Panic { - fn feature_gate() -> Option<Symbol> { - Some(sym::const_panic) + fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + Status::Unstable(sym::const_panic) } fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -289,8 +392,8 @@ impl NonConstOp for RawPtrComparison { #[derive(Debug)] pub struct RawPtrDeref; impl NonConstOp for RawPtrDeref { - fn feature_gate() -> Option<Symbol> { - Some(sym::const_raw_ptr_deref) + fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + Status::Unstable(sym::const_raw_ptr_deref) } fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -307,8 +410,8 @@ impl NonConstOp for RawPtrDeref { #[derive(Debug)] pub struct RawPtrToIntCast; impl NonConstOp for RawPtrToIntCast { - fn feature_gate() -> Option<Symbol> { - Some(sym::const_raw_ptr_to_usize_cast) + fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + Status::Unstable(sym::const_raw_ptr_to_usize_cast) } fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -326,8 +429,12 @@ impl NonConstOp for RawPtrToIntCast { #[derive(Debug)] pub struct StaticAccess; impl NonConstOp for StaticAccess { - fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { - matches!(ccx.const_kind(), hir::ConstContext::Static(_)) + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + if let hir::ConstContext::Static(_) = ccx.const_kind() { + Status::Allowed + } else { + Status::Forbidden + } } fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -369,16 +476,40 @@ impl NonConstOp for ThreadLocalAccess { } #[derive(Debug)] +pub struct Transmute; +impl NonConstOp for Transmute { + const STOPS_CONST_CHECKING: bool = true; + + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + if ccx.const_kind() != hir::ConstContext::ConstFn { + Status::Allowed + } else { + Status::Unstable(sym::const_fn_transmute) + } + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + feature_err( + &ccx.tcx.sess.parse_sess, + sym::const_fn_transmute, + span, + &format!("`transmute` is not allowed in {}s", ccx.const_kind()), + ) + .note("`transmute` is only allowed in constants and statics for now") + .emit(); + } +} + +#[derive(Debug)] pub struct UnionAccess; impl NonConstOp for UnionAccess { - fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { // Union accesses are stable in all contexts except `const fn`. - ccx.const_kind() != hir::ConstContext::ConstFn - || ccx.tcx.features().enabled(Self::feature_gate().unwrap()) - } - - fn feature_gate() -> Option<Symbol> { - Some(sym::const_fn_union) + if ccx.const_kind() != hir::ConstContext::ConstFn { + Status::Allowed + } else { + Status::Unstable(sym::const_fn_union) + } } fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -391,3 +522,138 @@ impl NonConstOp for UnionAccess { .emit(); } } + +/// See [#64992]. +/// +/// [#64992]: https://github.com/rust-lang/rust/issues/64992 +#[derive(Debug)] +pub struct UnsizingCast; +impl NonConstOp for UnsizingCast { + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + mcf_status_in_item(ccx) + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + mcf_emit_error( + ccx, + span, + "unsizing casts to types besides slices are not allowed in const fn", + ); + } +} + +// Types that cannot appear in the signature or locals of a `const fn`. +pub mod ty { + use super::*; + + #[derive(Debug)] + pub struct MutRef; + impl NonConstOp for MutRef { + const STOPS_CONST_CHECKING: bool = true; + + fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { + Status::Unstable(sym::const_mut_refs) + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + feature_err( + &ccx.tcx.sess.parse_sess, + sym::const_mut_refs, + span, + &format!("mutable references are not allowed in {}s", ccx.const_kind()), + ) + .emit() + } + } + + #[derive(Debug)] + pub struct FnPtr; + impl NonConstOp for FnPtr { + const STOPS_CONST_CHECKING: bool = true; + + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + // FIXME: This attribute a hack to allow the specialization of the `futures` API. See + // #59739. We should have a proper feature gate for this. + if ccx.tcx.has_attr(ccx.def_id.to_def_id(), sym::rustc_allow_const_fn_ptr) { + Status::Allowed + } else { + mcf_status_in_item(ccx) + } + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + mcf_emit_error(ccx, span, "function pointers in const fn are unstable"); + } + } + + #[derive(Debug)] + pub struct ImplTrait; + impl NonConstOp for ImplTrait { + const STOPS_CONST_CHECKING: bool = true; + + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + mcf_status_in_item(ccx) + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + mcf_emit_error(ccx, span, "`impl Trait` in const fn is unstable"); + } + } + + #[derive(Debug)] + pub struct TraitBound; + impl NonConstOp for TraitBound { + const STOPS_CONST_CHECKING: bool = true; + + fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + mcf_status_in_item(ccx) + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + mcf_emit_error( + ccx, + span, + "trait bounds other than `Sized` on const fn parameters are unstable", + ); + } + } + + /// A trait bound with the `?const Trait` opt-out + #[derive(Debug)] + pub struct TraitBoundNotConst; + impl NonConstOp for TraitBoundNotConst { + const STOPS_CONST_CHECKING: bool = true; + + fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + Status::Unstable(sym::const_trait_bound_opt_out) + } + + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + feature_err( + &ccx.tcx.sess.parse_sess, + sym::const_trait_bound_opt_out, + span, + "`?const Trait` syntax is unstable", + ) + .emit() + } + } +} + +fn mcf_status_in_item(ccx: &ConstCx<'_, '_>) -> Status { + if ccx.const_kind() != hir::ConstContext::ConstFn { + Status::Allowed + } else { + Status::Unstable(sym::const_fn) + } +} + +fn mcf_emit_error(ccx: &ConstCx<'_, '_>, span: Span, msg: &str) { + struct_span_err!(ccx.tcx.sess, span, E0723, "{}", msg) + .note( + "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \ + for more information", + ) + .help("add `#![feature(const_fn)]` to the crate attributes to enable") + .emit(); +} diff --git a/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs index 55075b3ab5e..0c171bbc464 100644 --- a/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs @@ -11,8 +11,13 @@ use super::ConstCx; /// Returns `true` if we should use the more precise live drop checker that runs after drop /// elaboration. -pub fn checking_enabled(tcx: TyCtxt<'tcx>) -> bool { - tcx.features().const_precise_live_drops +pub fn checking_enabled(ccx: &ConstCx<'_, '_>) -> bool { + // Const-stable functions must always use the stable live drop checker. + if ccx.is_const_stable_const_fn() { + return false; + } + + ccx.tcx.features().const_precise_live_drops } /// Look for live drops in a const context. @@ -25,12 +30,11 @@ pub fn check_live_drops(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &mir::Body< return; } - if !checking_enabled(tcx) { + let ccx = ConstCx { body, tcx, def_id, const_kind, param_env: tcx.param_env(def_id) }; + if !checking_enabled(&ccx) { return; } - let ccx = ConstCx { body, tcx, def_id, const_kind, param_env: tcx.param_env(def_id) }; - let mut visitor = CheckLiveDrops { ccx: &ccx, qualifs: Qualifs::default() }; visitor.visit_body(body); @@ -52,7 +56,7 @@ impl std::ops::Deref for CheckLiveDrops<'mir, 'tcx> { impl CheckLiveDrops<'mir, 'tcx> { fn check_live_drop(&self, span: Span) { - ops::non_const(self.ccx, ops::LiveDrop(None), span); + ops::non_const(self.ccx, ops::LiveDrop { dropped_at: None }, span); } } diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs index 445a0230afd..3f4b3ca2eed 100644 --- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs @@ -170,7 +170,7 @@ where // Special-case reborrows to be more like a copy of the reference. if let &[ref proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx).ty; - if let ty::Ref(..) = base_ty.kind { + if let ty::Ref(..) = base_ty.kind() { return in_place::<Q, _>( cx, in_local, diff --git a/compiler/rustc_mir/src/transform/check_consts/resolver.rs b/compiler/rustc_mir/src/transform/check_consts/resolver.rs index b8104292aab..a00301952b3 100644 --- a/compiler/rustc_mir/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_mir/src/transform/check_consts/resolver.rs @@ -165,23 +165,19 @@ where } } -impl<Q> dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> { - const BOTTOM_VALUE: bool = false; -} - impl<Q> dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> where Q: Qualif, { - type Idx = Local; + type Domain = BitSet<Local>; const NAME: &'static str = Q::ANALYSIS_NAME; - fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + BitSet::new_empty(body.local_decls.len()) } - fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { + fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) { self.transfer_function(state).initialize_state(); } } @@ -192,7 +188,7 @@ where { fn apply_statement_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, ) { @@ -201,7 +197,7 @@ where fn apply_terminator_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, ) { @@ -210,7 +206,7 @@ where fn apply_call_return_effect( &self, - state: &mut BitSet<Self::Idx>, + state: &mut Self::Domain, block: BasicBlock, func: &mir::Operand<'tcx>, args: &[mir::Operand<'tcx>], diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index e21f314ca15..dc28ba46d7c 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -7,19 +7,21 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::cast::CastTy; -use rustc_middle::ty::{self, Instance, InstanceDef, TyCtxt}; -use rustc_span::Span; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{ + self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt, TypeAndMut, +}; +use rustc_span::{sym, Span}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::{self, TraitEngine}; -use std::borrow::Cow; use std::ops::Deref; use super::ops::{self, NonConstOp}; use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{is_lang_panic_fn, ConstCx, Qualif}; -use crate::const_eval::{is_const_fn, is_unstable_const_fn}; +use crate::const_eval::is_unstable_const_fn; use crate::dataflow::impls::MaybeMutBorrowedLocals; use crate::dataflow::{self, Analysis}; @@ -57,6 +59,7 @@ impl Qualifs<'mir, 'tcx> { MaybeMutBorrowedLocals::mut_borrows_only(tcx, &body, param_env) .unsound_ignore_borrow_on_drop() .into_engine(tcx, &body, def_id.to_def_id()) + .pass_name("const_qualification") .iterate_to_fixpoint() .into_results_cursor(&body) }); @@ -176,6 +179,8 @@ pub struct Validator<'mir, 'tcx> { /// The span of the current statement. span: Span, + + const_checking_stopped: bool, } impl Deref for Validator<'mir, 'tcx> { @@ -188,30 +193,60 @@ impl Deref for Validator<'mir, 'tcx> { impl Validator<'mir, 'tcx> { pub fn new(ccx: &'mir ConstCx<'mir, 'tcx>) -> Self { - Validator { span: ccx.body.span, ccx, qualifs: Default::default() } + Validator { + span: ccx.body.span, + ccx, + qualifs: Default::default(), + const_checking_stopped: false, + } } pub fn check_body(&mut self) { - let ConstCx { tcx, body, def_id, const_kind, .. } = *self.ccx; - - let use_min_const_fn_checks = (const_kind == Some(hir::ConstContext::ConstFn) - && crate::const_eval::is_min_const_fn(tcx, def_id.to_def_id())) - && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you; - - if use_min_const_fn_checks { - // Enforce `min_const_fn` for stable `const fn`s. - use crate::transform::qualify_min_const_fn::is_min_const_fn; - if let Err((span, err)) = is_min_const_fn(tcx, def_id.to_def_id(), &body) { - error_min_const_fn_violation(tcx, span, err); - return; + let ConstCx { tcx, body, def_id, .. } = *self.ccx; + + // HACK: This function has side-effects???? Make sure we call it. + let _ = crate::const_eval::is_min_const_fn(tcx, def_id.to_def_id()); + + // The local type and predicate checks are not free and only relevant for `const fn`s. + if self.const_kind() == hir::ConstContext::ConstFn { + // Prevent const trait methods from being annotated as `stable`. + // FIXME: Do this as part of stability checking. + if self.is_const_stable_const_fn() { + let hir_id = tcx.hir().local_def_id_to_hir_id(self.def_id); + if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) { + struct_span_err!( + self.ccx.tcx.sess, + self.span, + E0723, + "trait methods cannot be stable const fn" + ) + .emit(); + } } + + self.check_item_predicates(); + + for local in &body.local_decls { + if local.internal { + continue; + } + + self.span = local.source_info.span; + self.check_local_or_return_ty(local.ty); + } + + // impl trait is gone in MIR, so check the return type of a const fn by its signature + // instead of the type of the return place. + self.span = body.local_decls[RETURN_PLACE].source_info.span; + let return_ty = tcx.fn_sig(def_id).output(); + self.check_local_or_return_ty(return_ty.skip_binder()); } self.visit_body(&body); // Ensure that the end result is `Sync` in a non-thread local `static`. - let should_check_for_sync = const_kind - == Some(hir::ConstContext::Static(hir::Mutability::Not)) + let should_check_for_sync = self.const_kind() + == hir::ConstContext::Static(hir::Mutability::Not) && !tcx.is_thread_local_static(def_id.to_def_id()); if should_check_for_sync { @@ -226,13 +261,22 @@ impl Validator<'mir, 'tcx> { /// Emits an error if an expression cannot be evaluated in the current context. pub fn check_op(&mut self, op: impl NonConstOp) { - ops::non_const(self.ccx, op, self.span); + self.check_op_spanned(op, self.span); } /// Emits an error at the given `span` if an expression cannot be evaluated in the current /// context. - pub fn check_op_spanned(&mut self, op: impl NonConstOp, span: Span) { - ops::non_const(self.ccx, op, span); + pub fn check_op_spanned<O: NonConstOp>(&mut self, op: O, span: Span) { + // HACK: This is for strict equivalence with the old `qualify_min_const_fn` pass, which + // only emitted one error per function. It should be removed and the test output updated. + if self.const_checking_stopped { + return; + } + + let err_emitted = ops::non_const(self.ccx, op, span); + if err_emitted && O::STOPS_CONST_CHECKING { + self.const_checking_stopped = true; + } } fn check_static(&mut self, def_id: DefId, span: Span) { @@ -242,6 +286,100 @@ impl Validator<'mir, 'tcx> { ); self.check_op_spanned(ops::StaticAccess, span) } + + fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>) { + for ty in ty.walk() { + let ty = match ty.unpack() { + GenericArgKind::Type(ty) => ty, + + // No constraints on lifetimes or constants, except potentially + // constants' types, but `walk` will get to them as well. + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue, + }; + + match *ty.kind() { + ty::Ref(_, _, hir::Mutability::Mut) => self.check_op(ops::ty::MutRef), + ty::Opaque(..) => self.check_op(ops::ty::ImplTrait), + ty::FnPtr(..) => self.check_op(ops::ty::FnPtr), + + ty::Dynamic(preds, _) => { + for pred in preds.iter() { + match pred.skip_binder() { + ty::ExistentialPredicate::AutoTrait(_) + | ty::ExistentialPredicate::Projection(_) => { + self.check_op(ops::ty::TraitBound) + } + ty::ExistentialPredicate::Trait(trait_ref) => { + if Some(trait_ref.def_id) != self.tcx.lang_items().sized_trait() { + self.check_op(ops::ty::TraitBound) + } + } + } + } + } + _ => {} + } + } + } + + fn check_item_predicates(&mut self) { + let ConstCx { tcx, def_id, .. } = *self.ccx; + + let mut current = def_id.to_def_id(); + loop { + let predicates = tcx.predicates_of(current); + for (predicate, _) in predicates.predicates { + match predicate.skip_binders() { + ty::PredicateAtom::RegionOutlives(_) + | ty::PredicateAtom::TypeOutlives(_) + | ty::PredicateAtom::WellFormed(_) + | ty::PredicateAtom::Projection(_) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue, + ty::PredicateAtom::ObjectSafe(_) => { + bug!("object safe predicate on function: {:#?}", predicate) + } + ty::PredicateAtom::ClosureKind(..) => { + bug!("closure kind predicate on function: {:#?}", predicate) + } + ty::PredicateAtom::Subtype(_) => { + bug!("subtype predicate on function: {:#?}", predicate) + } + ty::PredicateAtom::Trait(pred, constness) => { + if Some(pred.def_id()) == tcx.lang_items().sized_trait() { + continue; + } + match pred.self_ty().kind() { + ty::Param(p) => { + let generics = tcx.generics_of(current); + let def = generics.type_param(p, tcx); + let span = tcx.def_span(def.def_id); + + if constness == hir::Constness::Const { + self.check_op_spanned(ops::ty::TraitBound, span); + } else if !tcx.features().const_fn + || self.ccx.is_const_stable_const_fn() + { + // HACK: We shouldn't need the conditional above, but trait + // bounds on containing impl blocks are wrongly being marked as + // "not-const". + self.check_op_spanned(ops::ty::TraitBound, span); + } + } + // other kinds of bounds are either tautologies + // or cause errors in other passes + _ => continue, + } + } + } + } + match predicates.parent { + Some(parent) => current = parent, + None => break, + } + } + } } impl Visitor<'tcx> for Validator<'mir, 'tcx> { @@ -309,11 +447,6 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { Rvalue::Use(_) | Rvalue::Repeat(..) - | Rvalue::UnaryOp(UnOp::Neg, _) - | Rvalue::UnaryOp(UnOp::Not, _) - | Rvalue::NullaryOp(NullOp::SizeOf, _) - | Rvalue::CheckedBinaryOp(..) - | Rvalue::Cast(CastKind::Pointer(_), ..) | Rvalue::Discriminant(..) | Rvalue::Len(_) | Rvalue::Aggregate(..) => {} @@ -321,7 +454,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place) | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => { let ty = place.ty(self.body, self.tcx).ty; - let is_allowed = match ty.kind { + let is_allowed = match ty.kind() { // Inside a `static mut`, `&mut [...]` is allowed. ty::Array(..) | ty::Slice(_) if self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut) => @@ -363,6 +496,35 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { } } + Rvalue::Cast( + CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), + _, + _, + ) => {} + + Rvalue::Cast( + CastKind::Pointer( + PointerCast::UnsafeFnPointer + | PointerCast::ClosureFnPointer(_) + | PointerCast::ReifyFnPointer, + ), + _, + _, + ) => self.check_op(ops::FnPtrCast), + + Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, cast_ty) => { + if let Some(TypeAndMut { ty, .. }) = cast_ty.builtin_deref(true) { + let unsized_ty = self.tcx.struct_tail_erasing_lifetimes(ty, self.param_env); + + // Casting/coercing things to slices is fine. + if let ty::Slice(_) | ty::Str = unsized_ty.kind() { + return; + } + } + + self.check_op(ops::UnsizingCast); + } + Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { let operand_ty = operand.ty(self.body, self.tcx); let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); @@ -373,8 +535,23 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { } } - Rvalue::BinaryOp(op, ref lhs, _) => { - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind { + Rvalue::NullaryOp(NullOp::SizeOf, _) => {} + Rvalue::NullaryOp(NullOp::Box, _) => self.check_op(ops::HeapAllocation), + + Rvalue::UnaryOp(_, ref operand) => { + let ty = operand.ty(self.body, self.tcx); + if !(ty.is_integral() || ty.is_bool()) { + self.check_op(ops::NonPrimitiveOp) + } + } + + Rvalue::BinaryOp(op, ref lhs, ref rhs) + | Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { + let lhs_ty = lhs.ty(self.body, self.tcx); + let rhs_ty = rhs.ty(self.body, self.tcx); + + if let ty::RawPtr(_) | ty::FnPtr(..) = lhs_ty.kind() { + assert_eq!(lhs_ty, rhs_ty); assert!( op == BinOp::Eq || op == BinOp::Ne @@ -387,10 +564,12 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { self.check_op(ops::RawPtrComparison); } - } - Rvalue::NullaryOp(NullOp::Box, _) => { - self.check_op(ops::HeapAllocation); + if !(lhs_ty.is_integral() || lhs_ty.is_bool() || lhs_ty.is_char()) + || !(rhs_ty.is_integral() || rhs_ty.is_bool() || rhs_ty.is_char()) + { + self.check_op(ops::NonPrimitiveOp) + } } } } @@ -426,7 +605,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { 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 let ty::RawPtr(_) = base_ty.kind() { if proj_base.is_empty() { if let (local, []) = (place_local, proj_base) { let decl = &self.body.local_decls[local]; @@ -491,14 +670,19 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { } fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + use rustc_target::spec::abi::Abi::RustIntrinsic; + trace!("visit_terminator: terminator={:?} location={:?}", terminator, location); self.super_terminator(terminator, location); match &terminator.kind { TerminatorKind::Call { func, .. } => { - let fn_ty = func.ty(self.body, self.tcx); + let ConstCx { tcx, body, def_id: caller, param_env, .. } = *self.ccx; + let caller = caller.to_def_id(); + + let fn_ty = func.ty(body, tcx); - let (def_id, substs) = match fn_ty.kind { + let (mut callee, substs) = match *fn_ty.kind() { ty::FnDef(def_id, substs) => (def_id, substs), ty::FnPtr(_) => { @@ -510,38 +694,80 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { } }; - // At this point, we are calling a function whose `DefId` is known... - if is_const_fn(self.tcx, def_id) { - return; - } - - // See if this is a trait method for a concrete type whose impl of that trait is - // `const`. + // Resolve a trait method call to its concrete implementation, which may be in a + // `const` trait impl. if self.tcx.features().const_trait_impl { - let instance = Instance::resolve(self.tcx, self.param_env, def_id, substs); - debug!("Resolving ({:?}) -> {:?}", def_id, instance); + let instance = Instance::resolve(tcx, param_env, callee, substs); + debug!("Resolving ({:?}) -> {:?}", callee, instance); if let Ok(Some(func)) = instance { if let InstanceDef::Item(def) = func.def { - if is_const_fn(self.tcx, def.did) { - return; - } + callee = def.did; } } } - if is_lang_panic_fn(self.tcx, def_id) { + // At this point, we are calling a function, `callee`, whose `DefId` is known... + + if is_lang_panic_fn(tcx, callee) { self.check_op(ops::Panic); - } else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) { - // Exempt unstable const fns inside of macros or functions with - // `#[allow_internal_unstable]`. - use crate::transform::qualify_min_const_fn::lib_feature_allowed; - if !self.span.allows_unstable(feature) - && !lib_feature_allowed(self.tcx, self.def_id.to_def_id(), feature) - { - self.check_op(ops::FnCallUnstable(def_id, feature)); + return; + } + + // HACK: This is to "unstabilize" the `transmute` intrinsic + // within const fns. `transmute` is allowed in all other const contexts. + // This won't really scale to more intrinsics or functions. Let's allow const + // transmutes in const fn before we add more hacks to this. + if tcx.fn_sig(callee).abi() == RustIntrinsic + && tcx.item_name(callee) == sym::transmute + { + self.check_op(ops::Transmute); + return; + } + + if !tcx.is_const_fn_raw(callee) { + self.check_op(ops::FnCallNonConst(callee)); + return; + } + + // If the `const fn` we are trying to call is not const-stable, ensure that we have + // the proper feature gate enabled. + if let Some(gate) = is_unstable_const_fn(tcx, callee) { + if self.span.allows_unstable(gate) { + return; + } + + // Calling an unstable function *always* requires that the corresponding gate + // be enabled, even if the function has `#[allow_internal_unstable(the_gate)]`. + if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate) { + self.check_op(ops::FnCallUnstable(callee, Some(gate))); + return; + } + + // If this crate is not using stability attributes, or the caller is not claiming to be a + // stable `const fn`, that is all that is required. + if !self.ccx.is_const_stable_const_fn() { + return; + } + + // Otherwise, we are something const-stable calling a const-unstable fn. + + if super::allow_internal_unstable(tcx, caller, gate) { + return; + } + + self.check_op(ops::FnCallUnstable(callee, Some(gate))); + return; + } + + // FIXME(ecstaticmorse); For compatibility, we consider `unstable` callees that + // have no `rustc_const_stable` attributes to be const-unstable as well. This + // should be fixed later. + let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none() + && tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable()); + if callee_is_unstable_unmarked { + if self.ccx.is_const_stable_const_fn() { + self.check_op(ops::FnCallUnstable(callee, None)); } - } else { - self.check_op(ops::FnCallNonConst(def_id)); } } @@ -551,7 +777,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { | TerminatorKind::DropAndReplace { place: dropped_place, .. } => { // If we are checking live drops after drop-elaboration, don't emit duplicate // errors here. - if super::post_drop_elaboration::checking_enabled(self.tcx) { + if super::post_drop_elaboration::checking_enabled(self.ccx) { return; } @@ -576,43 +802,31 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { if needs_drop { self.check_op_spanned( - ops::LiveDrop(Some(terminator.source_info.span)), + ops::LiveDrop { dropped_at: Some(terminator.source_info.span) }, err_span, ); } } - TerminatorKind::InlineAsm { .. } => { - self.check_op(ops::InlineAsm); + TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm), + TerminatorKind::Abort => self.check_op(ops::Abort), + + TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { + self.check_op(ops::Generator) } - // FIXME: Some of these are only caught by `min_const_fn`, but should error here - // instead. - TerminatorKind::Abort - | TerminatorKind::Assert { .. } + TerminatorKind::Assert { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } | TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Unreachable - | TerminatorKind::Yield { .. } => {} + | TerminatorKind::Unreachable => {} } } } -fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) { - struct_span_err!(tcx.sess, span, E0723, "{}", msg) - .note( - "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \ - for more information", - ) - .help("add `#![feature(const_fn)]` to the crate attributes to enable") - .emit(); -} - fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) { let ty = body.return_ty(); tcx.infer_ctxt().enter(|infcx| { @@ -647,7 +861,7 @@ fn place_as_reborrow( // This is sufficient to prevent an access to a `static mut` from being marked as a // reborrow, even if the check above were to disappear. let inner_ty = Place::ty_from(place.local, inner, body, tcx).ty; - match inner_ty.kind { + match inner_ty.kind() { ty::Ref(..) => Some(inner), _ => None, } diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs index c3e04e698db..7309a4129e4 100644 --- a/compiler/rustc_mir/src/transform/check_unsafety.rs +++ b/compiler/rustc_mir/src/transform/check_unsafety.rs @@ -91,8 +91,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { ) } - if let ty::FnDef(func_id, _) = func_ty.kind { - self.check_target_features(func_id); + if let ty::FnDef(func_id, _) = func_ty.kind() { + self.check_target_features(*func_id); } } @@ -227,7 +227,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } } let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; - match base_ty.kind { + match base_ty.kind() { ty::RawPtr(..) => self.require_unsafe( UnsafetyViolationKind::GeneralAndConstFn, UnsafetyViolationDetails::DerefOfRawPointer, @@ -394,7 +394,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { ProjectionElem::Field(..) => { let ty = Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty; - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { if self.tcx.layout_scalar_valid_range(def.did) != (Bound::Unbounded, Bound::Unbounded) { diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index 56479b047fa..aa88719c26a 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -832,7 +832,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // FIXME: enable the general case stated above ^. let ty = &value.layout.ty; // Only do it for tuples - if let ty::Tuple(substs) = ty.kind { + if let ty::Tuple(substs) = ty.kind() { // Only do it if tuple is also a pair with two scalars if substs.len() == 2 { let alloc = self.use_ecx(|this| { diff --git a/compiler/rustc_mir/src/transform/dest_prop.rs b/compiler/rustc_mir/src/transform/dest_prop.rs new file mode 100644 index 00000000000..46cbced2d54 --- /dev/null +++ b/compiler/rustc_mir/src/transform/dest_prop.rs @@ -0,0 +1,1057 @@ +//! Propagates assignment destinations backwards in the CFG to eliminate redundant assignments. +//! +//! # Motivation +//! +//! MIR building can insert a lot of redundant copies, and Rust code in general often tends to move +//! values around a lot. The result is a lot of assignments of the form `dest = {move} src;` in MIR. +//! MIR building for constants in particular tends to create additional locals that are only used +//! inside a single block to shuffle a value around unnecessarily. +//! +//! LLVM by itself is not good enough at eliminating these redundant copies (eg. see +//! https://github.com/rust-lang/rust/issues/32966), so this leaves some performance on the table +//! that we can regain by implementing an optimization for removing these assign statements in rustc +//! itself. When this optimization runs fast enough, it can also speed up the constant evaluation +//! and code generation phases of rustc due to the reduced number of statements and locals. +//! +//! # The Optimization +//! +//! Conceptually, this optimization is "destination propagation". It is similar to the Named Return +//! Value Optimization, or NRVO, known from the C++ world, except that it isn't limited to return +//! values or the return place `_0`. On a very high level, independent of the actual implementation +//! details, it does the following: +//! +//! 1) Identify `dest = src;` statements that can be soundly eliminated. +//! 2) Replace all mentions of `src` with `dest` ("unifying" them and propagating the destination +//! backwards). +//! 3) Delete the `dest = src;` statement (by making it a `nop`). +//! +//! Step 1) is by far the hardest, so it is explained in more detail below. +//! +//! ## Soundness +//! +//! Given an `Assign` statement `dest = src;`, where `dest` is a `Place` and `src` is an `Rvalue`, +//! there are a few requirements that must hold for the optimization to be sound: +//! +//! * `dest` must not contain any *indirection* through a pointer. It must access part of the base +//! local. Otherwise it might point to arbitrary memory that is hard to track. +//! +//! It must also not contain any indexing projections, since those take an arbitrary `Local` as +//! the index, and that local might only be initialized shortly before `dest` is used. +//! +//! Subtle case: If `dest` is a, or projects through a union, then we have to make sure that there +//! remains an assignment to it, since that sets the "active field" of the union. But if `src` is +//! a ZST, it might not be initialized, so there might not be any use of it before the assignment, +//! and performing the optimization would simply delete the assignment, leaving `dest` +//! uninitialized. +//! +//! * `src` must be a bare `Local` without any indirections or field projections (FIXME: Is this a +//! fundamental restriction or just current impl state?). It can be copied or moved by the +//! assignment. +//! +//! * The `dest` and `src` locals must never be [*live*][liveness] at the same time. If they are, it +//! means that they both hold a (potentially different) value that is needed by a future use of +//! the locals. Unifying them would overwrite one of the values. +//! +//! Note that computing liveness of locals that have had their address taken is more difficult: +//! Short of doing full escape analysis on the address/pointer/reference, the pass would need to +//! assume that any operation that can potentially involve opaque user code (such as function +//! calls, destructors, and inline assembly) may access any local that had its address taken +//! before that point. +//! +//! Here, the first two conditions are simple structural requirements on the `Assign` statements +//! that can be trivially checked. The liveness requirement however is more difficult and costly to +//! check. +//! +//! ## Previous Work +//! +//! A [previous attempt] at implementing an optimization like this turned out to be a significant +//! regression in compiler performance. Fixing the regressions introduced a lot of undesirable +//! complexity to the implementation. +//! +//! A [subsequent approach] tried to avoid the costly computation by limiting itself to acyclic +//! CFGs, but still turned out to be far too costly to run due to suboptimal performance within +//! individual basic blocks, requiring a walk across the entire block for every assignment found +//! within the block. For the `tuple-stress` benchmark, which has 458745 statements in a single +//! block, this proved to be far too costly. +//! +//! Since the first attempt at this, the compiler has improved dramatically, and new analysis +//! frameworks have been added that should make this approach viable without requiring a limited +//! approach that only works for some classes of CFGs: +//! - rustc now has a powerful dataflow analysis framework that can handle forwards and backwards +//! analyses efficiently. +//! - Layout optimizations for generators have been added to improve code generation for +//! async/await, which are very similar in spirit to what this optimization does. Both walk the +//! MIR and record conflicting uses of locals in a `BitMatrix`. +//! +//! Also, rustc now has a simple NRVO pass (see `nrvo.rs`), which handles a subset of the cases that +//! this destination propagation pass handles, proving that similar optimizations can be performed +//! on MIR. +//! +//! ## Pre/Post Optimization +//! +//! It is recommended to run `SimplifyCfg` and then `SimplifyLocals` some time after this pass, as +//! it replaces the eliminated assign statements with `nop`s and leaves unused locals behind. +//! +//! [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis +//! [previous attempt]: https://github.com/rust-lang/rust/pull/47954 +//! [subsequent approach]: https://github.com/rust-lang/rust/pull/71003 + +use crate::dataflow::impls::{MaybeInitializedLocals, MaybeLiveLocals}; +use crate::dataflow::Analysis; +use crate::{ + transform::{MirPass, MirSource}, + util::{dump_mir, PassWhere}, +}; +use itertools::Itertools; +use rustc_data_structures::unify::{InPlaceUnificationTable, UnifyKey}; +use rustc_index::{ + bit_set::{BitMatrix, BitSet}, + vec::IndexVec, +}; +use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_middle::mir::{ + traversal, Body, InlineAsmOperand, Local, LocalKind, Location, Operand, Place, PlaceElem, + Rvalue, Statement, StatementKind, Terminator, TerminatorKind, +}; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +// Empirical measurements have resulted in some observations: +// - Running on a body with a single block and 500 locals takes barely any time +// - Running on a body with ~400 blocks and ~300 relevant locals takes "too long" +// ...so we just limit both to somewhat reasonable-ish looking values. +const MAX_LOCALS: usize = 500; +const MAX_BLOCKS: usize = 250; + +pub struct DestinationPropagation; + +impl<'tcx> MirPass<'tcx> for DestinationPropagation { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + // Only run at mir-opt-level=2 or higher for now (we don't fix up debuginfo and remove + // storage statements at the moment). + if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 { + return; + } + + let candidates = find_candidates(tcx, body); + if candidates.is_empty() { + debug!("{:?}: no dest prop candidates, done", source.def_id()); + return; + } + + // Collect all locals we care about. We only compute conflicts for these to save time. + let mut relevant_locals = BitSet::new_empty(body.local_decls.len()); + for CandidateAssignment { dest, src, loc: _ } in &candidates { + relevant_locals.insert(dest.local); + relevant_locals.insert(*src); + } + + // This pass unfortunately has `O(l² * s)` performance, where `l` is the number of locals + // and `s` is the number of statements and terminators in the function. + // To prevent blowing up compile times too much, we bail out when there are too many locals. + let relevant = relevant_locals.count(); + debug!( + "{:?}: {} locals ({} relevant), {} blocks", + source.def_id(), + body.local_decls.len(), + relevant, + body.basic_blocks().len() + ); + if relevant > MAX_LOCALS { + warn!( + "too many candidate locals in {:?} ({}, max is {}), not optimizing", + source.def_id(), + relevant, + MAX_LOCALS + ); + return; + } + if body.basic_blocks().len() > MAX_BLOCKS { + warn!( + "too many blocks in {:?} ({}, max is {}), not optimizing", + source.def_id(), + body.basic_blocks().len(), + MAX_BLOCKS + ); + return; + } + + let mut conflicts = Conflicts::build(tcx, body, source, &relevant_locals); + + let mut replacements = Replacements::new(body.local_decls.len()); + for candidate @ CandidateAssignment { dest, src, loc } in candidates { + // Merge locals that don't conflict. + if !conflicts.can_unify(dest.local, src) { + debug!("at assignment {:?}, conflict {:?} vs. {:?}", loc, dest.local, src); + continue; + } + + if replacements.for_src(candidate.src).is_some() { + debug!("src {:?} already has replacement", candidate.src); + continue; + } + + if !tcx.consider_optimizing(|| { + format!("DestinationPropagation {:?} {:?}", source.def_id(), candidate) + }) { + break; + } + + replacements.push(candidate); + conflicts.unify(candidate.src, candidate.dest.local); + } + + replacements.flatten(tcx); + + debug!("replacements {:?}", replacements.map); + + Replacer { tcx, replacements, place_elem_cache: Vec::new() }.visit_body(body); + + // FIXME fix debug info + } +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +struct UnifyLocal(Local); + +impl From<Local> for UnifyLocal { + fn from(l: Local) -> Self { + Self(l) + } +} + +impl UnifyKey for UnifyLocal { + type Value = (); + fn index(&self) -> u32 { + self.0.as_u32() + } + fn from_index(u: u32) -> Self { + Self(Local::from_u32(u)) + } + fn tag() -> &'static str { + "UnifyLocal" + } +} + +struct Replacements<'tcx> { + /// Maps locals to their replacement. + map: IndexVec<Local, Option<Place<'tcx>>>, + + /// Whose locals' live ranges to kill. + kill: BitSet<Local>, +} + +impl Replacements<'tcx> { + fn new(locals: usize) -> Self { + Self { map: IndexVec::from_elem_n(None, locals), kill: BitSet::new_empty(locals) } + } + + fn push(&mut self, candidate: CandidateAssignment<'tcx>) { + trace!("Replacements::push({:?})", candidate); + let entry = &mut self.map[candidate.src]; + assert!(entry.is_none()); + + *entry = Some(candidate.dest); + self.kill.insert(candidate.src); + self.kill.insert(candidate.dest.local); + } + + /// Applies the stored replacements to all replacements, until no replacements would result in + /// locals that need further replacements when applied. + fn flatten(&mut self, tcx: TyCtxt<'tcx>) { + // Note: This assumes that there are no cycles in the replacements, which is enforced via + // `self.unified_locals`. Otherwise this can cause an infinite loop. + + for local in self.map.indices() { + if let Some(replacement) = self.map[local] { + // Substitute the base local of `replacement` until fixpoint. + let mut base = replacement.local; + let mut reversed_projection_slices = Vec::with_capacity(1); + while let Some(replacement_for_replacement) = self.map[base] { + base = replacement_for_replacement.local; + reversed_projection_slices.push(replacement_for_replacement.projection); + } + + let projection: Vec<_> = reversed_projection_slices + .iter() + .rev() + .flat_map(|projs| projs.iter()) + .chain(replacement.projection.iter()) + .collect(); + let projection = tcx.intern_place_elems(&projection); + + // Replace with the final `Place`. + self.map[local] = Some(Place { local: base, projection }); + } + } + } + + fn for_src(&self, src: Local) -> Option<Place<'tcx>> { + self.map[src] + } +} + +struct Replacer<'tcx> { + tcx: TyCtxt<'tcx>, + replacements: Replacements<'tcx>, + place_elem_cache: Vec<PlaceElem<'tcx>>, +} + +impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_local(&mut self, local: &mut Local, context: PlaceContext, location: Location) { + if context.is_use() && self.replacements.for_src(*local).is_some() { + bug!( + "use of local {:?} should have been replaced by visit_place; context={:?}, loc={:?}", + local, + context, + location, + ); + } + } + + fn process_projection_elem( + &mut self, + elem: PlaceElem<'tcx>, + _: Location, + ) -> Option<PlaceElem<'tcx>> { + match elem { + PlaceElem::Index(local) => { + if let Some(replacement) = self.replacements.for_src(local) { + bug!( + "cannot replace {:?} with {:?} in index projection {:?}", + local, + replacement, + elem, + ); + } else { + None + } + } + _ => None, + } + } + + fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { + if let Some(replacement) = self.replacements.for_src(place.local) { + // Rebase `place`s projections onto `replacement`'s. + self.place_elem_cache.clear(); + self.place_elem_cache.extend(replacement.projection.iter().chain(place.projection)); + let projection = self.tcx.intern_place_elems(&self.place_elem_cache); + let new_place = Place { local: replacement.local, projection }; + + debug!("Replacer: {:?} -> {:?}", place, new_place); + *place = new_place; + } + + self.super_place(place, context, location); + } + + fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) { + self.super_statement(statement, location); + + match &statement.kind { + // FIXME: Don't delete storage statements, merge the live ranges instead + StatementKind::StorageDead(local) | StatementKind::StorageLive(local) + if self.replacements.kill.contains(*local) => + { + statement.make_nop() + } + + StatementKind::Assign(box (dest, rvalue)) => { + match rvalue { + Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) => { + // These might've been turned into self-assignments by the replacement + // (this includes the original statement we wanted to eliminate). + if dest == place { + debug!("{:?} turned into self-assignment, deleting", location); + statement.make_nop(); + } + } + _ => {} + } + } + + _ => {} + } + } +} + +struct Conflicts<'a> { + relevant_locals: &'a BitSet<Local>, + + /// The conflict matrix. It is always symmetric and the adjacency matrix of the corresponding + /// conflict graph. + matrix: BitMatrix<Local, Local>, + + /// Preallocated `BitSet` used by `unify`. + unify_cache: BitSet<Local>, + + /// Tracks locals that have been merged together to prevent cycles and propagate conflicts. + unified_locals: InPlaceUnificationTable<UnifyLocal>, +} + +impl Conflicts<'a> { + fn build<'tcx>( + tcx: TyCtxt<'tcx>, + body: &'_ Body<'tcx>, + source: MirSource<'tcx>, + relevant_locals: &'a BitSet<Local>, + ) -> Self { + // We don't have to look out for locals that have their address taken, since + // `find_candidates` already takes care of that. + + let conflicts = BitMatrix::from_row_n( + &BitSet::new_empty(body.local_decls.len()), + body.local_decls.len(), + ); + + let def_id = source.def_id(); + let mut init = MaybeInitializedLocals + .into_engine(tcx, body, def_id) + .iterate_to_fixpoint() + .into_results_cursor(body); + let mut live = MaybeLiveLocals + .into_engine(tcx, body, def_id) + .iterate_to_fixpoint() + .into_results_cursor(body); + + let mut reachable = None; + dump_mir( + tcx, + None, + "DestinationPropagation-dataflow", + &"", + source, + body, + |pass_where, w| { + let reachable = + reachable.get_or_insert_with(|| traversal::reachable_as_bitset(body)); + + match pass_where { + PassWhere::BeforeLocation(loc) if reachable.contains(loc.block) => { + init.seek_before_primary_effect(loc); + live.seek_after_primary_effect(loc); + + writeln!(w, " // init: {:?}", init.get())?; + writeln!(w, " // live: {:?}", live.get())?; + } + PassWhere::AfterTerminator(bb) if reachable.contains(bb) => { + let loc = body.terminator_loc(bb); + init.seek_after_primary_effect(loc); + live.seek_before_primary_effect(loc); + + writeln!(w, " // init: {:?}", init.get())?; + writeln!(w, " // live: {:?}", live.get())?; + } + + PassWhere::BeforeBlock(bb) if reachable.contains(bb) => { + init.seek_to_block_start(bb); + live.seek_to_block_start(bb); + + writeln!(w, " // init: {:?}", init.get())?; + writeln!(w, " // live: {:?}", live.get())?; + } + + PassWhere::BeforeCFG | PassWhere::AfterCFG | PassWhere::AfterLocation(_) => {} + + PassWhere::BeforeLocation(_) | PassWhere::AfterTerminator(_) => { + writeln!(w, " // init: <unreachable>")?; + writeln!(w, " // live: <unreachable>")?; + } + + PassWhere::BeforeBlock(_) => { + writeln!(w, " // init: <unreachable>")?; + writeln!(w, " // live: <unreachable>")?; + } + } + + Ok(()) + }, + ); + + let mut this = Self { + relevant_locals, + matrix: conflicts, + unify_cache: BitSet::new_empty(body.local_decls.len()), + unified_locals: { + let mut table = InPlaceUnificationTable::new(); + // Pre-fill table with all locals (this creates N nodes / "connected" components, + // "graph"-ically speaking). + for local in 0..body.local_decls.len() { + assert_eq!(table.new_key(()), UnifyLocal(Local::from_usize(local))); + } + table + }, + }; + + let mut live_and_init_locals = Vec::new(); + + // Visit only reachable basic blocks. The exact order is not important. + for (block, data) in traversal::preorder(body) { + // We need to observe the dataflow state *before* all possible locations (statement or + // terminator) in each basic block, and then observe the state *after* the terminator + // effect is applied. As long as neither `init` nor `borrowed` has a "before" effect, + // we will observe all possible dataflow states. + + // Since liveness is a backwards analysis, we need to walk the results backwards. To do + // that, we first collect in the `MaybeInitializedLocals` results in a forwards + // traversal. + + live_and_init_locals.resize_with(data.statements.len() + 1, || { + BitSet::new_empty(body.local_decls.len()) + }); + + // First, go forwards for `MaybeInitializedLocals` and apply intra-statement/terminator + // conflicts. + for (i, statement) in data.statements.iter().enumerate() { + this.record_statement_conflicts(statement); + + let loc = Location { block, statement_index: i }; + init.seek_before_primary_effect(loc); + + live_and_init_locals[i].clone_from(init.get()); + } + + this.record_terminator_conflicts(data.terminator()); + let term_loc = Location { block, statement_index: data.statements.len() }; + init.seek_before_primary_effect(term_loc); + live_and_init_locals[term_loc.statement_index].clone_from(init.get()); + + // Now, go backwards and union with the liveness results. + for statement_index in (0..=data.statements.len()).rev() { + let loc = Location { block, statement_index }; + live.seek_after_primary_effect(loc); + + live_and_init_locals[statement_index].intersect(live.get()); + + trace!("record conflicts at {:?}", loc); + + this.record_dataflow_conflicts(&mut live_and_init_locals[statement_index]); + } + + init.seek_to_block_end(block); + live.seek_to_block_end(block); + let mut conflicts = init.get().clone(); + conflicts.intersect(live.get()); + trace!("record conflicts at end of {:?}", block); + + this.record_dataflow_conflicts(&mut conflicts); + } + + this + } + + fn record_dataflow_conflicts(&mut self, new_conflicts: &mut BitSet<Local>) { + // Remove all locals that are not candidates. + new_conflicts.intersect(self.relevant_locals); + + for local in new_conflicts.iter() { + self.matrix.union_row_with(&new_conflicts, local); + } + } + + fn record_local_conflict(&mut self, a: Local, b: Local, why: &str) { + trace!("conflict {:?} <-> {:?} due to {}", a, b, why); + self.matrix.insert(a, b); + self.matrix.insert(b, a); + } + + /// Records locals that must not overlap during the evaluation of `stmt`. These locals conflict + /// and must not be merged. + fn record_statement_conflicts(&mut self, stmt: &Statement<'_>) { + match &stmt.kind { + // While the left and right sides of an assignment must not overlap, we do not mark + // conflicts here as that would make this optimization useless. When we optimize, we + // eliminate the resulting self-assignments automatically. + StatementKind::Assign(_) => {} + + StatementKind::LlvmInlineAsm(asm) => { + // Inputs and outputs must not overlap. + for (_, input) in &*asm.inputs { + if let Some(in_place) = input.place() { + if !in_place.is_indirect() { + for out_place in &*asm.outputs { + if !out_place.is_indirect() && !in_place.is_indirect() { + self.record_local_conflict( + in_place.local, + out_place.local, + "aliasing llvm_asm! operands", + ); + } + } + } + } + } + } + + StatementKind::SetDiscriminant { .. } + | StatementKind::StorageLive(..) + | StatementKind::StorageDead(..) + | StatementKind::Retag(..) + | StatementKind::FakeRead(..) + | StatementKind::AscribeUserType(..) + | StatementKind::Coverage(..) + | StatementKind::Nop => {} + } + } + + fn record_terminator_conflicts(&mut self, term: &Terminator<'_>) { + match &term.kind { + TerminatorKind::DropAndReplace { + place: dropped_place, + value, + target: _, + unwind: _, + } => { + if let Some(place) = value.place() { + if !place.is_indirect() && !dropped_place.is_indirect() { + self.record_local_conflict( + place.local, + dropped_place.local, + "DropAndReplace operand overlap", + ); + } + } + } + TerminatorKind::Yield { value, resume: _, resume_arg, drop: _ } => { + if let Some(place) = value.place() { + if !place.is_indirect() && !resume_arg.is_indirect() { + self.record_local_conflict( + place.local, + resume_arg.local, + "Yield operand overlap", + ); + } + } + } + TerminatorKind::Call { + func, + args, + destination: Some((dest_place, _)), + cleanup: _, + from_hir_call: _, + fn_span: _, + } => { + // No arguments may overlap with the destination. + for arg in args.iter().chain(Some(func)) { + if let Some(place) = arg.place() { + if !place.is_indirect() && !dest_place.is_indirect() { + self.record_local_conflict( + dest_place.local, + place.local, + "call dest/arg overlap", + ); + } + } + } + } + TerminatorKind::InlineAsm { + template: _, + operands, + options: _, + line_spans: _, + destination: _, + } => { + // The intended semantics here aren't documented, we just assume that nothing that + // could be written to by the assembly may overlap with any other operands. + for op in operands { + match op { + InlineAsmOperand::Out { reg: _, late: _, place: Some(dest_place) } + | InlineAsmOperand::InOut { + reg: _, + late: _, + in_value: _, + out_place: Some(dest_place), + } => { + // For output place `place`, add all places accessed by the inline asm. + for op in operands { + match op { + InlineAsmOperand::In { reg: _, value } => { + if let Some(p) = value.place() { + if !p.is_indirect() && !dest_place.is_indirect() { + self.record_local_conflict( + p.local, + dest_place.local, + "asm! operand overlap", + ); + } + } + } + InlineAsmOperand::Out { + reg: _, + late: _, + place: Some(place), + } => { + if !place.is_indirect() && !dest_place.is_indirect() { + self.record_local_conflict( + place.local, + dest_place.local, + "asm! operand overlap", + ); + } + } + InlineAsmOperand::InOut { + reg: _, + late: _, + in_value, + out_place, + } => { + if let Some(place) = in_value.place() { + if !place.is_indirect() && !dest_place.is_indirect() { + self.record_local_conflict( + place.local, + dest_place.local, + "asm! operand overlap", + ); + } + } + + if let Some(place) = out_place { + if !place.is_indirect() && !dest_place.is_indirect() { + self.record_local_conflict( + place.local, + dest_place.local, + "asm! operand overlap", + ); + } + } + } + InlineAsmOperand::Out { reg: _, late: _, place: None } + | InlineAsmOperand::Const { value: _ } + | InlineAsmOperand::SymFn { value: _ } + | InlineAsmOperand::SymStatic { def_id: _ } => {} + } + } + } + InlineAsmOperand::Const { value } => { + assert!(value.place().is_none()); + } + InlineAsmOperand::InOut { + reg: _, + late: _, + in_value: _, + out_place: None, + } + | InlineAsmOperand::In { reg: _, value: _ } + | InlineAsmOperand::Out { reg: _, late: _, place: None } + | InlineAsmOperand::SymFn { value: _ } + | InlineAsmOperand::SymStatic { def_id: _ } => {} + } + } + } + + TerminatorKind::Goto { .. } + | TerminatorKind::Call { destination: None, .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::Assert { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } => {} + } + } + + /// Checks whether `a` and `b` may be merged. Returns `false` if there's a conflict. + fn can_unify(&mut self, a: Local, b: Local) -> bool { + // After some locals have been unified, their conflicts are only tracked in the root key, + // so look that up. + let a = self.unified_locals.find(a).0; + let b = self.unified_locals.find(b).0; + + if a == b { + // Already merged (part of the same connected component). + return false; + } + + if self.matrix.contains(a, b) { + // Conflict (derived via dataflow, intra-statement conflicts, or inherited from another + // local during unification). + return false; + } + + true + } + + /// Merges the conflicts of `a` and `b`, so that each one inherits all conflicts of the other. + /// + /// `can_unify` must have returned `true` for the same locals, or this may panic or lead to + /// miscompiles. + /// + /// This is called when the pass makes the decision to unify `a` and `b` (or parts of `a` and + /// `b`) and is needed to ensure that future unification decisions take potentially newly + /// introduced conflicts into account. + /// + /// For an example, assume we have locals `_0`, `_1`, `_2`, and `_3`. There are these conflicts: + /// + /// * `_0` <-> `_1` + /// * `_1` <-> `_2` + /// * `_3` <-> `_0` + /// + /// We then decide to merge `_2` with `_3` since they don't conflict. Then we decide to merge + /// `_2` with `_0`, which also doesn't have a conflict in the above list. However `_2` is now + /// `_3`, which does conflict with `_0`. + fn unify(&mut self, a: Local, b: Local) { + trace!("unify({:?}, {:?})", a, b); + + // Get the root local of the connected components. The root local stores the conflicts of + // all locals in the connected component (and *is stored* as the conflicting local of other + // locals). + let a = self.unified_locals.find(a).0; + let b = self.unified_locals.find(b).0; + assert_ne!(a, b); + + trace!("roots: a={:?}, b={:?}", a, b); + trace!("{:?} conflicts: {:?}", a, self.matrix.iter(a).format(", ")); + trace!("{:?} conflicts: {:?}", b, self.matrix.iter(b).format(", ")); + + self.unified_locals.union(a, b); + + let root = self.unified_locals.find(a).0; + assert!(root == a || root == b); + + // Make all locals that conflict with `a` also conflict with `b`, and vice versa. + self.unify_cache.clear(); + for conflicts_with_a in self.matrix.iter(a) { + self.unify_cache.insert(conflicts_with_a); + } + for conflicts_with_b in self.matrix.iter(b) { + self.unify_cache.insert(conflicts_with_b); + } + for conflicts_with_a_or_b in self.unify_cache.iter() { + // Set both `a` and `b` for this local's row. + self.matrix.insert(conflicts_with_a_or_b, a); + self.matrix.insert(conflicts_with_a_or_b, b); + } + + // Write the locals `a` conflicts with to `b`'s row. + self.matrix.union_rows(a, b); + // Write the locals `b` conflicts with to `a`'s row. + self.matrix.union_rows(b, a); + } +} + +/// A `dest = {move} src;` statement at `loc`. +/// +/// We want to consider merging `dest` and `src` due to this assignment. +#[derive(Debug, Copy, Clone)] +struct CandidateAssignment<'tcx> { + /// Does not contain indirection or indexing (so the only local it contains is the place base). + dest: Place<'tcx>, + src: Local, + loc: Location, +} + +/// Scans the MIR for assignments between locals that we might want to consider merging. +/// +/// This will filter out assignments that do not match the right form (as described in the top-level +/// comment) and also throw out assignments that involve a local that has its address taken or is +/// otherwise ineligible (eg. locals used as array indices are ignored because we cannot propagate +/// arbitrary places into array indices). +fn find_candidates<'a, 'tcx>( + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, +) -> Vec<CandidateAssignment<'tcx>> { + let mut visitor = FindAssignments { + tcx, + body, + candidates: Vec::new(), + ever_borrowed_locals: ever_borrowed_locals(body), + locals_used_as_array_index: locals_used_as_array_index(body), + }; + visitor.visit_body(body); + visitor.candidates +} + +struct FindAssignments<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + candidates: Vec<CandidateAssignment<'tcx>>, + ever_borrowed_locals: BitSet<Local>, + locals_used_as_array_index: BitSet<Local>, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindAssignments<'a, 'tcx> { + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + if let StatementKind::Assign(box ( + dest, + Rvalue::Use(Operand::Copy(src) | Operand::Move(src)), + )) = &statement.kind + { + // `dest` must not have pointer indirection. + if dest.is_indirect() { + return; + } + + // `src` must be a plain local. + if !src.projection.is_empty() { + return; + } + + // Since we want to replace `src` with `dest`, `src` must not be required. + if is_local_required(src.local, self.body) { + return; + } + + // Can't optimize if both locals ever have their address taken (can introduce + // aliasing). + // FIXME: This can be smarter and take `StorageDead` into account (which + // invalidates borrows). + if self.ever_borrowed_locals.contains(dest.local) + && self.ever_borrowed_locals.contains(src.local) + { + return; + } + + assert_ne!(dest.local, src.local, "self-assignments are UB"); + + // We can't replace locals occurring in `PlaceElem::Index` for now. + if self.locals_used_as_array_index.contains(src.local) { + return; + } + + // Handle the "subtle case" described above by rejecting any `dest` that is or + // projects through a union. + let is_union = |ty: Ty<'_>| { + if let ty::Adt(def, _) = ty.kind() { + if def.is_union() { + return true; + } + } + + false + }; + let mut place_ty = PlaceTy::from_ty(self.body.local_decls[dest.local].ty); + if is_union(place_ty.ty) { + return; + } + for elem in dest.projection { + if let PlaceElem::Index(_) = elem { + // `dest` contains an indexing projection. + return; + } + + place_ty = place_ty.projection_ty(self.tcx, elem); + if is_union(place_ty.ty) { + return; + } + } + + self.candidates.push(CandidateAssignment { + dest: *dest, + src: src.local, + loc: location, + }); + } + } +} + +/// Some locals are part of the function's interface and can not be removed. +/// +/// Note that these locals *can* still be merged with non-required locals by removing that other +/// local. +fn is_local_required(local: Local, body: &Body<'_>) -> bool { + match body.local_kind(local) { + LocalKind::Arg | LocalKind::ReturnPointer => true, + LocalKind::Var | LocalKind::Temp => false, + } +} + +/// Walks MIR to find all locals that have their address taken anywhere. +fn ever_borrowed_locals(body: &Body<'_>) -> BitSet<Local> { + let mut visitor = BorrowCollector { locals: BitSet::new_empty(body.local_decls.len()) }; + visitor.visit_body(body); + visitor.locals +} + +struct BorrowCollector { + locals: BitSet<Local>, +} + +impl<'tcx> Visitor<'tcx> for BorrowCollector { + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + + match rvalue { + Rvalue::AddressOf(_, borrowed_place) | Rvalue::Ref(_, _, borrowed_place) => { + if !borrowed_place.is_indirect() { + self.locals.insert(borrowed_place.local); + } + } + + Rvalue::Cast(..) + | Rvalue::Use(..) + | Rvalue::Repeat(..) + | Rvalue::Len(..) + | Rvalue::BinaryOp(..) + | Rvalue::CheckedBinaryOp(..) + | Rvalue::NullaryOp(..) + | Rvalue::UnaryOp(..) + | Rvalue::Discriminant(..) + | Rvalue::Aggregate(..) + | Rvalue::ThreadLocalRef(..) => {} + } + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + self.super_terminator(terminator, location); + + match terminator.kind { + TerminatorKind::Drop { place: dropped_place, .. } + | TerminatorKind::DropAndReplace { place: dropped_place, .. } => { + self.locals.insert(dropped_place.local); + } + + TerminatorKind::Abort + | TerminatorKind::Assert { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Return + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Unreachable + | TerminatorKind::Yield { .. } + | TerminatorKind::InlineAsm { .. } => {} + } + } +} + +/// `PlaceElem::Index` only stores a `Local`, so we can't replace that with a full `Place`. +/// +/// Collect locals used as indices so we don't generate candidates that are impossible to apply +/// later. +fn locals_used_as_array_index(body: &Body<'_>) -> BitSet<Local> { + let mut visitor = IndexCollector { locals: BitSet::new_empty(body.local_decls.len()) }; + visitor.visit_body(body); + visitor.locals +} + +struct IndexCollector { + locals: BitSet<Local>, +} + +impl<'tcx> Visitor<'tcx> for IndexCollector { + fn visit_projection_elem( + &mut self, + local: Local, + proj_base: &[PlaceElem<'tcx>], + elem: PlaceElem<'tcx>, + context: PlaceContext, + location: Location, + ) { + if let PlaceElem::Index(i) = elem { + self.locals.insert(i); + } + self.super_projection_elem(local, proj_base, elem, context, location); + } +} diff --git a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs new file mode 100644 index 00000000000..67e679a8b08 --- /dev/null +++ b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs @@ -0,0 +1,339 @@ +use crate::{ + transform::{MirPass, MirSource}, + util::patch::MirPatch, +}; +use rustc_middle::mir::*; +use rustc_middle::ty::{Ty, TyCtxt}; +use std::{borrow::Cow, fmt::Debug}; + +use super::simplify::simplify_cfg; + +/// This pass optimizes something like +/// ```text +/// let x: Option<()>; +/// let y: Option<()>; +/// match (x,y) { +/// (Some(_), Some(_)) => {0}, +/// _ => {1} +/// } +/// ``` +/// into something like +/// ```text +/// let x: Option<()>; +/// let y: Option<()>; +/// let discriminant_x = // get discriminant of x +/// let discriminant_y = // get discriminant of y +/// if discriminant_x != discriminant_y || discriminant_x == None {1} else {0} +/// ``` +pub struct EarlyOtherwiseBranch; + +impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.opts.debugging_opts.mir_opt_level < 1 { + return; + } + trace!("running EarlyOtherwiseBranch on {:?}", source); + // we are only interested in this bb if the terminator is a switchInt + let bbs_with_switch = + body.basic_blocks().iter_enumerated().filter(|(_, bb)| is_switch(bb.terminator())); + + let opts_to_apply: Vec<OptimizationToApply<'tcx>> = bbs_with_switch + .flat_map(|(bb_idx, bb)| { + let switch = bb.terminator(); + let helper = Helper { body, tcx }; + let infos = helper.go(bb, switch)?; + Some(OptimizationToApply { infos, basic_block_first_switch: bb_idx }) + }) + .collect(); + + let should_cleanup = !opts_to_apply.is_empty(); + + for opt_to_apply in opts_to_apply { + trace!("SUCCESS: found optimization possibility to apply: {:?}", &opt_to_apply); + + let statements_before = + body.basic_blocks()[opt_to_apply.basic_block_first_switch].statements.len(); + let end_of_block_location = Location { + block: opt_to_apply.basic_block_first_switch, + statement_index: statements_before, + }; + + let mut patch = MirPatch::new(body); + + // create temp to store second discriminant in + let discr_type = opt_to_apply.infos[0].second_switch_info.discr_ty; + let discr_span = opt_to_apply.infos[0].second_switch_info.discr_source_info.span; + let second_discriminant_temp = patch.new_temp(discr_type, discr_span); + + patch.add_statement( + end_of_block_location, + StatementKind::StorageLive(second_discriminant_temp), + ); + + // create assignment of discriminant + let place_of_adt_to_get_discriminant_of = + opt_to_apply.infos[0].second_switch_info.place_of_adt_discr_read; + patch.add_assign( + end_of_block_location, + Place::from(second_discriminant_temp), + Rvalue::Discriminant(place_of_adt_to_get_discriminant_of), + ); + + // create temp to store NotEqual comparison between the two discriminants + let not_equal = BinOp::Ne; + let not_equal_res_type = not_equal.ty(tcx, discr_type, discr_type); + let not_equal_temp = patch.new_temp(not_equal_res_type, discr_span); + patch.add_statement(end_of_block_location, StatementKind::StorageLive(not_equal_temp)); + + // create NotEqual comparison between the two discriminants + let first_descriminant_place = + opt_to_apply.infos[0].first_switch_info.discr_used_in_switch; + let not_equal_rvalue = Rvalue::BinaryOp( + not_equal, + Operand::Copy(Place::from(second_discriminant_temp)), + Operand::Copy(Place::from(first_descriminant_place)), + ); + patch.add_statement( + end_of_block_location, + StatementKind::Assign(box (Place::from(not_equal_temp), not_equal_rvalue)), + ); + + let (mut targets_to_jump_to, values_to_jump_to): (Vec<_>, Vec<_>) = opt_to_apply + .infos + .iter() + .flat_map(|x| x.second_switch_info.targets_with_values.iter()) + .cloned() + .unzip(); + + // add otherwise case in the end + targets_to_jump_to.push(opt_to_apply.infos[0].first_switch_info.otherwise_bb); + // new block that jumps to the correct discriminant case. This block is switched to if the discriminants are equal + let new_switch_data = BasicBlockData::new(Some(Terminator { + source_info: opt_to_apply.infos[0].second_switch_info.discr_source_info, + kind: TerminatorKind::SwitchInt { + // the first and second discriminants are equal, so just pick one + discr: Operand::Copy(first_descriminant_place), + switch_ty: discr_type, + values: Cow::from(values_to_jump_to), + targets: targets_to_jump_to, + }, + })); + + let new_switch_bb = patch.new_block(new_switch_data); + + // switch on the NotEqual. If true, then jump to the `otherwise` case. + // If false, then jump to a basic block that then jumps to the correct disciminant case + let true_case = opt_to_apply.infos[0].first_switch_info.otherwise_bb; + let false_case = new_switch_bb; + patch.patch_terminator( + opt_to_apply.basic_block_first_switch, + TerminatorKind::if_( + tcx, + Operand::Move(Place::from(not_equal_temp)), + true_case, + false_case, + ), + ); + + // generate StorageDead for the second_discriminant_temp not in use anymore + patch.add_statement( + end_of_block_location, + StatementKind::StorageDead(second_discriminant_temp), + ); + + // Generate a StorageDead for not_equal_temp in each of the targets, since we moved it into the switch + for bb in [false_case, true_case].iter() { + patch.add_statement( + Location { block: *bb, statement_index: 0 }, + StatementKind::StorageDead(not_equal_temp), + ); + } + + patch.apply(body); + } + + // Since this optimization adds new basic blocks and invalidates others, + // clean up the cfg to make it nicer for other passes + if should_cleanup { + simplify_cfg(body); + } + } +} + +fn is_switch<'tcx>(terminator: &Terminator<'tcx>) -> bool { + match terminator.kind { + TerminatorKind::SwitchInt { .. } => true, + _ => false, + } +} + +struct Helper<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, +} + +#[derive(Debug, Clone)] +struct SwitchDiscriminantInfo<'tcx> { + /// Type of the discriminant being switched on + discr_ty: Ty<'tcx>, + /// The basic block that the otherwise branch points to + otherwise_bb: BasicBlock, + /// Target along with the value being branched from. Otherwise is not included + targets_with_values: Vec<(BasicBlock, u128)>, + discr_source_info: SourceInfo, + /// The place of the discriminant used in the switch + discr_used_in_switch: Place<'tcx>, + /// The place of the adt that has its discriminant read + place_of_adt_discr_read: Place<'tcx>, + /// The type of the adt that has its discriminant read + type_adt_matched_on: Ty<'tcx>, +} + +#[derive(Debug)] +struct OptimizationToApply<'tcx> { + infos: Vec<OptimizationInfo<'tcx>>, + /// Basic block of the original first switch + basic_block_first_switch: BasicBlock, +} + +#[derive(Debug)] +struct OptimizationInfo<'tcx> { + /// Info about the first switch and discriminant + first_switch_info: SwitchDiscriminantInfo<'tcx>, + /// Info about the second switch and discriminant + second_switch_info: SwitchDiscriminantInfo<'tcx>, +} + +impl<'a, 'tcx> Helper<'a, 'tcx> { + pub fn go( + &self, + bb: &BasicBlockData<'tcx>, + switch: &Terminator<'tcx>, + ) -> Option<Vec<OptimizationInfo<'tcx>>> { + // try to find the statement that defines the discriminant that is used for the switch + let discr = self.find_switch_discriminant_info(bb, switch)?; + + // go through each target, finding a discriminant read, and a switch + let results = discr.targets_with_values.iter().map(|(target, value)| { + self.find_discriminant_switch_pairing(&discr, target.clone(), value.clone()) + }); + + // if the optimization did not apply for one of the targets, then abort + if results.clone().any(|x| x.is_none()) || results.len() == 0 { + trace!("NO: not all of the targets matched the pattern for optimization"); + return None; + } + + Some(results.flatten().collect()) + } + + fn find_discriminant_switch_pairing( + &self, + discr_info: &SwitchDiscriminantInfo<'tcx>, + target: BasicBlock, + value: u128, + ) -> Option<OptimizationInfo<'tcx>> { + let bb = &self.body.basic_blocks()[target]; + // find switch + let terminator = bb.terminator(); + if is_switch(terminator) { + let this_bb_discr_info = self.find_switch_discriminant_info(bb, terminator)?; + + // the types of the two adts matched on have to be equalfor this optimization to apply + if discr_info.type_adt_matched_on != this_bb_discr_info.type_adt_matched_on { + trace!( + "NO: types do not match. LHS: {:?}, RHS: {:?}", + discr_info.type_adt_matched_on, + this_bb_discr_info.type_adt_matched_on + ); + return None; + } + + // the otherwise branch of the two switches have to point to the same bb + if discr_info.otherwise_bb != this_bb_discr_info.otherwise_bb { + trace!("NO: otherwise target is not the same"); + return None; + } + + // check that the value being matched on is the same. The + if this_bb_discr_info.targets_with_values.iter().find(|x| x.1 == value).is_none() { + trace!("NO: values being matched on are not the same"); + return None; + } + + // only allow optimization if the left and right of the tuple being matched are the same variants. + // so the following should not optimize + // ```rust + // let x: Option<()>; + // let y: Option<()>; + // match (x,y) { + // (Some(_), None) => {}, + // _ => {} + // } + // ``` + // We check this by seeing that the value of the first discriminant is the only other discriminant value being used as a target in the second switch + if !(this_bb_discr_info.targets_with_values.len() == 1 + && this_bb_discr_info.targets_with_values[0].1 == value) + { + trace!( + "NO: The second switch did not have only 1 target (besides otherwise) that had the same value as the value from the first switch that got us here" + ); + return None; + } + + // if we reach this point, the optimization applies, and we should be able to optimize this case + // store the info that is needed to apply the optimization + + Some(OptimizationInfo { + first_switch_info: discr_info.clone(), + second_switch_info: this_bb_discr_info, + }) + } else { + None + } + } + + fn find_switch_discriminant_info( + &self, + bb: &BasicBlockData<'tcx>, + switch: &Terminator<'tcx>, + ) -> Option<SwitchDiscriminantInfo<'tcx>> { + match &switch.kind { + TerminatorKind::SwitchInt { discr, targets, values, .. } => { + let discr_local = discr.place()?.as_local()?; + // the declaration of the discriminant read. Place of this read is being used in the switch + let discr_decl = &self.body.local_decls()[discr_local]; + let discr_ty = discr_decl.ty; + // the otherwise target lies as the last element + let otherwise_bb = targets.get(values.len())?.clone(); + let targets_with_values = targets + .iter() + .zip(values.iter()) + .map(|(t, v)| (t.clone(), v.clone())) + .collect(); + + // find the place of the adt where the discriminant is being read from + // assume this is the last statement of the block + let place_of_adt_discr_read = match bb.statements.last()?.kind { + StatementKind::Assign(box (_, Rvalue::Discriminant(adt_place))) => { + Some(adt_place) + } + _ => None, + }?; + + let type_adt_matched_on = place_of_adt_discr_read.ty(self.body, self.tcx).ty; + + Some(SwitchDiscriminantInfo { + discr_used_in_switch: discr.place()?, + discr_ty, + otherwise_bb, + targets_with_values, + discr_source_info: discr_decl.source_info, + place_of_adt_discr_read, + type_adt_matched_on, + }) + } + _ => unreachable!("must only be passed terminator that is a switch"), + } + } +} diff --git a/compiler/rustc_mir/src/transform/elaborate_drops.rs b/compiler/rustc_mir/src/transform/elaborate_drops.rs index 5f193069356..a8b2ee5705f 100644 --- a/compiler/rustc_mir/src/transform/elaborate_drops.rs +++ b/compiler/rustc_mir/src/transform/elaborate_drops.rs @@ -44,6 +44,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { let inits = MaybeInitializedPlaces::new(tcx, body, &env) .into_engine(tcx, body, def_id) .dead_unwinds(&dead_unwinds) + .pass_name("elaborate_drops") .iterate_to_fixpoint() .into_results_cursor(body); @@ -51,6 +52,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { .mark_inactive_variants_as_uninit() .into_engine(tcx, body, def_id) .dead_unwinds(&dead_unwinds) + .pass_name("elaborate_drops") .iterate_to_fixpoint() .into_results_cursor(body); @@ -83,6 +85,7 @@ fn find_dead_unwinds<'tcx>( let mut dead_unwinds = BitSet::new_empty(body.basic_blocks().len()); let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &env) .into_engine(tcx, body, def_id) + .pass_name("find_dead_unwinds") .iterate_to_fixpoint() .into_results_cursor(body); for (bb, bb_data) in body.basic_blocks().iter_enumerated() { diff --git a/compiler/rustc_mir/src/transform/generator.rs b/compiler/rustc_mir/src/transform/generator.rs index a22075e760a..1fffcf81515 100644 --- a/compiler/rustc_mir/src/transform/generator.rs +++ b/compiler/rustc_mir/src/transform/generator.rs @@ -467,8 +467,10 @@ fn locals_live_across_suspend_points( // Calculate the MIR locals which have been previously // borrowed (even if they are still active). - let borrowed_locals_results = - MaybeBorrowedLocals::all_borrows().into_engine(tcx, body_ref, def_id).iterate_to_fixpoint(); + let borrowed_locals_results = MaybeBorrowedLocals::all_borrows() + .into_engine(tcx, body_ref, def_id) + .pass_name("generator") + .iterate_to_fixpoint(); let mut borrowed_locals_cursor = dataflow::ResultsCursor::new(body_ref, &borrowed_locals_results); @@ -484,6 +486,7 @@ fn locals_live_across_suspend_points( // Calculate the liveness of MIR locals ignoring borrows. let mut liveness = MaybeLiveLocals .into_engine(tcx, body_ref, def_id) + .pass_name("generator") .iterate_to_fixpoint() .into_results_cursor(body_ref); @@ -726,12 +729,12 @@ fn sanitize_witness<'tcx>( saved_locals: &GeneratorSavedLocals, ) { let allowed_upvars = tcx.erase_regions(upvars); - let allowed = match witness.kind { + let allowed = match witness.kind() { ty::GeneratorWitness(s) => tcx.erase_late_bound_regions(&s), _ => { tcx.sess.delay_span_bug( body.span, - &format!("unexpected generator witness type {:?}", witness.kind), + &format!("unexpected generator witness type {:?}", witness.kind()), ); return; } @@ -1252,7 +1255,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { let gen_ty = body.local_decls.raw[1].ty; // Get the interior types and substs which typeck computed - let (upvars, interior, discr_ty, movable) = match gen_ty.kind { + let (upvars, interior, discr_ty, movable) = match *gen_ty.kind() { ty::Generator(_, substs, movability) => { let substs = substs.as_generator(); ( diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 315d4fa9d47..4e7cacc2f4a 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -4,7 +4,7 @@ use rustc_attr as attr; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -45,7 +45,8 @@ impl<'tcx> MirPass<'tcx> for Inline { // based function. debug!("function inlining is disabled when compiling with `instrument_coverage`"); } else { - Inliner { tcx, source }.run_pass(body); + Inliner { tcx, source, codegen_fn_attrs: tcx.codegen_fn_attrs(source.def_id()) } + .run_pass(body); } } } @@ -54,6 +55,7 @@ impl<'tcx> MirPass<'tcx> for Inline { struct Inliner<'tcx> { tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, + codegen_fn_attrs: &'tcx CodegenFnAttrs, } impl Inliner<'tcx> { @@ -107,8 +109,14 @@ impl Inliner<'tcx> { // Avoid a cycle here by only using `optimized_mir` only if we have // a lower `HirId` than the callee. This ensures that the callee will // not inline us. This trick only works without incremental compilation. - // So don't do it if that is enabled. - if !self.tcx.dep_graph.is_fully_enabled() && self_hir_id < callee_hir_id { + // So don't do it if that is enabled. Also avoid inlining into generators, + // since their `optimized_mir` is used for layout computation, which can + // create a cycle, even when no attempt is made to inline the function + // in the other direction. + if !self.tcx.dep_graph.is_fully_enabled() + && self_hir_id < callee_hir_id + && caller_body.generator_kind.is_none() + { self.tcx.optimized_mir(callsite.callee) } else { continue; @@ -191,7 +199,7 @@ impl Inliner<'tcx> { // Only consider direct calls to functions let terminator = bb_data.terminator(); if let TerminatorKind::Call { func: ref op, .. } = terminator.kind { - if let ty::FnDef(callee_def_id, substs) = op.ty(caller_body, self.tcx).kind { + if let ty::FnDef(callee_def_id, substs) = *op.ty(caller_body, self.tcx).kind() { let instance = Instance::resolve(self.tcx, param_env, callee_def_id, substs).ok().flatten()?; @@ -236,9 +244,19 @@ impl Inliner<'tcx> { return false; } - // Avoid inlining functions marked as no_sanitize if sanitizer is enabled, - // since instrumentation might be enabled and performed on the caller. - if self.tcx.sess.opts.debugging_opts.sanitizer.intersects(codegen_fn_attrs.no_sanitize) { + let self_features = &self.codegen_fn_attrs.target_features; + let callee_features = &codegen_fn_attrs.target_features; + if callee_features.iter().any(|feature| !self_features.contains(feature)) { + debug!("`callee has extra target features - not inlining"); + return false; + } + + let self_no_sanitize = + self.codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; + let callee_no_sanitize = + codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; + if self_no_sanitize != callee_no_sanitize { + debug!("`callee has incompatible no_sanitize attribute - not inlining"); return false; } @@ -336,7 +354,7 @@ impl Inliner<'tcx> { } TerminatorKind::Call { func: Operand::Constant(ref f), cleanup, .. } => { - if let ty::FnDef(def_id, _) = f.literal.ty.kind { + if let ty::FnDef(def_id, _) = *f.literal.ty.kind() { // Don't give intrinsics the extra penalty for calls let f = tcx.fn_sig(def_id); if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { @@ -488,7 +506,7 @@ impl Inliner<'tcx> { let return_block = destination.1; // Copy the arguments if needed. - let args: Vec<_> = self.make_call_args(args, &callsite, caller_body); + let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, return_block); let bb_len = caller_body.basic_blocks().len(); let mut integrator = Integrator { @@ -535,6 +553,7 @@ impl Inliner<'tcx> { args: Vec<Operand<'tcx>>, callsite: &CallSite<'tcx>, caller_body: &mut Body<'tcx>, + return_block: BasicBlock, ) -> Vec<Local> { let tcx = self.tcx; @@ -563,12 +582,22 @@ impl Inliner<'tcx> { // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. if tcx.is_closure(callsite.callee) { let mut args = args.into_iter(); - let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); - let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); + let self_ = self.create_temp_if_necessary( + args.next().unwrap(), + callsite, + caller_body, + return_block, + ); + let tuple = self.create_temp_if_necessary( + args.next().unwrap(), + callsite, + caller_body, + return_block, + ); 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 { + let tuple_tys = if let ty::Tuple(s) = tuple.ty(caller_body, tcx).ty.kind() { s } else { bug!("Closure arguments are not passed as a tuple"); @@ -584,13 +613,13 @@ impl Inliner<'tcx> { Operand::Move(tcx.mk_place_field(tuple, Field::new(i), ty.expect_ty())); // Spill to a local to make e.g., `tmp0`. - self.create_temp_if_necessary(tuple_field, callsite, caller_body) + self.create_temp_if_necessary(tuple_field, callsite, caller_body, return_block) }); closure_ref_arg.chain(tuple_tmp_args).collect() } else { args.into_iter() - .map(|a| self.create_temp_if_necessary(a, callsite, caller_body)) + .map(|a| self.create_temp_if_necessary(a, callsite, caller_body, return_block)) .collect() } } @@ -602,6 +631,7 @@ impl Inliner<'tcx> { arg: Operand<'tcx>, callsite: &CallSite<'tcx>, caller_body: &mut Body<'tcx>, + return_block: BasicBlock, ) -> Local { // FIXME: Analysis of the usage of the arguments to avoid // unnecessary temporaries. @@ -624,11 +654,19 @@ impl Inliner<'tcx> { let arg_tmp = LocalDecl::new(ty, callsite.location.span); let arg_tmp = caller_body.local_decls.push(arg_tmp); - let stmt = Statement { + caller_body[callsite.bb].statements.push(Statement { + source_info: callsite.location, + kind: StatementKind::StorageLive(arg_tmp), + }); + caller_body[callsite.bb].statements.push(Statement { source_info: callsite.location, kind: StatementKind::Assign(box (Place::from(arg_tmp), arg)), - }; - caller_body[callsite.bb].statements.push(stmt); + }); + caller_body[return_block].statements.insert( + 0, + Statement { source_info: callsite.location, kind: StatementKind::StorageDead(arg_tmp) }, + ); + arg_tmp } } diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs index 7967137e01e..3ed0aea1404 100644 --- a/compiler/rustc_mir/src/transform/instcombine.rs +++ b/compiler/rustc_mir/src/transform/instcombine.rs @@ -4,9 +4,14 @@ use crate::transform::{MirPass, MirSource}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::Mutability; use rustc_index::vec::Idx; -use rustc_middle::mir::visit::{MutVisitor, Visitor}; use rustc_middle::mir::{ - Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, + visit::PlaceContext, + visit::{MutVisitor, Visitor}, + Statement, +}; +use rustc_middle::mir::{ + BinOp, Body, BorrowKind, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, + Rvalue, }; use rustc_middle::ty::{self, TyCtxt}; use std::mem; @@ -66,10 +71,41 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { *rvalue = Rvalue::Use(Operand::Constant(box constant)); } + if let Some(operand) = self.optimizations.unneeded_equality_comparison.remove(&location) { + debug!("replacing {:?} with {:?}", rvalue, operand); + *rvalue = Rvalue::Use(operand); + } + + if let Some(place) = self.optimizations.unneeded_deref.remove(&location) { + debug!("unneeded_deref: replacing {:?} with {:?}", rvalue, place); + *rvalue = Rvalue::Use(Operand::Copy(place)); + } + self.super_rvalue(rvalue, location) } } +struct MutatingUseVisitor { + has_mutating_use: bool, + local_to_look_for: Local, +} + +impl MutatingUseVisitor { + fn has_mutating_use_in_stmt(local: Local, stmt: &Statement<'tcx>, location: Location) -> bool { + let mut _self = Self { has_mutating_use: false, local_to_look_for: local }; + _self.visit_statement(stmt, location); + _self.has_mutating_use + } +} + +impl<'tcx> Visitor<'tcx> for MutatingUseVisitor { + fn visit_local(&mut self, local: &Local, context: PlaceContext, _: Location) { + if *local == self.local_to_look_for { + self.has_mutating_use |= context.is_mutating_use(); + } + } +} + /// Finds optimization opportunities on the MIR. struct OptimizationFinder<'b, 'tcx> { body: &'b Body<'tcx>, @@ -81,6 +117,127 @@ impl OptimizationFinder<'b, 'tcx> { fn new(body: &'b Body<'tcx>, tcx: TyCtxt<'tcx>) -> OptimizationFinder<'b, 'tcx> { OptimizationFinder { body, tcx, optimizations: OptimizationList::default() } } + + fn find_deref_of_address(&mut self, rvalue: &Rvalue<'tcx>, location: Location) -> Option<()> { + // Look for the sequence + // + // _2 = &_1; + // ... + // _5 = (*_2); + // + // which we can replace the last statement with `_5 = _1;` to avoid the load of `_2`. + if let Rvalue::Use(op) = rvalue { + let local_being_derefed = match op.place()?.as_ref() { + PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), + _ => None, + }?; + + let stmt_index = location.statement_index; + // Look behind for statement that assigns the local from a address of operator. + // 6 is chosen as a heuristic determined by seeing the number of times + // the optimization kicked in compiling rust std. + let lower_index = stmt_index.saturating_sub(6); + let statements_to_look_in = self.body.basic_blocks()[location.block].statements + [lower_index..stmt_index] + .iter() + .rev(); + for stmt in statements_to_look_in { + match &stmt.kind { + // Exhaustive match on statements to detect conditions that warrant we bail out of the optimization. + rustc_middle::mir::StatementKind::Assign(box (l, r)) + if l.local == local_being_derefed => + { + match r { + // Looking for immutable reference e.g _local_being_deref = &_1; + Rvalue::Ref( + _, + // Only apply the optimization if it is an immutable borrow. + BorrowKind::Shared, + place_taken_address_of, + ) => { + self.optimizations + .unneeded_deref + .insert(location, *place_taken_address_of); + return Some(()); + } + + // We found an assignment of `local_being_deref` that is not an immutable ref, e.g the following sequence + // _2 = &_1; + // _3 = &5 + // _2 = _3; <-- this means it is no longer valid to replace the last statement with `_5 = _1;` + // _5 = (*_2); + _ => return None, + } + } + + // Inline asm can do anything, so bail out of the optimization. + rustc_middle::mir::StatementKind::LlvmInlineAsm(_) => return None, + + // Check that `local_being_deref` is not being used in a mutating way which can cause misoptimization. + rustc_middle::mir::StatementKind::Assign(box (_, _)) + | rustc_middle::mir::StatementKind::Coverage(_) + | rustc_middle::mir::StatementKind::Nop + | rustc_middle::mir::StatementKind::FakeRead(_, _) + | rustc_middle::mir::StatementKind::StorageLive(_) + | rustc_middle::mir::StatementKind::StorageDead(_) + | rustc_middle::mir::StatementKind::Retag(_, _) + | rustc_middle::mir::StatementKind::AscribeUserType(_, _) + | rustc_middle::mir::StatementKind::SetDiscriminant { .. } => { + if MutatingUseVisitor::has_mutating_use_in_stmt( + local_being_derefed, + stmt, + location, + ) { + return None; + } + } + } + } + } + Some(()) + } + + fn find_unneeded_equality_comparison(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + // find Ne(_place, false) or Ne(false, _place) + // or Eq(_place, true) or Eq(true, _place) + if let Rvalue::BinaryOp(op, l, r) = rvalue { + let const_to_find = if *op == BinOp::Ne { + false + } else if *op == BinOp::Eq { + true + } else { + return; + }; + // (const, _place) + if let Some(o) = self.find_operand_in_equality_comparison_pattern(l, r, const_to_find) { + self.optimizations.unneeded_equality_comparison.insert(location, o.clone()); + } + // (_place, const) + else if let Some(o) = + self.find_operand_in_equality_comparison_pattern(r, l, const_to_find) + { + self.optimizations.unneeded_equality_comparison.insert(location, o.clone()); + } + } + } + + fn find_operand_in_equality_comparison_pattern( + &self, + l: &Operand<'tcx>, + r: &'a Operand<'tcx>, + const_to_find: bool, + ) -> Option<&'a Operand<'tcx>> { + let const_ = l.constant()?; + if const_.literal.ty == self.tcx.types.bool + && const_.literal.val.try_to_bool() == Some(const_to_find) + { + if r.place().is_some() { + return Some(r); + } + } + + None + } } impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { @@ -91,7 +248,7 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { { // The dereferenced place must have type `&_`. let ty = Place::ty_from(local, proj_base, self.body, self.tcx).ty; - if let ty::Ref(_, _, Mutability::Not) = ty.kind { + if let ty::Ref(_, _, Mutability::Not) = ty.kind() { self.optimizations.and_stars.insert(location); } } @@ -99,13 +256,17 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { if let Rvalue::Len(ref place) = *rvalue { let place_ty = place.ty(&self.body.local_decls, self.tcx).ty; - if let ty::Array(_, len) = place_ty.kind { + if let ty::Array(_, len) = place_ty.kind() { let span = self.body.source_info(location).span; let constant = Constant { span, literal: len, user_ty: None }; self.optimizations.arrays_lengths.insert(location, constant); } } + let _ = self.find_deref_of_address(rvalue, location); + + self.find_unneeded_equality_comparison(rvalue, location); + self.super_rvalue(rvalue, location) } } @@ -114,4 +275,6 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { struct OptimizationList<'tcx> { and_stars: FxHashSet<Location>, arrays_lengths: FxHashMap<Location, Constant<'tcx>>, + unneeded_equality_comparison: FxHashMap<Location, Operand<'tcx>>, + unneeded_deref: FxHashMap<Location, Place<'tcx>>, } diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs index f60e6da714a..a5b30a25a9b 100644 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs @@ -1,23 +1,34 @@ use crate::transform::{MirPass, MirSource}; +use crate::util::pretty; +use crate::util::spanview::{ + source_range_no_file, statement_kind_name, terminator_kind_name, write_spanview_document, + SpanViewable, TOOLTIP_INDENT, +}; + use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_index::bit_set::BitSet; use rustc_middle::hir; use rustc_middle::ich::StableHashingContext; use rustc_middle::mir; use rustc_middle::mir::coverage::*; use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{BasicBlock, Coverage, CoverageInfo, Location, Statement, StatementKind}; +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, Coverage, CoverageInfo, Location, Statement, StatementKind, + TerminatorKind, +}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::{FileName, Pos, RealFileName, Span, Symbol}; -/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with -/// the intrinsic llvm.instrprof.increment. +/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected +/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen +/// to construct the coverage map. pub struct InstrumentCoverage; -/// The `query` provider for `CoverageInfo`, requested by `codegen_intrinsic_call()` when -/// constructing the arguments for `llvm.instrprof.increment`. +/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each +/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). pub(crate) fn provide(providers: &mut Providers) { providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); } @@ -43,8 +54,8 @@ impl Visitor<'_> for CoverageVisitor { } } -fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, mir_def_id: DefId) -> CoverageInfo { - let mir_body = tcx.optimized_mir(mir_def_id); +fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { + let mir_body = tcx.optimized_mir(def_id); // The `num_counters` argument to `llvm.instrprof.increment` is the number of injected // counters, with each counter having a counter ID from `0..num_counters-1`. MIR optimization @@ -63,18 +74,30 @@ fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, mir_def_id: DefId) -> Coverage } impl<'tcx> MirPass<'tcx> for InstrumentCoverage { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, mir_body: &mut mir::Body<'tcx>) { + fn run_pass( + &self, + tcx: TyCtxt<'tcx>, + mir_source: MirSource<'tcx>, + mir_body: &mut mir::Body<'tcx>, + ) { // If the InstrumentCoverage pass is called on promoted MIRs, skip them. // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 - if src.promoted.is_none() { - Instrumentor::new(tcx, src, mir_body).inject_counters(); + if mir_source.promoted.is_none() { + Instrumentor::new(&self.name(), tcx, mir_source, mir_body).inject_counters(); } } } +#[derive(Clone)] +struct CoverageRegion { + pub span: Span, + pub blocks: Vec<BasicBlock>, +} + struct Instrumentor<'a, 'tcx> { + pass_name: &'a str, tcx: TyCtxt<'tcx>, - mir_def_id: DefId, + mir_source: MirSource<'tcx>, mir_body: &'a mut mir::Body<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>, function_source_hash: Option<u64>, @@ -83,12 +106,17 @@ struct Instrumentor<'a, 'tcx> { } impl<'a, 'tcx> Instrumentor<'a, 'tcx> { - fn new(tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self { - let mir_def_id = src.def_id(); - let hir_body = hir_body(tcx, mir_def_id); + fn new( + pass_name: &'a str, + tcx: TyCtxt<'tcx>, + mir_source: MirSource<'tcx>, + mir_body: &'a mut mir::Body<'tcx>, + ) -> Self { + let hir_body = hir_body(tcx, mir_source.def_id()); Self { + pass_name, tcx, - mir_def_id, + mir_source, mir_body, hir_body, function_source_hash: None, @@ -127,19 +155,100 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { } fn inject_counters(&mut self) { + let tcx = self.tcx; + let def_id = self.mir_source.def_id(); + let mir_body = &self.mir_body; let body_span = self.hir_body.value.span; - debug!("instrumenting {:?}, span: {:?}", self.mir_def_id, body_span); - - // FIXME(richkadel): As a first step, counters are only injected at the top of each - // function. The complete solution will inject counters at each conditional code branch. - let block = rustc_middle::mir::START_BLOCK; - let counter = self.make_counter(); - self.inject_statement(counter, body_span, block); - - // FIXME(richkadel): The next step to implement source based coverage analysis will be - // instrumenting branches within functions, and some regions will be counted by "counter - // expression". The function to inject counter expression is implemented. Replace this - // "fake use" with real use. + debug!( + "instrumenting {:?}, span: {}", + def_id, + tcx.sess.source_map().span_to_string(body_span) + ); + + if !tcx.sess.opts.debugging_opts.experimental_coverage { + // Coverage at the function level should be accurate. This is the default implementation + // if `-Z experimental-coverage` is *NOT* enabled. + let block = rustc_middle::mir::START_BLOCK; + let counter = self.make_counter(); + self.inject_statement(counter, body_span, block); + return; + } + // FIXME(richkadel): else if `-Z experimental-coverage` *IS* enabled: Efforts are still in + // progress to identify the correct code region spans and associated counters to generate + // accurate Rust coverage reports. + + let block_span = |data: &BasicBlockData<'tcx>| { + // The default span will be the `Terminator` span; but until we have a smarter solution, + // the coverage region also incorporates at least the statements in this BasicBlock as + // well. Extend the span to encompass all, if possible. + // FIXME(richkadel): Assuming the terminator's span is already known to be contained in `body_span`. + let mut span = data.terminator().source_info.span; + // FIXME(richkadel): It's looking unlikely that we should compute a span from MIR + // spans, but if we do keep something like this logic, we will need a smarter way + // to combine `Statement`s and/or `Terminator`s with `Span`s from different + // files. + for statement_span in data.statements.iter().map(|statement| statement.source_info.span) + { + // Only combine Spans from the function's body_span. + if body_span.contains(statement_span) { + span = span.to(statement_span); + } + } + span + }; + + // Traverse the CFG but ignore anything following an `unwind` + let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| { + let mut successors = term_kind.successors(); + match &term_kind { + // SwitchInt successors are never unwind, and all of them should be traversed + TerminatorKind::SwitchInt { .. } => successors, + // For all other kinds, return only the first successor, if any, and ignore unwinds + _ => successors.next().into_iter().chain(&[]), + } + }); + + let mut coverage_regions = Vec::with_capacity(cfg_without_unwind.size_hint().0); + for (bb, data) in cfg_without_unwind { + if !body_span.contains(data.terminator().source_info.span) { + continue; + } + + // FIXME(richkadel): Regions will soon contain multiple blocks. + let mut blocks = Vec::new(); + blocks.push(bb); + let span = block_span(data); + coverage_regions.push(CoverageRegion { span, blocks }); + } + + let span_viewables = if pretty::dump_enabled(tcx, self.pass_name, def_id) { + Some(self.span_viewables(&coverage_regions)) + } else { + None + }; + + // Inject counters for the selected spans + for CoverageRegion { span, blocks } in coverage_regions { + debug!( + "Injecting counter at: {:?}:\n{}\n==========", + span, + tcx.sess.source_map().span_to_snippet(span).expect("Error getting source for span"), + ); + let counter = self.make_counter(); + self.inject_statement(counter, span, blocks[0]); + } + + if let Some(span_viewables) = span_viewables { + let mut file = + pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, self.mir_source) + .expect("Unexpected error creating MIR spanview HTML file"); + write_spanview_document(tcx, def_id, span_viewables, &mut file) + .expect("Unexpected IO error dumping coverage spans as HTML"); + } + + // FIXME(richkadel): Some regions will be counted by "counter expression". Counter + // expressions are supported, but are not yet generated. When they are, remove this `fake_use` + // block. let fake_use = false; if fake_use { let add = false; @@ -193,6 +302,83 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { }; data.statements.push(statement); } + + /// Converts the computed `CoverageRegion`s into `SpanViewable`s. + fn span_viewables(&self, coverage_regions: &Vec<CoverageRegion>) -> Vec<SpanViewable> { + let mut span_viewables = Vec::new(); + for coverage_region in coverage_regions { + span_viewables.push(SpanViewable { + span: coverage_region.span, + id: format!("{}", coverage_region.blocks[0].index()), + tooltip: self.make_tooltip_text(coverage_region), + }); + } + span_viewables + } + + /// A custom tooltip renderer used in a spanview HTML+CSS document used for coverage analysis. + fn make_tooltip_text(&self, coverage_region: &CoverageRegion) -> String { + const INCLUDE_COVERAGE_STATEMENTS: bool = false; + let tcx = self.tcx; + let source_map = tcx.sess.source_map(); + let mut text = Vec::new(); + for (i, &bb) in coverage_region.blocks.iter().enumerate() { + if i > 0 { + text.push("\n".to_owned()); + } + text.push(format!("{:?}: {}:", bb, &source_map.span_to_string(coverage_region.span))); + let data = &self.mir_body.basic_blocks()[bb]; + for statement in &data.statements { + let statement_string = match statement.kind { + StatementKind::Coverage(box ref coverage) => match coverage.kind { + CoverageKind::Counter { id, .. } => { + if !INCLUDE_COVERAGE_STATEMENTS { + continue; + } + format!("increment counter #{}", id.index()) + } + CoverageKind::Expression { id, lhs, op, rhs } => { + if !INCLUDE_COVERAGE_STATEMENTS { + continue; + } + format!( + "expression #{} = {} {} {}", + id.index(), + lhs.index(), + if op == Op::Add { "+" } else { "-" }, + rhs.index() + ) + } + CoverageKind::Unreachable => { + if !INCLUDE_COVERAGE_STATEMENTS { + continue; + } + String::from("unreachable") + } + }, + _ => format!("{:?}", statement), + }; + let source_range = source_range_no_file(tcx, &statement.source_info.span); + text.push(format!( + "\n{}{}: {}: {}", + TOOLTIP_INDENT, + source_range, + statement_kind_name(statement), + statement_string + )); + } + let term = data.terminator(); + let source_range = source_range_no_file(tcx, &term.source_info.span); + text.push(format!( + "\n{}{}: {}: {:?}", + TOOLTIP_INDENT, + source_range, + terminator_kind_name(term), + term.kind + )); + } + text.join("") + } } /// Convert the Span into its file name, start line and column, and end line and column @@ -227,7 +413,7 @@ fn make_code_region<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> CodeRegion { } fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { - let hir_node = tcx.hir().get_if_local(def_id).expect("DefId is local"); + let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local"); let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); tcx.hir().body(fn_body_id) } @@ -245,3 +431,61 @@ fn hash( node.hash_stable(hcx, &mut stable_hasher); stable_hasher.finish() } + +pub struct ShortCircuitPreorder< + 'a, + 'tcx, + F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>, +> { + body: &'a mir::Body<'tcx>, + visited: BitSet<BasicBlock>, + worklist: Vec<BasicBlock>, + filtered_successors: F, +} + +impl<'a, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> + ShortCircuitPreorder<'a, 'tcx, F> +{ + pub fn new( + body: &'a mir::Body<'tcx>, + filtered_successors: F, + ) -> ShortCircuitPreorder<'a, 'tcx, F> { + let worklist = vec![mir::START_BLOCK]; + + ShortCircuitPreorder { + body, + visited: BitSet::new_empty(body.basic_blocks().len()), + worklist, + filtered_successors, + } + } +} + +impl<'a: 'tcx, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> Iterator + for ShortCircuitPreorder<'a, 'tcx, F> +{ + type Item = (BasicBlock, &'a BasicBlockData<'tcx>); + + fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> { + while let Some(idx) = self.worklist.pop() { + if !self.visited.insert(idx) { + continue; + } + + let data = &self.body[idx]; + + if let Some(ref term) = data.terminator { + self.worklist.extend((self.filtered_successors)(&term.kind)); + } + + return Some((idx, data)); + } + + None + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let size = self.body.basic_blocks().len() - self.visited.count(); + (size, Some(size)) + } +} diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index c3a34756122..f48ad039b4f 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -16,6 +16,7 @@ use std::borrow::Cow; pub mod add_call_guards; pub mod add_moves_for_packed_drops; pub mod add_retag; +pub mod check_const_item_mutation; pub mod check_consts; pub mod check_packed_ref; pub mod check_unsafety; @@ -23,7 +24,9 @@ pub mod cleanup_post_borrowck; pub mod const_prop; pub mod copy_prop; pub mod deaggregator; +pub mod dest_prop; pub mod dump_mir; +pub mod early_otherwise_branch; pub mod elaborate_drops; pub mod generator; pub mod inline; @@ -35,6 +38,7 @@ pub mod nrvo; pub mod promote_consts; pub mod qualify_min_const_fn; pub mod remove_noop_landing_pads; +pub mod remove_unneeded_drops; pub mod required_consts; pub mod rustc_peek; pub mod simplify; @@ -307,6 +311,7 @@ fn mir_const<'tcx>( &[&[ // MIR-level lints. &check_packed_ref::CheckPackedRef, + &check_const_item_mutation::CheckConstItemMutation, // What we need to do constant evaluation. &simplify::SimplifyCfg::new("initial"), &rustc_peek::SanityCheck, @@ -327,7 +332,11 @@ fn mir_promoted( // this point, before we steal the mir-const result. // Also this means promotion can rely on all const checks having been done. let _ = tcx.mir_const_qualif_opt_const_arg(def); - + let _ = if let Some(param_did) = def.const_param_did { + tcx.mir_abstract_const_of_const_arg((def.did, param_did)) + } else { + tcx.mir_abstract_const(def.did.to_def_id()) + }; let mut body = tcx.mir_const(def).steal(); let mut required_consts = Vec::new(); @@ -453,13 +462,17 @@ fn run_optimization_passes<'tcx>( // The main optimizations that we do on MIR. let optimizations: &[&dyn MirPass<'tcx>] = &[ - &instcombine::InstCombine, + &remove_unneeded_drops::RemoveUnneededDrops, &match_branches::MatchBranchSimplification, + // inst combine is after MatchBranchSimplification to clean up Ne(_1, false) + &instcombine::InstCombine, &const_prop::ConstProp, &simplify_branches::SimplifyBranches::new("after-const-prop"), + &early_otherwise_branch::EarlyOtherwiseBranch, &simplify_comparison_integral::SimplifyComparisonIntegral, &simplify_try::SimplifyArmIdentity, &simplify_try::SimplifyBranchSame, + &dest_prop::DestinationPropagation, ©_prop::CopyPropagation, &simplify_branches::SimplifyBranches::new("after-copy-prop"), &remove_noop_landing_pads::RemoveNoopLandingPads, diff --git a/compiler/rustc_mir/src/transform/nrvo.rs b/compiler/rustc_mir/src/transform/nrvo.rs index 1f3d7bb7cc6..1ffb5a87c47 100644 --- a/compiler/rustc_mir/src/transform/nrvo.rs +++ b/compiler/rustc_mir/src/transform/nrvo.rs @@ -1,6 +1,6 @@ use rustc_hir::Mutability; use rustc_index::bit_set::HybridBitSet; -use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, BasicBlock, Local, Location}; use rustc_middle::ty::TyCtxt; @@ -36,6 +36,12 @@ impl<'tcx> MirPass<'tcx> for RenameReturnPlace { return; } + if tcx.sess.opts.debugging_opts.mir_opt_level >= 2 { + // The `DestinationPropagation` pass runs at level 2, so this pass is redundant (and + // fails some asserts). + return; + } + let returned_local = match local_eligible_for_nrvo(body) { Some(l) => l, None => { @@ -196,9 +202,10 @@ impl MutVisitor<'tcx> for RenameToReturnPlace<'tcx> { self.super_terminator(terminator, loc); } - fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) { - assert_ne!(*l, mir::RETURN_PLACE); - if *l == self.to_rename { + fn visit_local(&mut self, l: &mut Local, ctxt: PlaceContext, _: Location) { + if *l == mir::RETURN_PLACE { + assert_eq!(ctxt, PlaceContext::NonUse(NonUseContext::VarDebugInfo)); + } else if *l == self.to_rename { *l = mir::RETURN_PLACE; } } diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index b2dda1caa54..cd361e430fa 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -92,7 +92,7 @@ pub enum TempState { impl TempState { pub fn is_promotable(&self) -> bool { debug!("is_promotable: self={:?}", self); - if let TempState::Defined { .. } = *self { true } else { false } + matches!(self, TempState::Defined { .. } ) } } @@ -220,7 +220,7 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { match terminator.kind { TerminatorKind::Call { ref func, .. } => { - if let ty::FnDef(def_id, _) = func.ty(self.ccx.body, self.ccx.tcx).kind { + if let ty::FnDef(def_id, _) = *func.ty(self.ccx.body, self.ccx.tcx).kind() { let fn_sig = self.ccx.tcx.fn_sig(def_id); if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() { let name = self.ccx.tcx.item_name(def_id); @@ -242,11 +242,8 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { } TerminatorKind::InlineAsm { ref operands, .. } => { for (index, op) in operands.iter().enumerate() { - match op { - InlineAsmOperand::Const { .. } => { - self.candidates.push(Candidate::InlineAsm { bb: location.block, index }) - } - _ => {} + if let InlineAsmOperand::Const { .. } = op { + self.candidates.push(Candidate::InlineAsm { bb: location.block, index }) } } } @@ -297,6 +294,17 @@ impl std::ops::Deref for Validator<'a, 'tcx> { struct Unpromotable; impl<'tcx> Validator<'_, 'tcx> { + /// Determines if this code could be executed at runtime and thus is subject to codegen. + /// That means even unused constants need to be evaluated. + /// + /// `const_kind` should not be used in this file other than through this method! + fn maybe_runtime(&self) -> bool { + match self.const_kind { + None | Some(hir::ConstContext::ConstFn) => true, + Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) => false, + } + } + fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> { match candidate { Candidate::Ref(loc) => { @@ -363,20 +371,10 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] - // is allowed right now, and only in functions. - if self.const_kind - == Some(hir::ConstContext::Static(hir::Mutability::Mut)) - { - // Inside a `static mut`, &mut [...] is also allowed. - match ty.kind { - ty::Array(..) | ty::Slice(_) => {} - _ => return Err(Unpromotable), - } - } else if let ty::Array(_, len) = ty.kind { - // FIXME(eddyb) the `self.is_non_const_fn` condition - // seems unnecessary, given that this is merely a ZST. + // is allowed right now. + if let ty::Array(_, len) = ty.kind() { match len.try_eval_usize(self.tcx, self.param_env) { - Some(0) if self.const_kind.is_none() => {} + Some(0) => {} _ => return Err(Unpromotable), } } else { @@ -503,9 +501,10 @@ impl<'tcx> Validator<'_, 'tcx> { match place { PlaceRef { local, projection: [] } => self.validate_local(local), PlaceRef { local, projection: [proj_base @ .., elem] } => { + // Validate topmost projection, then recurse. match *elem { ProjectionElem::Deref => { - let mut not_promotable = true; + let mut promotable = false; // This is a special treatment for cases like *&STATIC where STATIC is a // global static variable. // This pattern is generated only when global static variables are directly @@ -520,6 +519,9 @@ impl<'tcx> Validator<'_, 'tcx> { }) = def_stmt { if let Some(did) = c.check_static_ptr(self.tcx) { + // Evaluating a promoted may not read statics except if it got + // promoted from a static (this is a CTFE check). So we + // can only promote static accesses inside statics. if let Some(hir::ConstContext::Static(..)) = self.const_kind { // The `is_empty` predicate is introduced to exclude the case // where the projection operations are [ .field, * ]. @@ -532,13 +534,13 @@ impl<'tcx> Validator<'_, 'tcx> { if proj_base.is_empty() && !self.tcx.is_thread_local_static(did) { - not_promotable = false; + promotable = true; } } } } } - if not_promotable { + if !promotable { return Err(Unpromotable); } } @@ -553,7 +555,7 @@ impl<'tcx> Validator<'_, 'tcx> { } ProjectionElem::Field(..) => { - if self.const_kind.is_none() { + if self.maybe_runtime() { let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; if let Some(def) = base_ty.ty_adt_def() { @@ -581,6 +583,10 @@ impl<'tcx> Validator<'_, 'tcx> { if let Some(def_id) = c.check_static_ptr(self.tcx) { // Only allow statics (not consts) to refer to other statics. // FIXME(eddyb) does this matter at all for promotion? + // FIXME(RalfJung) it makes little sense to not promote this in `fn`/`const fn`, + // and in `const` this cannot occur anyway. The only concern is that we might + // promote even `let x = &STATIC` which would be useless, but this applies to + // promotion inside statics as well. let is_static = matches!(self.const_kind, Some(hir::ConstContext::Static(_))); if !is_static { return Err(Unpromotable); @@ -599,21 +605,18 @@ impl<'tcx> Validator<'_, 'tcx> { fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { match *rvalue { - Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.const_kind.is_none() => { + Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { let operand_ty = operand.ty(self.body, self.tcx); let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - match (cast_in, cast_out) { - (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { - // in normal functions, mark such casts as not promotable - return Err(Unpromotable); - } - _ => {} + if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) { + // ptr-to-int casts are not possible in consts and thus not promotable + return Err(Unpromotable); } } - Rvalue::BinaryOp(op, ref lhs, _) if self.const_kind.is_none() => { - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind { + Rvalue::BinaryOp(op, ref lhs, _) => { + if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind() { assert!( op == BinOp::Eq || op == BinOp::Ne @@ -624,13 +627,14 @@ impl<'tcx> Validator<'_, 'tcx> { || op == BinOp::Offset ); - // raw pointer operations are not allowed inside promoteds + // raw pointer operations are not allowed inside consts and thus not promotable return Err(Unpromotable); } } Rvalue::NullaryOp(NullOp::Box, _) => return Err(Unpromotable), + // FIXME(RalfJung): the rest is *implicitly considered promotable*... that seems dangerous. _ => {} } @@ -652,11 +656,11 @@ impl<'tcx> Validator<'_, 'tcx> { } Rvalue::AddressOf(_, place) => { - // Raw reborrows can come from reference to pointer coercions, - // so are allowed. + // We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is + // no problem, only using it is. if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; - if let ty::Ref(..) = base_ty.kind { + if let ty::Ref(..) = base_ty.kind() { return self.validate_place(PlaceRef { local: place.local, projection: proj_base, @@ -672,18 +676,10 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] - // is allowed right now, and only in functions. - if self.const_kind == Some(hir::ConstContext::Static(hir::Mutability::Mut)) { - // Inside a `static mut`, &mut [...] is also allowed. - match ty.kind { - ty::Array(..) | ty::Slice(_) => {} - _ => return Err(Unpromotable), - } - } else if let ty::Array(_, len) = ty.kind { - // FIXME(eddyb): We only return `Unpromotable` for `&mut []` inside a - // const context which seems unnecessary given that this is merely a ZST. + // is allowed right now. + if let ty::Array(_, len) = ty.kind() { match len.try_eval_usize(self.tcx, self.param_env) { - Some(0) if self.const_kind.is_none() => {} + Some(0) => {} _ => return Err(Unpromotable), } } else { @@ -695,7 +691,7 @@ impl<'tcx> Validator<'_, 'tcx> { let mut place = place.as_ref(); if let [proj_base @ .., ProjectionElem::Deref] = &place.projection { let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; - if let ty::Ref(..) = base_ty.kind { + if let ty::Ref(..) = base_ty.kind() { place = PlaceRef { local: place.local, projection: proj_base }; } } @@ -748,8 +744,8 @@ impl<'tcx> Validator<'_, 'tcx> { ) -> Result<(), Unpromotable> { let fn_ty = callee.ty(self.body, self.tcx); - if !self.explicit && self.const_kind.is_none() { - if let ty::FnDef(def_id, _) = fn_ty.kind { + if !self.explicit && self.maybe_runtime() { + if let ty::FnDef(def_id, _) = *fn_ty.kind() { // Never promote runtime `const fn` calls of // functions without `#[rustc_promotable]`. if !self.tcx.is_promotable_const_fn(def_id) { @@ -758,7 +754,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } - let is_const_fn = match fn_ty.kind { + let is_const_fn = match *fn_ty.kind() { ty::FnDef(def_id, _) => { is_const_fn(self.tcx, def_id) || is_unstable_const_fn(self.tcx, def_id).is_some() diff --git a/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs b/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs index 26db4600a2b..f15a7f7c2c8 100644 --- a/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs +++ b/compiler/rustc_mir/src/transform/qualify_min_const_fn.rs @@ -1,4 +1,3 @@ -use rustc_attr as attr; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::*; @@ -30,7 +29,8 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - | ty::PredicateAtom::WellFormed(_) | ty::PredicateAtom::Projection(_) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => continue, + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue, ty::PredicateAtom::ObjectSafe(_) => { bug!("object safe predicate on function: {:#?}", predicate) } @@ -44,7 +44,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; } - match pred.self_ty().kind { + match pred.self_ty().kind() { ty::Param(ref p) => { // Allow `T: ?const Trait` if constness == hir::Constness::NotConst @@ -106,7 +106,7 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> Mc GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue, }; - match ty.kind { + match ty.kind() { ty::Ref(_, _, hir::Mutability::Mut) => { if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) { return Err((span, "mutable references in const fn are unstable".into())); @@ -203,7 +203,7 @@ fn check_rvalue( )); }; let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); - if let ty::Slice(_) | ty::Str = unsized_ty.kind { + if let ty::Slice(_) | ty::Str = unsized_ty.kind() { check_operand(tcx, op, span, def_id, body)?; // Casting/coercing things to slices is fine. Ok(()) @@ -343,8 +343,7 @@ fn feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bo // However, we cannot allow stable `const fn`s to use unstable features without an explicit // opt-in via `allow_internal_unstable`. - attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id)) - .map_or(false, |mut features| features.any(|name| name == feature_gate)) + super::check_consts::allow_internal_unstable(tcx, def_id, feature_gate) } /// Returns `true` if the given library feature gate is allowed within the function with the given `DefId`. @@ -363,8 +362,7 @@ pub fn lib_feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbo // However, we cannot allow stable `const fn`s to use unstable features without an explicit // opt-in via `allow_internal_unstable`. - attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id)) - .map_or(false, |mut features| features.any(|name| name == feature_gate)) + super::check_consts::allow_internal_unstable(tcx, def_id, feature_gate) } fn check_terminator( @@ -406,7 +404,7 @@ fn check_terminator( fn_span: _, } => { let fn_ty = func.ty(body, tcx); - if let ty::FnDef(fn_def_id, _) = fn_ty.kind { + if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { // Allow unstable const if we opt in by using #[allow_internal_unstable] // on function or macro declaration. if !crate::const_eval::is_min_const_fn(tcx, fn_def_id) diff --git a/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs b/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs index 0bad1e5037a..4079f0110e2 100644 --- a/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs @@ -102,6 +102,16 @@ impl RemoveNoopLandingPads { let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect(); for bb in postorder { debug!(" processing {:?}", bb); + if let Some(unwind) = body[bb].terminator_mut().unwind_mut() { + if let Some(unwind_bb) = *unwind { + if nop_landing_pads.contains(unwind_bb) { + debug!(" removing noop landing pad"); + landing_pads_removed += 1; + *unwind = None; + } + } + } + for target in body[bb].terminator_mut().successors_mut() { if *target != resume_block && nop_landing_pads.contains(*target) { debug!(" folding noop jump to {:?} to resume block", target); @@ -110,15 +120,6 @@ impl RemoveNoopLandingPads { } } - if let Some(unwind) = body[bb].terminator_mut().unwind_mut() { - if *unwind == Some(resume_block) { - debug!(" removing noop landing pad"); - jumps_folded -= 1; - landing_pads_removed += 1; - *unwind = None; - } - } - let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); if is_nop_landing_pad { nop_landing_pads.insert(bb); diff --git a/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs b/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs new file mode 100644 index 00000000000..b9f29786c64 --- /dev/null +++ b/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs @@ -0,0 +1,58 @@ +//! This pass replaces a drop of a type that does not need dropping, with a goto + +use crate::transform::{MirPass, MirSource}; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; + +use super::simplify::simplify_cfg; + +pub struct RemoveUnneededDrops; + +impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + trace!("Running RemoveUnneededDrops on {:?}", source); + let mut opt_finder = RemoveUnneededDropsOptimizationFinder { + tcx, + body, + optimizations: vec![], + def_id: source.def_id().expect_local(), + }; + opt_finder.visit_body(body); + let should_simplify = !opt_finder.optimizations.is_empty(); + for (loc, target) in opt_finder.optimizations { + let terminator = body.basic_blocks_mut()[loc.block].terminator_mut(); + debug!("SUCCESS: replacing `drop` with goto({:?})", target); + terminator.kind = TerminatorKind::Goto { target }; + } + + // if we applied optimizations, we potentially have some cfg to cleanup to + // make it easier for further passes + if should_simplify { + simplify_cfg(body); + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for RemoveUnneededDropsOptimizationFinder<'a, 'tcx> { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + match terminator.kind { + TerminatorKind::Drop { place, target, .. } => { + let ty = place.ty(self.body, self.tcx); + let needs_drop = ty.ty.needs_drop(self.tcx, self.tcx.param_env(self.def_id)); + if !needs_drop { + self.optimizations.push((location, target)); + } + } + _ => {} + } + self.super_terminator(terminator, location); + } +} +pub struct RemoveUnneededDropsOptimizationFinder<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + optimizations: Vec<(Location, BasicBlock)>, + def_id: LocalDefId, +} diff --git a/compiler/rustc_mir/src/transform/rustc_peek.rs b/compiler/rustc_mir/src/transform/rustc_peek.rs index 00d269a4af8..015af44b80f 100644 --- a/compiler/rustc_mir/src/transform/rustc_peek.rs +++ b/compiler/rustc_mir/src/transform/rustc_peek.rs @@ -1,4 +1,6 @@ -use rustc_ast as ast; +use std::borrow::Borrow; + +use rustc_ast::ast; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi; @@ -16,7 +18,7 @@ use crate::dataflow::impls::{ use crate::dataflow::move_paths::{HasMoveData, MoveData}; use crate::dataflow::move_paths::{LookupResult, MovePathIndex}; use crate::dataflow::MoveDataParamEnv; -use crate::dataflow::{Analysis, Results, ResultsCursor}; +use crate::dataflow::{Analysis, JoinSemiLattice, Results, ResultsCursor}; pub struct SanityCheck; @@ -180,7 +182,7 @@ enum PeekCallKind { impl PeekCallKind { fn from_arg_ty(arg: Ty<'_>) -> Self { - match arg.kind { + match arg.kind() { ty::Ref(_, _, _) => PeekCallKind::ByRef, _ => PeekCallKind::ByVal, } @@ -205,7 +207,7 @@ impl PeekCall { if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } = &terminator.kind { - if let ty::FnDef(def_id, substs) = func.literal.ty.kind { + if let ty::FnDef(def_id, substs) = *func.literal.ty.kind() { let sig = tcx.fn_sig(def_id); let name = tcx.item_name(def_id); if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek { @@ -248,25 +250,26 @@ pub trait RustcPeekAt<'tcx>: Analysis<'tcx> { &self, tcx: TyCtxt<'tcx>, place: mir::Place<'tcx>, - flow_state: &BitSet<Self::Idx>, + flow_state: &Self::Domain, call: PeekCall, ); } -impl<'tcx, A> RustcPeekAt<'tcx> for A +impl<'tcx, A, D> RustcPeekAt<'tcx> for A where - A: Analysis<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>, + A: Analysis<'tcx, Domain = D> + HasMoveData<'tcx>, + D: JoinSemiLattice + Clone + Borrow<BitSet<MovePathIndex>>, { fn peek_at( &self, tcx: TyCtxt<'tcx>, place: mir::Place<'tcx>, - flow_state: &BitSet<Self::Idx>, + flow_state: &Self::Domain, call: PeekCall, ) { match self.move_data().rev_lookup.find(place.as_ref()) { LookupResult::Exact(peek_mpi) => { - let bit_state = flow_state.contains(peek_mpi); + let bit_state = flow_state.borrow().contains(peek_mpi); debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state); if !bit_state { tcx.sess.span_err(call.span, "rustc_peek: bit not set"); diff --git a/compiler/rustc_mir/src/transform/simplify.rs b/compiler/rustc_mir/src/transform/simplify.rs index d8995e92abf..3fc8e6d4b04 100644 --- a/compiler/rustc_mir/src/transform/simplify.rs +++ b/compiler/rustc_mir/src/transform/simplify.rs @@ -281,8 +281,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { fn strip_nops(&mut self) { for blk in self.basic_blocks.iter_mut() { - blk.statements - .retain(|stmt| if let StatementKind::Nop = stmt.kind { false } else { true }) + blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop)) } } } diff --git a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs index a450a75d091..9b460c9ecb1 100644 --- a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs +++ b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs @@ -61,26 +61,6 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { _ => unreachable!(), } - let terminator = bb.terminator_mut(); - - // add StorageDead for the place switched on at the top of each target - for bb_idx in new_targets.iter() { - storage_deads_to_insert.push(( - *bb_idx, - Statement { - source_info: terminator.source_info, - kind: StatementKind::StorageDead(opt.to_switch_on.local), - }, - )); - } - - terminator.kind = TerminatorKind::SwitchInt { - discr: Operand::Move(opt.to_switch_on), - switch_ty: opt.branch_value_ty, - values: vec![new_value].into(), - targets: new_targets, - }; - // delete comparison statement if it the value being switched on was moved, which means it can not be user later on if opt.can_remove_bin_op_stmt { bb.statements[opt.bin_op_stmt_idx].make_nop(); @@ -106,14 +86,35 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { } } + let terminator = bb.terminator(); + // remove StorageDead (if it exists) being used in the assign of the comparison for (stmt_idx, stmt) in bb.statements.iter().enumerate() { if !matches!(stmt.kind, StatementKind::StorageDead(local) if local == opt.to_switch_on.local) { continue; } - storage_deads_to_remove.push((stmt_idx, opt.bb_idx)) + storage_deads_to_remove.push((stmt_idx, opt.bb_idx)); + // if we have StorageDeads to remove then make sure to insert them at the top of each target + for bb_idx in new_targets.iter() { + storage_deads_to_insert.push(( + *bb_idx, + Statement { + source_info: terminator.source_info, + kind: StatementKind::StorageDead(opt.to_switch_on.local), + }, + )); + } } + + let terminator = bb.terminator_mut(); + + terminator.kind = TerminatorKind::SwitchInt { + discr: Operand::Move(opt.to_switch_on), + switch_ty: opt.branch_value_ty, + values: vec![new_value].into(), + targets: new_targets, + }; } for (idx, bb_idx) in storage_deads_to_remove { diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs index 06829cc2f14..4935997eb82 100644 --- a/compiler/rustc_mir/src/transform/simplify_try.rs +++ b/compiler/rustc_mir/src/transform/simplify_try.rs @@ -16,7 +16,7 @@ use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{self, List, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; -use std::iter::{Enumerate, Peekable}; +use std::iter::{once, Enumerate, Peekable}; use std::slice::Iter; /// Simplifies arms of form `Variant(x) => Variant(x)` to just a move. @@ -230,8 +230,8 @@ fn get_arm_identity_info<'a, 'tcx>( } } } - - nop_stmts.sort(); + // We sort primitive usize here so we can use unstable sort + nop_stmts.sort_unstable(); // Use one of the statements we're going to discard between the point // where the storage location for the variant field becomes live and @@ -367,11 +367,7 @@ fn optimization_applies<'tcx>( } impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { - return; - } - + fn run_pass(&self, _tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { trace!("running SimplifyArmIdentity on {:?}", source); let local_uses = LocalUseCounter::get_local_uses(body); let (basic_blocks, local_decls, debug_info) = @@ -555,6 +551,12 @@ struct SimplifyBranchSameOptimization { bb_to_opt_terminator: BasicBlock, } +struct SwitchTargetAndValue { + target: BasicBlock, + // None in case of the `otherwise` case + value: Option<u128>, +} + struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> { body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, @@ -566,8 +568,16 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { .basic_blocks() .iter_enumerated() .filter_map(|(bb_idx, bb)| { - let (discr_switched_on, targets) = match &bb.terminator().kind { - TerminatorKind::SwitchInt { targets, discr, .. } => (discr, targets), + let (discr_switched_on, targets_and_values) = match &bb.terminator().kind { + TerminatorKind::SwitchInt { targets, discr, values, .. } => { + // if values.len() == targets.len() - 1, we need to include None where no value is present + // such that the zip does not throw away targets. If no `otherwise` case is in targets, the zip will simply throw away the added None + let values_extended = values.iter().map(|x|Some(*x)).chain(once(None)); + let targets_and_values:Vec<_> = targets.iter().zip(values_extended) + .map(|(target, value)| SwitchTargetAndValue{target:*target, value}) + .collect(); + assert_eq!(targets.len(), targets_and_values.len()); + (discr, targets_and_values)}, _ => return None, }; @@ -591,9 +601,9 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { }, }; - let mut iter_bbs_reachable = targets + let mut iter_bbs_reachable = targets_and_values .iter() - .map(|idx| (*idx, &self.body.basic_blocks()[*idx])) + .map(|target_and_value| (target_and_value, &self.body.basic_blocks()[target_and_value.target])) .filter(|(_, bb)| { // Reaching `unreachable` is UB so assume it doesn't happen. bb.terminator().kind != TerminatorKind::Unreachable @@ -607,16 +617,16 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { }) .peekable(); - let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(targets[0]); + let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(&targets_and_values[0]); let mut all_successors_equivalent = StatementEquality::TrivialEqual; // All successor basic blocks must be equal or contain statements that are pairwise considered equal. - for ((bb_l_idx,bb_l), (bb_r_idx,bb_r)) in iter_bbs_reachable.tuple_windows() { + for ((target_and_value_l,bb_l), (target_and_value_r,bb_r)) in iter_bbs_reachable.tuple_windows() { let trivial_checks = bb_l.is_cleanup == bb_r.is_cleanup && bb_l.terminator().kind == bb_r.terminator().kind; let statement_check = || { bb_l.statements.iter().zip(&bb_r.statements).try_fold(StatementEquality::TrivialEqual, |acc,(l,r)| { - let stmt_equality = self.statement_equality(*adt_matched_on, &l, bb_l_idx, &r, bb_r_idx); + let stmt_equality = self.statement_equality(*adt_matched_on, &l, target_and_value_l, &r, target_and_value_r); if matches!(stmt_equality, StatementEquality::NotEqual) { // short circuit None @@ -638,7 +648,7 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { // statements are trivially equal, so just take first trace!("Statements are trivially equal"); Some(SimplifyBranchSameOptimization { - bb_to_goto: bb_first, + bb_to_goto: bb_first.target, bb_to_opt_terminator: bb_idx, }) } @@ -673,16 +683,16 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { &self, adt_matched_on: Place<'tcx>, x: &Statement<'tcx>, - x_bb_idx: BasicBlock, + x_target_and_value: &SwitchTargetAndValue, y: &Statement<'tcx>, - y_bb_idx: BasicBlock, + y_target_and_value: &SwitchTargetAndValue, ) -> StatementEquality { let helper = |rhs: &Rvalue<'tcx>, - place: &Box<Place<'tcx>>, + place: &Place<'tcx>, variant_index: &VariantIdx, side_to_choose| { let place_type = place.ty(self.body, self.tcx).ty; - let adt = match place_type.kind { + let adt = match *place_type.kind() { ty::Adt(adt, _) if adt.is_enum() => adt, _ => return StatementEquality::NotEqual, }; @@ -714,16 +724,20 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { ( StatementKind::Assign(box (_, rhs)), StatementKind::SetDiscriminant { place, variant_index }, - ) => { + ) + // we need to make sure that the switch value that targets the bb with SetDiscriminant (y), is the same as the variant index + if Some(variant_index.index() as u128) == y_target_and_value.value => { // choose basic block of x, as that has the assign - helper(rhs, place, variant_index, x_bb_idx) + helper(rhs, place, variant_index, x_target_and_value.target) } ( StatementKind::SetDiscriminant { place, variant_index }, StatementKind::Assign(box (_, rhs)), - ) => { + ) + // we need to make sure that the switch value that targets the bb with SetDiscriminant (x), is the same as the variant index + if Some(variant_index.index() as u128) == x_target_and_value.value => { // choose basic block of y, as that has the assign - helper(rhs, place, variant_index, y_bb_idx) + helper(rhs, place, variant_index, y_target_and_value.target) } _ => { trace!("NO: statements `{:?}` and `{:?}` not considered equal", x, y); diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs index d7c9ecd0655..d3ca14abdca 100644 --- a/compiler/rustc_mir/src/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -4,8 +4,8 @@ use super::{MirPass, MirSource}; use rustc_middle::mir::visit::Visitor; use rustc_middle::{ mir::{ - AggregateKind, BasicBlock, Body, Location, MirPhase, Operand, Rvalue, Statement, - StatementKind, Terminator, TerminatorKind, + AggregateKind, BasicBlock, Body, BorrowKind, Location, MirPhase, Operand, Rvalue, + Statement, StatementKind, Terminator, TerminatorKind, }, ty::{ self, @@ -274,9 +274,33 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) } } + Rvalue::Ref(_, BorrowKind::Shallow, _) => { + if self.mir_phase > MirPhase::DropLowering { + self.fail( + location, + "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase", + ); + } + } _ => {} } } + StatementKind::AscribeUserType(..) => { + if self.mir_phase > MirPhase::DropLowering { + self.fail( + location, + "`AscribeUserType` should have been removed after drop lowering phase", + ); + } + } + StatementKind::FakeRead(..) => { + if self.mir_phase > MirPhase::DropLowering { + self.fail( + location, + "`FakeRead` should have been removed after drop lowering phase", + ); + } + } _ => {} } } @@ -331,7 +355,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } TerminatorKind::Call { func, destination, cleanup, .. } => { let func_ty = func.ty(&self.body.local_decls, self.tcx); - match func_ty.kind { + match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( location, diff --git a/compiler/rustc_mir/src/util/alignment.rs b/compiler/rustc_mir/src/util/alignment.rs index 202e5e27f1d..a0728a6a630 100644 --- a/compiler/rustc_mir/src/util/alignment.rs +++ b/compiler/rustc_mir/src/util/alignment.rs @@ -47,7 +47,7 @@ where ProjectionElem::Deref => break, ProjectionElem::Field(..) => { let ty = Place::ty_from(place.local, proj_base, local_decls, tcx).ty; - match ty.kind { + match ty.kind() { ty::Adt(def, _) if def.repr.packed() => return true, _ => {} } diff --git a/compiler/rustc_mir/src/util/borrowck_errors.rs b/compiler/rustc_mir/src/util/borrowck_errors.rs index f8bb7e7a85d..83bf7584f2e 100644 --- a/compiler/rustc_mir/src/util/borrowck_errors.rs +++ b/compiler/rustc_mir/src/util/borrowck_errors.rs @@ -287,7 +287,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> { ty: Ty<'_>, is_index: Option<bool>, ) -> DiagnosticBuilder<'cx> { - let type_name = match (&ty.kind, is_index) { + let type_name = match (&ty.kind(), is_index) { (&ty::Array(_, _), Some(true)) | (&ty::Array(_, _), None) => "array", (&ty::Slice(_), _) => "slice", _ => span_bug!(move_from_span, "this path should not cause illegal move"), diff --git a/compiler/rustc_mir/src/util/elaborate_drops.rs b/compiler/rustc_mir/src/util/elaborate_drops.rs index 642935d243d..bf0a6be9a7d 100644 --- a/compiler/rustc_mir/src/util/elaborate_drops.rs +++ b/compiler/rustc_mir/src/util/elaborate_drops.rs @@ -858,7 +858,7 @@ where /// ADT, both in the success case or if one of the destructors fail. fn open_drop(&mut self) -> BasicBlock { let ty = self.place_ty(self.place); - match ty.kind { + match ty.kind() { ty::Closure(_, substs) => { let tys: Vec<_> = substs.as_closure().upvar_tys().collect(); self.open_drop_for_tuple(&tys) diff --git a/compiler/rustc_mir/src/util/find_self_call.rs b/compiler/rustc_mir/src/util/find_self_call.rs new file mode 100644 index 00000000000..5b146eeb87c --- /dev/null +++ b/compiler/rustc_mir/src/util/find_self_call.rs @@ -0,0 +1,36 @@ +use rustc_middle::mir::*; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::def_id::DefId; + +/// Checks if the specified `local` is used as the `self` prameter of a method call +/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is +/// returned. +pub fn find_self_call<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + local: Local, + block: BasicBlock, +) -> Option<(DefId, SubstsRef<'tcx>)> { + debug!("find_self_call(local={:?}): terminator={:?}", local, &body[block].terminator); + if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) = + &body[block].terminator + { + debug!("find_self_call: func={:?}", func); + if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { + if let ty::FnDef(def_id, substs) = *ty.kind() { + if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = + tcx.opt_associated_item(def_id) + { + debug!("find_self_call: args={:?}", args); + if let [Operand::Move(self_place) | Operand::Copy(self_place), ..] = **args { + if self_place.as_local() == Some(local) { + return Some((def_id, substs)); + } + } + } + } + } + } + None +} diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index 50193c4a0db..4511962d68f 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -55,16 +55,28 @@ where writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?; // Global graph properties - writeln!(w, r#" graph [fontname="monospace"];"#)?; - writeln!(w, r#" node [fontname="monospace"];"#)?; - writeln!(w, r#" edge [fontname="monospace"];"#)?; + let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font); + let mut graph_attrs = vec![&font[..]]; + let mut content_attrs = vec![&font[..]]; + + let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + if dark_mode { + graph_attrs.push(r#"bgcolor="black""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + + writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node [{}];"#, content_attrs_str)?; + writeln!(w, r#" edge [{}];"#, content_attrs_str)?; // Graph label write_graph_label(tcx, def_id, body, w)?; // Nodes for (block, _) in body.basic_blocks().iter_enumerated() { - write_node(def_id, block, body, w)?; + write_node(def_id, block, body, dark_mode, w)?; } // Edges @@ -84,6 +96,7 @@ where pub fn write_node_label<W: Write, INIT, FINI>( block: BasicBlock, body: &Body<'_>, + dark_mode: bool, w: &mut W, num_cols: u32, init: INIT, @@ -100,8 +113,9 @@ where // Basic block number at the top. write!( w, - r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#, - attrs = r#"bgcolor="gray" align="center""#, + r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#, + bgcolor = if dark_mode { "dimgray" } else { "gray" }, + attrs = r#"align="center""#, colspan = num_cols, blk = block.index() )?; @@ -134,11 +148,12 @@ fn write_node<W: Write>( def_id: DefId, block: BasicBlock, body: &Body<'_>, + dark_mode: bool, w: &mut W, ) -> io::Result<()> { // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?; - write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?; + write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?; // Close the node label and the node itself. writeln!(w, ">];") } diff --git a/compiler/rustc_mir/src/util/mod.rs b/compiler/rustc_mir/src/util/mod.rs index 8bbe207c077..699f3bcf014 100644 --- a/compiler/rustc_mir/src/util/mod.rs +++ b/compiler/rustc_mir/src/util/mod.rs @@ -7,11 +7,14 @@ pub mod storage; mod alignment; pub mod collect_writes; +mod find_self_call; mod graphviz; pub(crate) mod pretty; +pub(crate) mod spanview; pub use self::aggregate::expand_aggregate; pub use self::alignment::is_disaligned; +pub use self::find_self_call::find_self_call; pub use self::graphviz::write_node_label as write_graphviz_node_label; pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz}; pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere}; diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index 2a9cbc7fc0e..49c644a20bf 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -6,6 +6,7 @@ use std::io::{self, Write}; use std::path::{Path, PathBuf}; use super::graphviz::write_mir_fn_graphviz; +use super::spanview::write_mir_fn_spanview; use crate::transform::MirSource; use either::Either; use rustc_data_structures::fx::FxHashMap; @@ -147,6 +148,16 @@ fn dump_matched_mir_node<'tcx, F>( write_mir_fn_graphviz(tcx, source.def_id(), body, false, &mut file)?; }; } + + if let Some(spanview) = tcx.sess.opts.debugging_opts.dump_mir_spanview { + let _: io::Result<()> = try { + let mut file = + create_dump_file(tcx, "html", pass_num, pass_name, disambiguator, source)?; + if source.def_id().is_local() { + write_mir_fn_spanview(tcx, source.def_id(), body, spanview, &mut file)?; + } + }; + } } /// Returns the path to the filename where we should dump a given MIR. @@ -387,7 +398,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { self.super_constant(constant, location); let Constant { span, user_ty, literal } = constant; - match literal.ty.kind { + match literal.ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {} // Unit type ty::Tuple(tys) if tys.is_empty() => {} @@ -405,7 +416,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) { self.super_const(constant); let ty::Const { ty, val, .. } = constant; - match ty.kind { + match ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => {} // Unit type ty::Tuple(tys) if tys.is_empty() => {} @@ -503,7 +514,7 @@ fn write_scope_tree( write!(indented_decl, " as {:?}", user_ty).unwrap(); } } - indented_decl.push_str(";"); + indented_decl.push(';'); let local_name = if local == RETURN_PLACE { " return place".to_string() } else { String::new() }; @@ -620,14 +631,11 @@ pub fn write_allocations<'tcx>( None => write!(w, " (deallocated)")?, Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?, Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { - match tcx.const_eval_poly(did) { - Ok(ConstValue::ByRef { alloc, .. }) => { + match tcx.eval_static_initializer(did) { + Ok(alloc) => { write!(w, " (static: {}, ", tcx.def_path_str(did))?; write_allocation_track_relocs(w, alloc)?; } - Ok(_) => { - span_bug!(tcx.def_span(did), " static item without `ByRef` initializer") - } Err(_) => write!( w, " (static: {}, error during initializer evaluation)", diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs new file mode 100644 index 00000000000..fe33fffe0ea --- /dev/null +++ b/compiler/rustc_mir/src/util/spanview.rs @@ -0,0 +1,672 @@ +use rustc_hir::def_id::DefId; +use rustc_middle::hir; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::MirSpanview; +use rustc_span::{BytePos, Pos, Span, SyntaxContext}; + +use std::cmp; +use std::io::{self, Write}; + +pub const TOOLTIP_INDENT: &str = " "; + +const CARET: char = '\u{2038}'; // Unicode `CARET` +const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT BINDING BRACKET +const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET` +const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">"; +const HEADER: &str = r#"<!DOCTYPE html> +<html> +<head> + <title>coverage_of_if_else - Code Regions</title> + <style> + .line { + counter-increment: line; + } + .line:before { + content: counter(line) ": "; + font-family: Menlo, Monaco, monospace; + font-style: italic; + width: 3.8em; + display: inline-block; + text-align: right; + filter: opacity(50%); + -webkit-user-select: none; + } + .code { + color: #dddddd; + background-color: #222222; + font-family: Menlo, Monaco, monospace; + line-height: 1.4em; + border-bottom: 2px solid #222222; + white-space: pre; + display: inline-block; + } + .odd { + background-color: #55bbff; + color: #223311; + } + .even { + background-color: #ee7756; + color: #551133; + } + .code { + --index: calc(var(--layer) - 1); + padding-top: calc(var(--index) * 0.15em); + filter: + hue-rotate(calc(var(--index) * 25deg)) + saturate(calc(100% - (var(--index) * 2%))) + brightness(calc(100% - (var(--index) * 1.5%))); + } + .annotation { + color: #4444ff; + font-family: monospace; + font-style: italic; + display: none; + -webkit-user-select: none; + } + body:active .annotation { + /* requires holding mouse down anywhere on the page */ + display: inline-block; + } + span:hover .annotation { + /* requires hover over a span ONLY on its first line */ + display: inline-block; + } + </style> +</head> +<body>"#; + +const FOOTER: &str = r#" +</body> +</html>"#; + +/// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator. +pub struct SpanViewable { + pub span: Span, + pub id: String, + pub tooltip: String, +} + +/// Write a spanview HTML+CSS file to analyze MIR element spans. +pub fn write_mir_fn_spanview<'tcx, W>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + body: &Body<'tcx>, + spanview: MirSpanview, + w: &mut W, +) -> io::Result<()> +where + W: Write, +{ + let body_span = hir_body(tcx, def_id).value.span; + let mut span_viewables = Vec::new(); + for (bb, data) in body.basic_blocks().iter_enumerated() { + match spanview { + MirSpanview::Statement => { + for (i, statement) in data.statements.iter().enumerate() { + if let Some(span_viewable) = + statement_span_viewable(tcx, body_span, bb, i, statement) + { + span_viewables.push(span_viewable); + } + } + if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) { + span_viewables.push(span_viewable); + } + } + MirSpanview::Terminator => { + if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) { + span_viewables.push(span_viewable); + } + } + MirSpanview::Block => { + if let Some(span_viewable) = block_span_viewable(tcx, body_span, bb, data) { + span_viewables.push(span_viewable); + } + } + } + } + write_spanview_document(tcx, def_id, span_viewables, w)?; + Ok(()) +} + +/// Generate a spanview HTML+CSS document for the given local function `def_id`, and a pre-generated +/// list `SpanViewable`s. +pub fn write_spanview_document<'tcx, W>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + mut span_viewables: Vec<SpanViewable>, + w: &mut W, +) -> io::Result<()> +where + W: Write, +{ + let fn_span = fn_span(tcx, def_id); + let mut from_pos = fn_span.lo(); + let end_pos = fn_span.hi(); + let source_map = tcx.sess.source_map(); + let start = source_map.lookup_char_pos(from_pos); + let indent_to_initial_start_col = " ".repeat(start.col.to_usize()); + debug!( + "fn_span source is:\n{}{}", + indent_to_initial_start_col, + source_map.span_to_snippet(fn_span).expect("function should have printable source") + ); + writeln!(w, "{}", HEADER)?; + write!( + w, + r#"<div class="code" style="counter-reset: line {}"><span class="line">{}"#, + start.line - 1, + indent_to_initial_start_col, + )?; + span_viewables.sort_unstable_by(|a, b| { + let a = a.span; + let b = b.span; + if a.lo() == b.lo() { + // Sort hi() in reverse order so shorter spans are attempted after longer spans. + // This should give shorter spans a higher "layer", so they are not covered by + // the longer spans. + b.hi().partial_cmp(&a.hi()) + } else { + a.lo().partial_cmp(&b.lo()) + } + .unwrap() + }); + let mut ordered_viewables = &span_viewables[..]; + const LOWEST_VIEWABLE_LAYER: usize = 1; + let mut alt = false; + while ordered_viewables.len() > 0 { + debug!( + "calling write_next_viewable with from_pos={}, end_pos={}, and viewables len={}", + from_pos.to_usize(), + end_pos.to_usize(), + ordered_viewables.len() + ); + let (next_from_pos, next_ordered_viewables) = write_next_viewable_with_overlaps( + tcx, + from_pos, + end_pos, + ordered_viewables, + alt, + LOWEST_VIEWABLE_LAYER, + w, + )?; + debug!( + "DONE calling write_next_viewable, with new from_pos={}, \ + and remaining viewables len={}", + next_from_pos.to_usize(), + next_ordered_viewables.len() + ); + assert!( + from_pos != next_from_pos || ordered_viewables.len() != next_ordered_viewables.len(), + "write_next_viewable_with_overlaps() must make a state change" + ); + from_pos = next_from_pos; + if next_ordered_viewables.len() != ordered_viewables.len() { + ordered_viewables = next_ordered_viewables; + alt = !alt; + } + } + if from_pos < end_pos { + write_coverage_gap(tcx, from_pos, end_pos, w)?; + } + write!(w, r#"</span></div>"#)?; + writeln!(w, "{}", FOOTER)?; + Ok(()) +} + +/// Format a string showing the start line and column, and end line and column within a file. +pub fn source_range_no_file<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> String { + let source_map = tcx.sess.source_map(); + let start = source_map.lookup_char_pos(span.lo()); + let end = source_map.lookup_char_pos(span.hi()); + format!("{}:{}-{}:{}", start.line, start.col.to_usize() + 1, end.line, end.col.to_usize() + 1) +} + +pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str { + use StatementKind::*; + match statement.kind { + Assign(..) => "Assign", + FakeRead(..) => "FakeRead", + SetDiscriminant { .. } => "SetDiscriminant", + StorageLive(..) => "StorageLive", + StorageDead(..) => "StorageDead", + LlvmInlineAsm(..) => "LlvmInlineAsm", + Retag(..) => "Retag", + AscribeUserType(..) => "AscribeUserType", + Coverage(..) => "Coverage", + Nop => "Nop", + } +} + +pub fn terminator_kind_name(term: &Terminator<'_>) -> &'static str { + use TerminatorKind::*; + match term.kind { + Goto { .. } => "Goto", + SwitchInt { .. } => "SwitchInt", + Resume => "Resume", + Abort => "Abort", + Return => "Return", + Unreachable => "Unreachable", + Drop { .. } => "Drop", + DropAndReplace { .. } => "DropAndReplace", + Call { .. } => "Call", + Assert { .. } => "Assert", + Yield { .. } => "Yield", + GeneratorDrop => "GeneratorDrop", + FalseEdge { .. } => "FalseEdge", + FalseUnwind { .. } => "FalseUnwind", + InlineAsm { .. } => "InlineAsm", + } +} + +fn statement_span_viewable<'tcx>( + tcx: TyCtxt<'tcx>, + body_span: Span, + bb: BasicBlock, + i: usize, + statement: &Statement<'tcx>, +) -> Option<SpanViewable> { + let span = statement.source_info.span; + if !body_span.contains(span) { + return None; + } + let id = format!("{}[{}]", bb.index(), i); + let tooltip = tooltip(tcx, &id, span, vec![statement.clone()], &None); + Some(SpanViewable { span, id, tooltip }) +} + +fn terminator_span_viewable<'tcx>( + tcx: TyCtxt<'tcx>, + body_span: Span, + bb: BasicBlock, + data: &BasicBlockData<'tcx>, +) -> Option<SpanViewable> { + let term = data.terminator(); + let span = term.source_info.span; + if !body_span.contains(span) { + return None; + } + let id = format!("{}:{}", bb.index(), terminator_kind_name(term)); + let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator); + Some(SpanViewable { span, id, tooltip }) +} + +fn block_span_viewable<'tcx>( + tcx: TyCtxt<'tcx>, + body_span: Span, + bb: BasicBlock, + data: &BasicBlockData<'tcx>, +) -> Option<SpanViewable> { + let span = compute_block_span(data, body_span); + if !body_span.contains(span) { + return None; + } + let id = format!("{}", bb.index()); + let tooltip = tooltip(tcx, &id, span, data.statements.clone(), &data.terminator); + Some(SpanViewable { span, id, tooltip }) +} + +fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Span { + let mut span = data.terminator().source_info.span; + for statement_span in data.statements.iter().map(|statement| statement.source_info.span) { + // Only combine Spans from the root context, and within the function's body_span. + if statement_span.ctxt() == SyntaxContext::root() && body_span.contains(statement_span) { + span = span.to(statement_span); + } + } + span +} + +/// Recursively process each ordered span. Spans that overlap will have progressively varying +/// styles, such as increased padding for each overlap. Non-overlapping adjacent spans will +/// have alternating style choices, to help distinguish between them if, visually adjacent. +/// The `layer` is incremented for each overlap, and the `alt` bool alternates between true +/// and false, for each adjacent non-overlapping span. Source code between the spans (code +/// that is not in any coverage region) has neutral styling. +fn write_next_viewable_with_overlaps<'tcx, 'b, W>( + tcx: TyCtxt<'tcx>, + mut from_pos: BytePos, + mut to_pos: BytePos, + ordered_viewables: &'b [SpanViewable], + alt: bool, + layer: usize, + w: &mut W, +) -> io::Result<(BytePos, &'b [SpanViewable])> +where + W: Write, +{ + let debug_indent = " ".repeat(layer); + let (viewable, mut remaining_viewables) = + ordered_viewables.split_first().expect("ordered_viewables should have some"); + + if from_pos < viewable.span.lo() { + debug!( + "{}advance from_pos to next SpanViewable (from from_pos={} to viewable.span.lo()={} \ + of {:?}), with to_pos={}", + debug_indent, + from_pos.to_usize(), + viewable.span.lo().to_usize(), + viewable.span, + to_pos.to_usize() + ); + let hi = cmp::min(viewable.span.lo(), to_pos); + write_coverage_gap(tcx, from_pos, hi, w)?; + from_pos = hi; + if from_pos < viewable.span.lo() { + debug!( + "{}EARLY RETURN: stopped before getting to next SpanViewable, at {}", + debug_indent, + from_pos.to_usize() + ); + return Ok((from_pos, ordered_viewables)); + } + } + + if from_pos < viewable.span.hi() { + // Set to_pos to the end of this `viewable` to ensure the recursive calls stop writing + // with room to print the tail. + to_pos = cmp::min(viewable.span.hi(), to_pos); + debug!( + "{}update to_pos (if not closer) to viewable.span.hi()={}; to_pos is now {}", + debug_indent, + viewable.span.hi().to_usize(), + to_pos.to_usize() + ); + } + + let mut subalt = false; + while remaining_viewables.len() > 0 && remaining_viewables[0].span.overlaps(viewable.span) { + let overlapping_viewable = &remaining_viewables[0]; + debug!("{}overlapping_viewable.span={:?}", debug_indent, overlapping_viewable.span); + + let span = + trim_span(viewable.span, from_pos, cmp::min(overlapping_viewable.span.lo(), to_pos)); + let mut some_html_snippet = if from_pos <= viewable.span.hi() || viewable.span.is_empty() { + // `viewable` is not yet fully rendered, so start writing the span, up to either the + // `to_pos` or the next `overlapping_viewable`, whichever comes first. + debug!( + "{}make html_snippet (may not write it if early exit) for partial span {:?} \ + of viewable.span {:?}", + debug_indent, span, viewable.span + ); + from_pos = span.hi(); + make_html_snippet(tcx, span, Some(&viewable)) + } else { + None + }; + + // Defer writing the HTML snippet (until after early return checks) ONLY for empty spans. + // An empty Span with Some(html_snippet) is probably a tail marker. If there is an early + // exit, there should be another opportunity to write the tail marker. + if !span.is_empty() { + if let Some(ref html_snippet) = some_html_snippet { + debug!( + "{}write html_snippet for that partial span of viewable.span {:?}", + debug_indent, viewable.span + ); + write_span(html_snippet, &viewable.tooltip, alt, layer, w)?; + } + some_html_snippet = None; + } + + if from_pos < overlapping_viewable.span.lo() { + debug!( + "{}EARLY RETURN: from_pos={} has not yet reached the \ + overlapping_viewable.span {:?}", + debug_indent, + from_pos.to_usize(), + overlapping_viewable.span + ); + // must have reached `to_pos` before reaching the start of the + // `overlapping_viewable.span` + return Ok((from_pos, ordered_viewables)); + } + + if from_pos == to_pos + && !(from_pos == overlapping_viewable.span.lo() && overlapping_viewable.span.is_empty()) + { + debug!( + "{}EARLY RETURN: from_pos=to_pos={} and overlapping_viewable.span {:?} is not \ + empty, or not from_pos", + debug_indent, + to_pos.to_usize(), + overlapping_viewable.span + ); + // `to_pos` must have occurred before the overlapping viewable. Return + // `ordered_viewables` so we can continue rendering the `viewable`, from after the + // `to_pos`. + return Ok((from_pos, ordered_viewables)); + } + + if let Some(ref html_snippet) = some_html_snippet { + debug!( + "{}write html_snippet for that partial span of viewable.span {:?}", + debug_indent, viewable.span + ); + write_span(html_snippet, &viewable.tooltip, alt, layer, w)?; + } + + debug!( + "{}recursively calling write_next_viewable with from_pos={}, to_pos={}, \ + and viewables len={}", + debug_indent, + from_pos.to_usize(), + to_pos.to_usize(), + remaining_viewables.len() + ); + // Write the overlaps (and the overlaps' overlaps, if any) up to `to_pos`. + let (next_from_pos, next_remaining_viewables) = write_next_viewable_with_overlaps( + tcx, + from_pos, + to_pos, + &remaining_viewables, + subalt, + layer + 1, + w, + )?; + debug!( + "{}DONE recursively calling write_next_viewable, with new from_pos={}, and remaining \ + viewables len={}", + debug_indent, + next_from_pos.to_usize(), + next_remaining_viewables.len() + ); + assert!( + from_pos != next_from_pos + || remaining_viewables.len() != next_remaining_viewables.len(), + "write_next_viewable_with_overlaps() must make a state change" + ); + from_pos = next_from_pos; + if next_remaining_viewables.len() != remaining_viewables.len() { + remaining_viewables = next_remaining_viewables; + subalt = !subalt; + } + } + if from_pos <= viewable.span.hi() { + let span = trim_span(viewable.span, from_pos, to_pos); + debug!( + "{}After overlaps, writing (end span?) {:?} of viewable.span {:?}", + debug_indent, span, viewable.span + ); + if let Some(ref html_snippet) = make_html_snippet(tcx, span, Some(&viewable)) { + from_pos = span.hi(); + write_span(html_snippet, &viewable.tooltip, alt, layer, w)?; + } + } + debug!("{}RETURN: No more overlap", debug_indent); + Ok(( + from_pos, + if from_pos < viewable.span.hi() { ordered_viewables } else { remaining_viewables }, + )) +} + +#[inline(always)] +fn write_coverage_gap<'tcx, W>( + tcx: TyCtxt<'tcx>, + lo: BytePos, + hi: BytePos, + w: &mut W, +) -> io::Result<()> +where + W: Write, +{ + let span = Span::with_root_ctxt(lo, hi); + if let Some(ref html_snippet) = make_html_snippet(tcx, span, None) { + write_span(html_snippet, "", false, 0, w) + } else { + Ok(()) + } +} + +fn write_span<W>( + html_snippet: &str, + tooltip: &str, + alt: bool, + layer: usize, + w: &mut W, +) -> io::Result<()> +where + W: Write, +{ + let maybe_alt_class = if layer > 0 { + if alt { " odd" } else { " even" } + } else { + "" + }; + let maybe_title_attr = if !tooltip.is_empty() { + format!(" title=\"{}\"", escape_attr(tooltip)) + } else { + "".to_owned() + }; + if layer == 1 { + write!(w, "<span>")?; + } + for (i, line) in html_snippet.lines().enumerate() { + if i > 0 { + write!(w, "{}", NEW_LINE_SPAN)?; + } + write!( + w, + r#"<span class="code{}" style="--layer: {}"{}>{}</span>"#, + maybe_alt_class, layer, maybe_title_attr, line + )?; + } + // Check for and translate trailing newlines, because `str::lines()` ignores them + if html_snippet.ends_with('\n') { + write!(w, "{}", NEW_LINE_SPAN)?; + } + if layer == 1 { + write!(w, "</span>")?; + } + Ok(()) +} + +fn make_html_snippet<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + some_viewable: Option<&SpanViewable>, +) -> Option<String> { + let source_map = tcx.sess.source_map(); + let snippet = source_map + .span_to_snippet(span) + .unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err)); + let html_snippet = if let Some(viewable) = some_viewable { + let is_head = span.lo() == viewable.span.lo(); + let is_tail = span.hi() == viewable.span.hi(); + let mut labeled_snippet = if is_head { + format!(r#"<span class="annotation">{}{}</span>"#, viewable.id, ANNOTATION_LEFT_BRACKET) + } else { + "".to_owned() + }; + if span.is_empty() { + if is_head && is_tail { + labeled_snippet.push(CARET); + } + } else { + labeled_snippet.push_str(&escape_html(&snippet)); + }; + if is_tail { + labeled_snippet.push_str(&format!( + r#"<span class="annotation">{}{}</span>"#, + ANNOTATION_RIGHT_BRACKET, viewable.id + )); + } + labeled_snippet + } else { + escape_html(&snippet) + }; + if html_snippet.is_empty() { None } else { Some(html_snippet) } +} + +fn tooltip<'tcx>( + tcx: TyCtxt<'tcx>, + spanview_id: &str, + span: Span, + statements: Vec<Statement<'tcx>>, + terminator: &Option<Terminator<'tcx>>, +) -> String { + let source_map = tcx.sess.source_map(); + let mut text = Vec::new(); + text.push(format!("{}: {}:", spanview_id, &source_map.span_to_string(span))); + for statement in statements { + let source_range = source_range_no_file(tcx, &statement.source_info.span); + text.push(format!( + "\n{}{}: {}: {}", + TOOLTIP_INDENT, + source_range, + statement_kind_name(&statement), + format!("{:?}", statement) + )); + } + if let Some(term) = terminator { + let source_range = source_range_no_file(tcx, &term.source_info.span); + text.push(format!( + "\n{}{}: {}: {:?}", + TOOLTIP_INDENT, + source_range, + terminator_kind_name(term), + term.kind + )); + } + text.join("") +} + +fn trim_span(span: Span, from_pos: BytePos, to_pos: BytePos) -> Span { + trim_span_hi(trim_span_lo(span, from_pos), to_pos) +} + +fn trim_span_lo(span: Span, from_pos: BytePos) -> Span { + if from_pos <= span.lo() { span } else { span.with_lo(cmp::min(span.hi(), from_pos)) } +} + +fn trim_span_hi(span: Span, to_pos: BytePos) -> Span { + if to_pos >= span.hi() { span } else { span.with_hi(cmp::max(span.lo(), to_pos)) } +} + +fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span { + let hir_id = + tcx.hir().local_def_id_to_hir_id(def_id.as_local().expect("expected DefId is local")); + let fn_decl_span = tcx.hir().span(hir_id); + let body_span = hir_body(tcx, def_id).value.span; + debug_assert_eq!(fn_decl_span.ctxt(), body_span.ctxt()); + fn_decl_span.to(body_span) +} + +fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { + let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local"); + let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); + tcx.hir().body(fn_body_id) +} + +fn escape_html(s: &str) -> String { + s.replace("&", "&").replace("<", "<").replace(">", ">") +} + +fn escape_attr(s: &str) -> String { + s.replace("&", "&") + .replace("\"", """) + .replace("'", "'") + .replace("<", "<") + .replace(">", ">") +} diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index d1cbf209b06..beaf12b1db0 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -96,8 +96,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } StmtKind::Let { remainder_scope, init_scope, pattern, initializer, lint_level } => { - let ignores_expr_result = - if let PatKind::Wild = *pattern.kind { true } else { false }; + let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild); this.block_context.push(BlockFrame::Statement { ignores_expr_result }); // Enter the remainder scope, i.e., the bindings' destruction scope. diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 982aefcf604..244a70f83b0 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -21,7 +21,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let Expr { ty, temp_lifetime: _, span, kind } = expr; match kind { ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value), - ExprKind::Literal { literal, user_ty } => { + ExprKind::Literal { literal, user_ty, const_id: _ } => { let user_ty = user_ty.map(|user_ty| { this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation { span, 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 1e3e104c2ba..39dbb6dd3ff 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -367,7 +367,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tcx = self.hir.tcx(); let place_ty = Place::ty_from(base_place.local, &base_place.projection, &self.local_decls, tcx); - if let ty::Slice(_) = place_ty.ty.kind { + if let ty::Slice(_) = place_ty.ty.kind() { // We need to create fake borrows to ensure that the bounds // check that we just did stays valid. Since we can't assign to // unsized values, we only need to ensure that none of the @@ -406,7 +406,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &self.local_decls, tcx, ); - match index_ty.ty.kind { + match index_ty.ty.kind() { // The previous index expression has already // done any index expressions needed here. ty::Slice(_) => break, diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs index a9cc0cc2f24..9984b527ffd 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -76,6 +76,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { local_decl.local_info = Some(box LocalInfo::StaticRef { def_id, is_thread_local: true }); } + ExprKind::Literal { const_id: Some(def_id), .. } => { + local_decl.local_info = Some(box LocalInfo::ConstRef { def_id }); + } _ => {} } this.local_decls.push(local_decl) diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 3d623abfa6e..319fae5009e 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -168,7 +168,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { exit_block.unit() } ExprKind::Call { ty, fun, args, from_hir_call, fn_span } => { - let intrinsic = match ty.kind { + let intrinsic = match *ty.kind() { ty::FnDef(def_id, _) => { let f = ty.fn_sig(this.hir.tcx()); if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 3a525d10b08..a9b8a6181d4 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -321,7 +321,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let target_block = self.cfg.start_new_block(); let mut schedule_drops = true; // We keep a stack of all of the bindings and type asciptions - // from the the parent candidates that we visit, that also need to + // from the parent candidates that we visit, that also need to // be bound for each candidate. traverse_candidate( candidate, @@ -1793,7 +1793,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .flat_map(|(bindings, _)| bindings) .chain(&candidate.bindings) .filter(|binding| { - if let BindingMode::ByValue = binding.binding_mode { true } else { false } + matches!(binding.binding_mode, BindingMode::ByValue ) }); // Read all of the by reference bindings to ensure that the // place they refer to can't be modified by the guard. diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index e584aeb9226..a28a181e935 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -154,7 +154,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } PatKind::Range(PatRange { lo, hi, end }) => { - let (range, bias) = match lo.ty.kind { + let (range, bias) = match *lo.ty.kind() { ty::Char => { (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0) } diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index c4a87a554a3..d81c3b68f48 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -215,7 +215,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestKind::SwitchInt { switch_ty, ref options } => { let target_blocks = make_target_blocks(self); - let terminator = if switch_ty.kind == ty::Bool { + let terminator = if *switch_ty.kind() == ty::Bool { assert!(!options.is_empty() && options.len() <= 2); if let [first_bb, second_bb] = *target_blocks { let (true_bb, false_bb) = match options[0] { @@ -368,8 +368,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // We want to do this even when the scrutinee is a reference to an // array, so we can call `<[u8]>::eq` rather than having to find an // `<[u8; N]>::eq`. - let unsize = |ty: Ty<'tcx>| match ty.kind { - ty::Ref(region, rty, _) => match rty.kind { + let unsize = |ty: Ty<'tcx>| match ty.kind() { + ty::Ref(region, rty, _) => match rty.kind() { ty::Array(inner_ty, n) => Some((region, inner_ty, n)), _ => None, }, @@ -407,7 +407,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - let deref_ty = match ty.kind { + let deref_ty = match *ty.kind() { ty::Ref(_, deref_ty, _) => deref_ty, _ => bug!("non_scalar_compare called on non-reference type: {}", ty), }; diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index c6d39947f7d..4ef88c25cad 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -31,9 +31,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { suffix: &'pat [Pat<'tcx>], ) { let tcx = self.hir.tcx(); - let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind { + let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind() { ty::Array(_, length) => { - (length.eval_usize(tcx, self.hir.param_env).try_into().unwrap(), true) + (length.eval_usize(tcx, self.hir.param_env), true) } _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), }; diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 249cce0ba19..aa96ae87591 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -96,7 +96,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_ let body = tcx.hir().body(body_id); let ty = tcx.type_of(fn_def_id); let mut abi = fn_sig.abi; - let implicit_argument = match ty.kind { + let implicit_argument = match ty.kind() { ty::Closure(..) => { // HACK(eddyb) Avoid having RustCall on closures, // as it adds unnecessary (and wrong) auto-tupling. @@ -159,7 +159,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_ let (yield_ty, return_ty) = if body.generator_kind.is_some() { let gen_ty = tcx.typeck_body(body_id).node_type(id); - let gen_sig = match gen_ty.kind { + let gen_sig = match gen_ty.kind() { ty::Generator(_, gen_substs, ..) => gen_substs.as_generator().sig(), _ => span_bug!(tcx.hir().span(id), "generator w/o generator type: {:?}", ty), }; @@ -228,7 +228,7 @@ fn liberated_closure_env_ty( ) -> Ty<'_> { let closure_ty = tcx.typeck_body(body_id).node_type(closure_expr_id); - let (closure_def_id, closure_substs) = match closure_ty.kind { + let (closure_def_id, closure_substs) = match *closure_ty.kind() { ty::Closure(closure_def_id, closure_substs) => (closure_def_id, closure_substs), _ => bug!("closure expr does not have closure type: {:?}", closure_ty), }; @@ -840,11 +840,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let closure_env_arg = Local::new(1); let mut closure_env_projs = vec![]; let mut closure_ty = self.local_decls[closure_env_arg].ty; - if let ty::Ref(_, ty, _) = closure_ty.kind { + if let ty::Ref(_, ty, _) = closure_ty.kind() { closure_env_projs.push(ProjectionElem::Deref); closure_ty = ty; } - let upvar_substs = match closure_ty.kind { + let upvar_substs = match closure_ty.kind() { ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs), _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty), diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 313bb979a51..714041ad4e8 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -1,11 +1,12 @@ //! Construction of MIR from HIR. //! //! This crate also contains the match exhaustiveness and usefulness checking. - +#![feature(array_windows)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(const_fn)] #![feature(const_panic)] +#![feature(control_flow_enum)] #![feature(crate_visibility_modifier)] #![feature(bool_to_option)] #![feature(or_patterns)] diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index fd2d5a4abd4..a8d7c612a84 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -70,7 +70,7 @@ impl<'mir, 'tcx> Search<'mir, 'tcx> { let param_env = tcx.param_env(def_id); let func_ty = func.ty(body, tcx); - if let ty::FnDef(fn_def_id, substs) = func_ty.kind { + if let ty::FnDef(fn_def_id, substs) = *func_ty.kind() { let (call_fn_id, call_substs) = if let Ok(Some(instance)) = Instance::resolve(tcx, param_env, fn_def_id, substs) { (instance.def_id(), instance.substs) @@ -117,7 +117,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { // A diverging InlineAsm is treated as non-recursing TerminatorKind::InlineAsm { destination, .. } => { if destination.is_some() { - ControlFlow::Continue + ControlFlow::CONTINUE } else { ControlFlow::Break(NonRecursive) } @@ -131,7 +131,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue, + | TerminatorKind::SwitchInt { .. } => ControlFlow::CONTINUE, } } @@ -144,7 +144,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { } } - ControlFlow::Continue + ControlFlow::CONTINUE } fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool { diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index dd5515d39b0..a7bb2864daf 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -2,7 +2,7 @@ use rustc_ast as ast; use rustc_middle::mir::interpret::{ truncate, Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar, }; -use rustc_middle::ty::{self, ParamEnv, TyCtxt, TyS}; +use rustc_middle::ty::{self, ParamEnv, TyCtxt}; use rustc_span::symbol::Symbol; use rustc_target::abi::Size; @@ -21,19 +21,21 @@ crate fn lit_to_const<'tcx>( Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) }; - let lit = match (lit, &ty.kind) { - (ast::LitKind::Str(s, _), ty::Ref(_, TyS { kind: ty::Str, .. }, _)) => { + let lit = match (lit, &ty.kind()) { + (ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => { let s = s.as_str(); let allocation = Allocation::from_byte_aligned_bytes(s.as_bytes()); let allocation = tcx.intern_const_alloc(allocation); ConstValue::Slice { data: allocation, start: 0, end: s.len() } } - (ast::LitKind::ByteStr(data), ty::Ref(_, TyS { kind: ty::Slice(_), .. }, _)) => { + (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) + if matches!(inner_ty.kind(), ty::Slice(_)) => + { let allocation = Allocation::from_byte_aligned_bytes(data as &Vec<u8>); let allocation = tcx.intern_const_alloc(allocation); ConstValue::Slice { data: allocation, start: 0, end: data.len() } } - (ast::LitKind::ByteStr(data), ty::Ref(_, TyS { kind: ty::Array(_, _), .. }, _)) => { + (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { let id = tcx.allocate_bytes(data); ConstValue::Scalar(Scalar::Ptr(id.into())) } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 066e46fec14..13e69474cfb 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -117,7 +117,14 @@ fn apply_adjustment<'a, 'tcx>( }, }; - overloaded_place(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()]) + overloaded_place( + cx, + hir_expr, + adjustment.target, + Some(call), + vec![expr.to_ref()], + deref.span, + ) } Adjust::Borrow(AutoBorrow::Ref(_, m)) => { ExprKind::Borrow { borrow_kind: m.to_borrow_kind(), arg: expr.to_ref() } @@ -247,6 +254,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( hir::ExprKind::Lit(ref lit) => ExprKind::Literal { literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false), user_ty: None, + const_id: None, }, hir::ExprKind::Binary(op, ref lhs, ref rhs) => { @@ -276,7 +284,14 @@ fn make_mirror_unadjusted<'a, 'tcx>( hir::ExprKind::Index(ref lhs, ref index) => { if cx.typeck_results().is_method_call(expr) { - overloaded_place(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()]) + overloaded_place( + cx, + expr, + expr_ty, + None, + vec![lhs.to_ref(), index.to_ref()], + expr.span, + ) } else { ExprKind::Index { lhs: lhs.to_ref(), index: index.to_ref() } } @@ -284,7 +299,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( hir::ExprKind::Unary(hir::UnOp::UnDeref, ref arg) => { if cx.typeck_results().is_method_call(expr) { - overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()]) + overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()], expr.span) } else { ExprKind::Deref { arg: arg.to_ref() } } @@ -306,6 +321,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( ExprKind::Literal { literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true), user_ty: None, + const_id: None, } } else { ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() } @@ -313,7 +329,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( } } - hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind { + hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind() { ty::Adt(adt, substs) => match adt.adt_kind() { AdtKind::Struct | AdtKind::Union => { let user_provided_types = cx.typeck_results().user_provided_types(); @@ -363,7 +379,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( hir::ExprKind::Closure(..) => { let closure_ty = cx.typeck_results().expr_ty(expr); - let (def_id, substs, movability) = match closure_ty.kind { + let (def_id, substs, movability) = match *closure_ty.kind() { ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs), None), ty::Generator(def_id, substs, movability) => { (def_id, UpvarSubsts::Generator(substs), Some(movability)) @@ -447,6 +463,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx, ty), user_ty, + const_id: None, }, } .to_ref(), @@ -473,6 +490,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx, ty), user_ty: None, + const_id: None, }, } .to_ref(), @@ -585,7 +603,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( temp_lifetime, ty: var_ty, span: expr.span, - kind: ExprKind::Literal { literal, user_ty: None }, + kind: ExprKind::Literal { literal, user_ty: None, const_id: None }, } .to_ref() }; @@ -714,7 +732,11 @@ fn method_callee<'a, 'tcx>( temp_lifetime, ty, span, - kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx(), ty), user_ty }, + kind: ExprKind::Literal { + literal: ty::Const::zero_sized(cx.tcx(), ty), + user_ty, + const_id: None, + }, } } @@ -777,6 +799,7 @@ fn convert_path_expr<'a, 'tcx>( ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx, cx.typeck_results().node_type(expr.hir_id)), user_ty, + const_id: None, } } @@ -794,6 +817,7 @@ fn convert_path_expr<'a, 'tcx>( .tcx .mk_const(ty::Const { val, ty: cx.typeck_results().node_type(expr.hir_id) }), user_ty: None, + const_id: Some(def_id), } } @@ -810,6 +834,7 @@ fn convert_path_expr<'a, 'tcx>( ty: cx.typeck_results().node_type(expr.hir_id), }), user_ty, + const_id: Some(def_id), } } @@ -818,7 +843,7 @@ fn convert_path_expr<'a, 'tcx>( let user_provided_type = user_provided_types.get(expr.hir_id).copied(); debug!("convert_path_expr: user_provided_type={:?}", user_provided_type); let ty = cx.typeck_results().node_type(expr.hir_id); - match ty.kind { + match ty.kind() { // A unit struct/variant which is used as a value. // We return a completely different ExprKind here to account for this special case. ty::Adt(adt_def, substs) => ExprKind::Adt { @@ -899,7 +924,7 @@ fn convert_var<'tcx>( }); let region = cx.tcx.mk_region(region); - let self_expr = if let ty::Closure(_, closure_substs) = closure_ty.kind { + let self_expr = if let ty::Closure(_, closure_substs) = closure_ty.kind() { match cx.infcx.closure_kind(closure_substs).unwrap() { ty::ClosureKind::Fn => { let ref_closure_ty = cx.tcx.mk_ref( @@ -1014,6 +1039,7 @@ fn overloaded_place<'a, 'tcx>( place_ty: Ty<'tcx>, overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, args: Vec<ExprRef<'tcx>>, + span: Span, ) -> ExprKind<'tcx> { // For an overloaded *x or x[y] expression of type T, the method // call returns an &T and we must add the deref so that the types @@ -1027,26 +1053,26 @@ fn overloaded_place<'a, 'tcx>( // Reconstruct the output assuming it's a reference with the // same region and mutability as the receiver. This holds for // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. - let (region, mutbl) = match recv_ty.kind { + let (region, mutbl) = match *recv_ty.kind() { ty::Ref(region, _, mutbl) => (region, mutbl), - _ => span_bug!(expr.span, "overloaded_place: receiver is not a reference"), + _ => span_bug!(span, "overloaded_place: receiver is not a reference"), }; let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); // construct the complete expression `foo()` for the overloaded call, // which will yield the &T type let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - let fun = method_callee(cx, expr, expr.span, overloaded_callee); + let fun = method_callee(cx, expr, span, overloaded_callee); let ref_expr = Expr { temp_lifetime, ty: ref_ty, - span: expr.span, + span, kind: ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false, - fn_span: expr.span, + fn_span: span, }, }; diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs index 2837bfa040f..4d57fd5c64f 100644 --- a/compiler/rustc_mir_build/src/thir/mod.rs +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -273,6 +273,10 @@ crate enum ExprKind<'tcx> { Literal { literal: &'tcx Const<'tcx>, user_ty: Option<Canonical<'tcx, UserType<'tcx>>>, + /// The `DefId` of the `const` item this literal + /// was produced from, if this is not a user-written + /// literal value. + const_id: Option<DefId>, }, /// A literal containing the address of a `static`. /// diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 3202f7d1b1b..904524e13ae 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -139,10 +139,10 @@ //! //! It is computed as follows. We look at the pattern `p_1` on top of the stack, //! and we have three cases: -//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. -//! 1.2. `p_1 = _`. We return the rest of the stack: +//! 2.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. +//! 2.2. `p_1 = _`. We return the rest of the stack: //! p_2, .., p_n -//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting +//! 2.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting //! stack. //! D((r_1, p_2, .., p_n)) //! D((r_2, p_2, .., p_n)) @@ -276,7 +276,7 @@ use self::Usefulness::*; use self::WitnessPreference::*; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::vec::Idx; use super::{compare_const_vals, PatternFoldable, PatternFolder}; @@ -327,7 +327,7 @@ impl<'tcx> LiteralExpander<'tcx> { crty: Ty<'tcx>, ) -> ConstValue<'tcx> { debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty); - match (val, &crty.kind, &rty.kind) { + match (val, &crty.kind(), &rty.kind()) { // the easy case, deref a reference (ConstValue::Scalar(p), x, y) if x == y => { match p { @@ -368,41 +368,35 @@ impl<'tcx> LiteralExpander<'tcx> { impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> { fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> { - debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind, pat.kind); - match (&pat.ty.kind, &*pat.kind) { - ( - &ty::Ref(_, rty, _), - &PatKind::Constant { - value: - Const { - val: ty::ConstKind::Value(val), - ty: ty::TyS { kind: ty::Ref(_, crty, _), .. }, - }, - }, - ) => Pat { - ty: pat.ty, - span: pat.span, - kind: box PatKind::Deref { - subpattern: Pat { - ty: rty, + debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind); + match (pat.ty.kind(), &*pat.kind) { + (&ty::Ref(_, rty, _), &PatKind::Constant { value: Const { val, ty: const_ty } }) + if const_ty.is_ref() => + { + let crty = + if let ty::Ref(_, crty, _) = const_ty.kind() { crty } else { unreachable!() }; + if let ty::ConstKind::Value(val) = val { + Pat { + ty: pat.ty, span: pat.span, - kind: box PatKind::Constant { - value: Const::from_value( - self.tcx, - self.fold_const_value_deref(*val, rty, crty), - rty, - ), + kind: box PatKind::Deref { + subpattern: Pat { + ty: rty, + span: pat.span, + kind: box PatKind::Constant { + value: Const::from_value( + self.tcx, + self.fold_const_value_deref(*val, rty, crty), + rty, + ), + }, + }, }, - }, - }, - }, - - ( - &ty::Ref(_, rty, _), - &PatKind::Constant { - value: Const { val, ty: ty::TyS { kind: ty::Ref(_, crty, _), .. } }, - }, - ) => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty), + } + } else { + bug!("cannot deref {:#?}, {} -> {}", val, crty, rty) + } + } (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self), (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self), @@ -422,7 +416,7 @@ impl<'tcx> Pat<'tcx> { /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` /// works well. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] crate struct PatStack<'p, 'tcx>(SmallVec<[&'p Pat<'tcx>; 2]>); impl<'p, 'tcx> PatStack<'p, 'tcx> { @@ -510,13 +504,36 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> { } } +/// Depending on the match patterns, the specialization process might be able to use a fast path. +/// Tracks whether we can use the fast path and the lookup table needed in those cases. +#[derive(Clone, Debug, PartialEq)] +enum SpecializationCache { + /// Patterns consist of only enum variants. + /// Variant patterns does not intersect with each other (in contrast to range patterns), + /// so it is possible to precompute the result of `Matrix::specialize_constructor` at a + /// lower computational complexity. + /// `lookup` is responsible for holding the precomputed result of + /// `Matrix::specialize_constructor`, while `wilds` is used for two purposes: the first one is + /// the precomputed result of `Matrix::specialize_wildcard`, and the second is to be used as a + /// fallback for `Matrix::specialize_constructor` when it tries to apply a constructor that + /// has not been seen in the `Matrix`. See `update_cache` for further explanations. + Variants { lookup: FxHashMap<DefId, SmallVec<[usize; 1]>>, wilds: SmallVec<[usize; 1]> }, + /// Does not belong to the cases above, use the slow path. + Incompatible, +} + /// A 2D matrix. -#[derive(Clone)] -crate struct Matrix<'p, 'tcx>(Vec<PatStack<'p, 'tcx>>); +#[derive(Clone, PartialEq)] +crate struct Matrix<'p, 'tcx> { + patterns: Vec<PatStack<'p, 'tcx>>, + cache: SpecializationCache, +} impl<'p, 'tcx> Matrix<'p, 'tcx> { crate fn empty() -> Self { - Matrix(vec![]) + // Use `SpecializationCache::Incompatible` as a placeholder; we will initialize it on the + // first call to `push`. See the first half of `update_cache`. + Matrix { patterns: vec![], cache: SpecializationCache::Incompatible } } /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it. @@ -528,18 +545,101 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { self.push(row) } } else { - self.0.push(row); + self.patterns.push(row); + self.update_cache(self.patterns.len() - 1); + } + } + + fn update_cache(&mut self, idx: usize) { + let row = &self.patterns[idx]; + // We don't know which kind of cache could be used until we see the first row; therefore an + // empty `Matrix` is initialized with `SpecializationCache::Empty`, then the cache is + // assigned the appropriate variant below on the first call to `push`. + if self.patterns.is_empty() { + self.cache = if row.is_empty() { + SpecializationCache::Incompatible + } else { + match *row.head().kind { + PatKind::Variant { .. } => SpecializationCache::Variants { + lookup: FxHashMap::default(), + wilds: SmallVec::new(), + }, + // Note: If the first pattern is a wildcard, then all patterns after that is not + // useful. The check is simple enough so we treat it as the same as unsupported + // patterns. + _ => SpecializationCache::Incompatible, + } + }; + } + // Update the cache. + match &mut self.cache { + SpecializationCache::Variants { ref mut lookup, ref mut wilds } => { + let head = row.head(); + match *head.kind { + _ if head.is_wildcard() => { + // Per rule 1.3 in the top-level comments, a wildcard pattern is included in + // the result of `specialize_constructor` for *any* `Constructor`. + // We push the wildcard pattern to the precomputed result for constructors + // that we have seen before; results for constructors we have not yet seen + // defaults to `wilds`, which is updated right below. + for (_, v) in lookup.iter_mut() { + v.push(idx); + } + // Per rule 2.1 and 2.2 in the top-level comments, only wildcard patterns + // are included in the result of `specialize_wildcard`. + // What we do here is to track the wildcards we have seen; so in addition to + // acting as the precomputed result of `specialize_wildcard`, `wilds` also + // serves as the default value of `specialize_constructor` for constructors + // that are not in `lookup`. + wilds.push(idx); + } + PatKind::Variant { adt_def, variant_index, .. } => { + // Handle the cases of rule 1.1 and 1.2 in the top-level comments. + // A variant pattern can only be included in the results of + // `specialize_constructor` for a particular constructor, therefore we are + // using a HashMap to track that. + lookup + .entry(adt_def.variants[variant_index].def_id) + // Default to `wilds` for absent keys. See above for an explanation. + .or_insert_with(|| wilds.clone()) + .push(idx); + } + _ => { + self.cache = SpecializationCache::Incompatible; + } + } + } + SpecializationCache::Incompatible => {} } } /// Iterate over the first component of each row fn heads<'a>(&'a self) -> impl Iterator<Item = &'a Pat<'tcx>> + Captures<'p> { - self.0.iter().map(|r| r.head()) + self.patterns.iter().map(|r| r.head()) } /// This computes `D(self)`. See top of the file for explanations. fn specialize_wildcard(&self) -> Self { - self.0.iter().filter_map(|r| r.specialize_wildcard()).collect() + match &self.cache { + SpecializationCache::Variants { wilds, .. } => { + let result = + wilds.iter().filter_map(|&i| self.patterns[i].specialize_wildcard()).collect(); + // When debug assertions are enabled, check the results against the "slow path" + // result. + debug_assert_eq!( + result, + Self { + patterns: self.patterns.clone(), + cache: SpecializationCache::Incompatible + } + .specialize_wildcard() + ); + result + } + SpecializationCache::Incompatible => { + self.patterns.iter().filter_map(|r| r.specialize_wildcard()).collect() + } + } } /// This computes `S(constructor, self)`. See top of the file for explanations. @@ -549,10 +649,47 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, ) -> Matrix<'p, 'tcx> { - self.0 - .iter() - .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) - .collect() + match &self.cache { + SpecializationCache::Variants { lookup, wilds } => { + let result: Self = if let Constructor::Variant(id) = constructor { + lookup + .get(id) + // Default to `wilds` for absent keys. See `update_cache` for an explanation. + .unwrap_or(&wilds) + .iter() + .filter_map(|&i| { + self.patterns[i].specialize_constructor( + cx, + constructor, + ctor_wild_subpatterns, + ) + }) + .collect() + } else { + unreachable!() + }; + // When debug assertions are enabled, check the results against the "slow path" + // result. + debug_assert_eq!( + result, + Matrix { + patterns: self.patterns.clone(), + cache: SpecializationCache::Incompatible + } + .specialize_constructor( + cx, + constructor, + ctor_wild_subpatterns + ) + ); + result + } + SpecializationCache::Incompatible => self + .patterns + .iter() + .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) + .collect(), + } } } @@ -574,7 +711,7 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; - let &Matrix(ref m) = self; + let Matrix { patterns: m, .. } = self; let pretty_printed_matrix: Vec<Vec<String>> = m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect(); @@ -639,7 +776,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { - match ty.kind { + match ty.kind() { ty::Adt(def, ..) => { def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() } @@ -920,14 +1057,14 @@ impl<'tcx> Constructor<'tcx> { let mut subpatterns = fields.all_patterns(); let pat = match self { - Single | Variant(_) => match ty.kind { + Single | Variant(_) => match ty.kind() { ty::Adt(..) | ty::Tuple(..) => { let subpatterns = subpatterns .enumerate() .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) .collect(); - if let ty::Adt(adt, substs) = ty.kind { + if let ty::Adt(adt, substs) = ty.kind() { if adt.is_enum() { PatKind::Variant { adt_def: adt, @@ -1074,7 +1211,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { let wildcard_from_ty = |ty| &*cx.pattern_arena.alloc(Pat::wildcard_from_ty(ty)); let ret = match constructor { - Single | Variant(_) => match ty.kind { + Single | Variant(_) => match ty.kind() { ty::Tuple(ref fs) => { Fields::wildcards_from_tys(cx, fs.into_iter().map(|ty| ty.expect_ty())) } @@ -1125,7 +1262,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } _ => Fields::empty(), }, - Slice(slice) => match ty.kind { + Slice(slice) => match *ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { let arity = slice.arity(); Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) @@ -1443,7 +1580,7 @@ fn all_constructors<'a, 'tcx>( .unwrap(), ) }; - match pcx.ty.kind { + match *pcx.ty.kind() { ty::Bool => { [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect() } @@ -1558,7 +1695,7 @@ struct IntRange<'tcx> { impl<'tcx> IntRange<'tcx> { #[inline] fn is_integral(ty: Ty<'_>) -> bool { - match ty.kind { + match ty.kind() { ty::Char | ty::Int(_) | ty::Uint(_) => true, _ => false, } @@ -1580,7 +1717,7 @@ impl<'tcx> IntRange<'tcx> { #[inline] fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> { - match ty.kind { + match *ty.kind() { ty::Char => Some((Size::from_bytes(4), 0)), ty::Int(ity) => { let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); @@ -1658,7 +1795,7 @@ impl<'tcx> IntRange<'tcx> { // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. fn signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> u128 { - match ty.kind { + match *ty.kind() { ty::Int(ity) => { let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128; 1u128 << (bits - 1) @@ -1830,7 +1967,7 @@ crate fn is_useful<'p, 'tcx>( is_under_guard: bool, is_top_level: bool, ) -> Usefulness<'tcx> { - let &Matrix(ref rows) = matrix; + let Matrix { patterns: rows, .. } = matrix; debug!("is_useful({:#?}, {:#?})", matrix, v); // The base case. We are pattern-matching on () and the return value is @@ -2070,7 +2207,7 @@ fn pat_constructor<'tcx>( if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) { Some(IntRange(int_range)) } else { - match (value.val, &value.ty.kind) { + match (value.val, &value.ty.kind()) { (_, ty::Array(_, n)) => { let len = n.eval_usize(tcx, param_env); Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) })) @@ -2102,7 +2239,7 @@ fn pat_constructor<'tcx>( } PatKind::Array { ref prefix, ref slice, ref suffix } | PatKind::Slice { ref prefix, ref slice, ref suffix } => { - let array_len = match pat.ty.kind { + let array_len = match pat.ty.kind() { ty::Array(_, length) => Some(length.eval_usize(tcx, param_env)), ty::Slice(_) => None, _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty), @@ -2141,7 +2278,7 @@ fn slice_pat_covered_by_const<'tcx>( ) }; - let data: &[u8] = match (const_val_val, &const_val.ty.kind) { + let data: &[u8] = match (const_val_val, &const_val.ty.kind()) { (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => { assert_eq!(*t, tcx.types.u8); let n = n.eval_usize(tcx, param_env); @@ -2272,7 +2409,7 @@ fn split_grouped_constructors<'p, 'tcx>( // `borders` is the set of borders between equivalence classes: each equivalence // class lies between 2 borders. let row_borders = matrix - .0 + .patterns .iter() .flat_map(|row| { IntRange::from_pat(tcx, param_env, row.head()).map(|r| (r, row.len())) @@ -2305,19 +2442,19 @@ fn split_grouped_constructors<'p, 'tcx>( // interval into a constructor. split_ctors.extend( borders - .windows(2) - .filter_map(|window| match (window[0], window[1]) { - (Border::JustBefore(n), Border::JustBefore(m)) => { + .array_windows() + .filter_map(|&pair| match pair { + [Border::JustBefore(n), Border::JustBefore(m)] => { if n < m { Some(IntRange { range: n..=(m - 1), ty, span }) } else { None } } - (Border::JustBefore(n), Border::AfterMax) => { + [Border::JustBefore(n), Border::AfterMax] => { Some(IntRange { range: n..=u128::MAX, ty, span }) } - (Border::AfterMax, _) => None, + [Border::AfterMax, _] => None, }) .map(IntRange), ); @@ -2561,7 +2698,7 @@ fn specialize_one_pattern<'p, 'tcx>( // elements don't necessarily point to memory, they are usually // just integers. The only time they should be pointing to memory // is when they are subslices of nonzero slices. - let (alloc, offset, n, ty) = match value.ty.kind { + let (alloc, offset, n, ty) = match value.ty.kind() { ty::Array(t, n) => { let n = n.eval_usize(cx.tcx, cx.param_env); // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce, 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 4e7108667e1..047bf7db4c8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -289,7 +289,7 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa cx.typeck_results.extract_binding_mode(cx.tcx.sess, p.hir_id, p.span) { let pat_ty = cx.typeck_results.pat_ty(p).peel_refs(); - if let ty::Adt(edef, _) = pat_ty.kind { + if let ty::Adt(edef, _) = pat_ty.kind() { if edef.is_enum() && edef.variants.iter().any(|variant| { variant.ident == ident && variant.ctor_kind == CtorKind::Const @@ -442,7 +442,7 @@ fn check_exhaustive<'p, 'tcx>( // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by // `is_useful` to exhaustively match uninhabited types, so we manually check here. if is_empty_match && !cx.tcx.features().exhaustive_patterns { - let scrutinee_is_visibly_uninhabited = match scrut_ty.kind { + let scrutinee_is_visibly_uninhabited = match scrut_ty.kind() { ty::Never => true, ty::Adt(def, _) => { def.is_enum() @@ -462,7 +462,7 @@ fn check_exhaustive<'p, 'tcx>( Err(err) => err, }; - let non_empty_enum = match scrut_ty.kind { + let non_empty_enum = match scrut_ty.kind() { ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(), _ => false, }; @@ -541,7 +541,7 @@ fn adt_defined_here( witnesses: &[super::Pat<'_>], ) { let ty = ty.peel_refs(); - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { if let Some(sp) = cx.tcx.hir().span_if_local(def.did) { err.span_label(sp, format!("`{}` defined here", ty)); } @@ -556,7 +556,7 @@ fn adt_defined_here( fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> { let mut covered = vec![]; - if let ty::Adt(def, _) = ty.kind { + if let ty::Adt(def, _) = ty.kind() { // Don't point at variants that have already been covered due to other patterns to avoid // visual clutter. for pattern in patterns { 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 f6d3ccc1ae0..2816bad7eab 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 @@ -2,6 +2,7 @@ use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::Field; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::Span; @@ -118,7 +119,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } if let Some(non_sm_ty) = structural { - let msg = match non_sm_ty { + let msg = with_no_trimmed_paths(|| match non_sm_ty { traits::NonStructuralMatchTy::Adt(adt_def) => { let path = self.tcx().def_path_str(adt_def.did); format!( @@ -148,7 +149,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { traits::NonStructuralMatchTy::Foreign => { bug!("use of a value of a foreign type inside a pattern") } - }; + }); // double-check there even *is* a semantic `PartialEq` to dispatch to. // @@ -216,7 +217,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .collect() }; - let kind = match cv.ty.kind { + let kind = match cv.ty.kind() { ty::Float(_) => { tcx.struct_span_lint_hir( lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, @@ -246,11 +247,9 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { PatKind::Wild } // keep old code until future-compat upgraded to errors. - ty::Ref(_, adt_ty @ ty::TyS { kind: ty::Adt(_, _), .. }, _) - if !self.type_marked_structural(adt_ty) => - { + ty::Ref(_, adt_ty, _) if adt_ty.is_adt() && !self.type_marked_structural(adt_ty) => { let adt_def = - if let ty::Adt(adt_def, _) = adt_ty.kind { adt_def } else { unreachable!() }; + if let ty::Adt(adt_def, _) = adt_ty.kind() { adt_def } else { unreachable!() }; debug!( "adt_def {:?} has !type_marked_structural for adt_ty: {:?}", diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index c163cb0e60c..718ed78889f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -39,19 +39,19 @@ crate enum PatternError { NonConstPath(Span), } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq)] crate enum BindingMode { ByValue, ByRef(BorrowKind), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] crate struct FieldPat<'tcx> { crate field: Field, crate pattern: Pat<'tcx>, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] crate struct Pat<'tcx> { crate ty: Ty<'tcx>, crate span: Span, @@ -116,7 +116,7 @@ crate struct Ascription<'tcx> { crate user_ty_span: Span, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] crate enum PatKind<'tcx> { Wild, @@ -237,7 +237,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { Some(&adt_def.variants[variant_index]) } _ => { - if let ty::Adt(adt, _) = self.ty.kind { + if let ty::Adt(adt, _) = self.ty.kind() { if !adt.is_enum() { Some(&adt.variants[VariantIdx::new(0)]) } else { @@ -302,7 +302,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { Ok(()) } PatKind::Deref { ref subpattern } => { - match self.ty.kind { + match self.ty.kind() { ty::Adt(def, _) if def.is_box() => write!(f, "box ")?, ty::Ref(_, _, mutbl) => { write!(f, "&{}", mutbl.prefix_str())?; @@ -559,7 +559,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } hir::PatKind::Tuple(ref pats, ddpos) => { - let tys = match ty.kind { + let tys = match ty.kind() { ty::Tuple(ref tys) => tys, _ => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty), }; @@ -588,7 +588,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // x's type, which is &T, where we want T (the type being matched). let var_ty = ty; if let ty::BindByReference(_) = bm { - if let ty::Ref(_, rty, _) = ty.kind { + if let ty::Ref(_, rty, _) = ty.kind() { ty = rty; } else { bug!("`ref {}` has wrong type {}", ident, ty); @@ -608,7 +608,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::TupleStruct(ref qpath, ref pats, ddpos) => { let res = self.typeck_results.qpath_res(qpath, pat.hir_id); - let adt_def = match ty.kind { + let adt_def = match ty.kind() { ty::Adt(adt_def, _) => adt_def, _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty), }; @@ -670,7 +670,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let prefix = self.lower_patterns(prefix); let slice = self.lower_opt_pattern(slice); let suffix = self.lower_patterns(suffix); - match ty.kind { + match ty.kind() { // Matching a slice, `[T]`. ty::Slice(..) => PatKind::Slice { prefix, slice, suffix }, // Fixed-length array, `[T; len]`. @@ -704,7 +704,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let enum_id = self.tcx.parent(variant_id).unwrap(); let adt_def = self.tcx.adt_def(enum_id); if adt_def.is_enum() { - let substs = match ty.kind { + let substs = match ty.kind() { ty::Adt(_, substs) | ty::FnDef(_, substs) => substs, ty::Error(_) => { // Avoid ICE (#50585) @@ -1058,7 +1058,7 @@ crate fn compare_const_vals<'tcx>( if let (Some(a), Some(b)) = (a_bits, b_bits) { use rustc_apfloat::Float; - return match ty.kind { + return match *ty.kind() { ty::Float(ast::FloatTy::F32) => { let l = ::rustc_apfloat::ieee::Single::from_bits(a); let r = ::rustc_apfloat::ieee::Single::from_bits(b); @@ -1081,7 +1081,7 @@ crate fn compare_const_vals<'tcx>( }; } - if let ty::Str = ty.kind { + if let ty::Str = ty.kind() { if let ( ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }), ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }), diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs index 7de60ddda41..aea8667314f 100644 --- a/compiler/rustc_mir_build/src/thir/util.rs +++ b/compiler/rustc_mir_build/src/thir/util.rs @@ -17,7 +17,7 @@ crate trait UserAnnotatedTyHelpers<'tcx> { let mut user_ty = *user_provided_types.get(hir_id)?; debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty); let ty = self.typeck_results().node_type(hir_id); - match ty.kind { + match ty.kind() { ty::Adt(adt_def, ..) => { if let UserType::TypeOf(ref mut did, _) = &mut user_ty.value { *did = adt_def.did; diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index a65d3446819..32b124970cf 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -1,21 +1,19 @@ use rustc_ast::ast::AttrStyle; use rustc_ast::token::{self, CommentKind, Token, TokenKind}; -use rustc_data_structures::sync::Lrc; -use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError}; -use rustc_lexer::Base; -use rustc_lexer::{unescape, RawStrError}; +use rustc_ast::tokenstream::{Spacing, TokenStream}; +use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError, PResult}; +use rustc_lexer::unescape::{self, Mode}; +use rustc_lexer::{Base, DocStyle, RawStrError}; use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Pos, Span}; -use std::char; use tracing::debug; mod tokentrees; mod unescape_error_reporting; mod unicode_chars; -use rustc_lexer::{unescape::Mode, DocStyle}; use unescape_error_reporting::{emit_unescape_error, push_escaped_char}; #[derive(Clone, Debug)] @@ -27,7 +25,17 @@ pub struct UnmatchedBrace { pub candidate_span: Option<Span>, } -pub struct StringReader<'a> { +crate fn parse_token_trees<'a>( + sess: &'a ParseSess, + src: &'a str, + start_pos: BytePos, + override_span: Option<Span>, +) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) { + StringReader { sess, start_pos, pos: start_pos, end_src_index: src.len(), src, override_span } + .into_token_trees() +} + +struct StringReader<'a> { sess: &'a ParseSess, /// Initial position, read-only. start_pos: BytePos, @@ -36,71 +44,55 @@ pub struct StringReader<'a> { /// Stop reading src at this index. end_src_index: usize, /// Source text to tokenize. - src: Lrc<String>, + src: &'a str, override_span: Option<Span>, } impl<'a> StringReader<'a> { - pub fn new( - sess: &'a ParseSess, - source_file: Lrc<rustc_span::SourceFile>, - override_span: Option<Span>, - ) -> Self { - let src = source_file.src.clone().unwrap_or_else(|| { - sess.span_diagnostic - .bug(&format!("cannot lex `source_file` without source: {}", source_file.name)); - }); - - StringReader { - sess, - start_pos: source_file.start_pos, - pos: source_file.start_pos, - end_src_index: src.len(), - src, - override_span, - } - } - fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span { self.override_span.unwrap_or_else(|| Span::with_root_ctxt(lo, hi)) } - /// Returns the next token, including trivia like whitespace or comments. - pub fn next_token(&mut self) -> Token { + /// Returns the next token, and info about preceding whitespace, if any. + fn next_token(&mut self) -> (Spacing, Token) { + let mut spacing = Spacing::Joint; + + // Skip `#!` at the start of the file let start_src_index = self.src_index(self.pos); let text: &str = &self.src[start_src_index..self.end_src_index]; - - if text.is_empty() { - let span = self.mk_sp(self.pos, self.pos); - return Token::new(token::Eof, span); + let is_beginning_of_file = self.pos == self.start_pos; + if is_beginning_of_file { + if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { + self.pos = self.pos + BytePos::from_usize(shebang_len); + spacing = Spacing::Alone; + } } - { - let is_beginning_of_file = self.pos == self.start_pos; - if is_beginning_of_file { - if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { - let start = self.pos; - self.pos = self.pos + BytePos::from_usize(shebang_len); - - let sym = self.symbol_from(start + BytePos::from_usize("#!".len())); - let kind = token::Shebang(sym); + // Skip trivial (whitespace & comments) tokens + loop { + let start_src_index = self.src_index(self.pos); + let text: &str = &self.src[start_src_index..self.end_src_index]; - let span = self.mk_sp(start, self.pos); - return Token::new(kind, span); - } + if text.is_empty() { + let span = self.mk_sp(self.pos, self.pos); + return (spacing, Token::new(token::Eof, span)); } - } - let token = rustc_lexer::first_token(text); + let token = rustc_lexer::first_token(text); - let start = self.pos; - self.pos = self.pos + BytePos::from_usize(token.len); + let start = self.pos; + self.pos = self.pos + BytePos::from_usize(token.len); - debug!("try_next_token: {:?}({:?})", token.kind, self.str_from(start)); + debug!("next_token: {:?}({:?})", token.kind, self.str_from(start)); - let kind = self.cook_lexer_token(token.kind, start); - let span = self.mk_sp(start, self.pos); - Token::new(kind, span) + match self.cook_lexer_token(token.kind, start) { + Some(kind) => { + let span = self.mk_sp(start, self.pos); + return (spacing, Token::new(kind, span)); + } + None => spacing = Spacing::Alone, + } + } } /// Report a fatal lexical error with a given span. @@ -140,19 +132,16 @@ impl<'a> StringReader<'a> { /// Turns simple `rustc_lexer::TokenKind` enum into a rich /// `librustc_ast::TokenKind`. This turns strings into interned /// symbols and runs additional validation. - fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> TokenKind { - match token { + fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> Option<TokenKind> { + Some(match token { rustc_lexer::TokenKind::LineComment { doc_style } => { - match doc_style { - Some(doc_style) => { - // Opening delimiter of the length 3 is not included into the symbol. - let content_start = start + BytePos(3); - let content = self.str_from(content_start); + // Skip non-doc comments + let doc_style = doc_style?; - self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style) - } - None => token::Comment, - } + // Opening delimiter of the length 3 is not included into the symbol. + let content_start = start + BytePos(3); + let content = self.str_from(content_start); + self.cook_doc_comment(content_start, content, CommentKind::Line, doc_style) } rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => { if !terminated { @@ -171,20 +160,18 @@ impl<'a> StringReader<'a> { .emit(); FatalError.raise(); } - match doc_style { - Some(doc_style) => { - // Opening delimiter of the length 3 and closing delimiter of the length 2 - // are not included into the symbol. - let content_start = start + BytePos(3); - let content_end = self.pos - BytePos(if terminated { 2 } else { 0 }); - let content = self.str_from_to(content_start, content_end); - - self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style) - } - None => token::Comment, - } + + // Skip non-doc comments + let doc_style = doc_style?; + + // Opening delimiter of the length 3 and closing delimiter of the length 2 + // are not included into the symbol. + let content_start = start + BytePos(3); + let content_end = self.pos - BytePos(if terminated { 2 } else { 0 }); + let content = self.str_from_to(content_start, content_end); + self.cook_doc_comment(content_start, content, CommentKind::Block, doc_style) } - rustc_lexer::TokenKind::Whitespace => token::Whitespace, + rustc_lexer::TokenKind::Whitespace => return None, rustc_lexer::TokenKind::Ident | rustc_lexer::TokenKind::RawIdent => { let is_raw_ident = token == rustc_lexer::TokenKind::RawIdent; let mut ident_start = start; @@ -282,12 +269,11 @@ impl<'a> StringReader<'a> { // this should be inside `rustc_lexer`. However, we should first remove compound // tokens like `<<` from `rustc_lexer`, and then add fancier error recovery to it, // as there will be less overall work to do this way. - let token = unicode_chars::check_for_substitution(self, start, c, &mut err) - .unwrap_or_else(|| token::Unknown(self.symbol_from(start))); + let token = unicode_chars::check_for_substitution(self, start, c, &mut err); err.emit(); - token + token? } - } + }) } fn cook_doc_comment( @@ -439,10 +425,6 @@ impl<'a> StringReader<'a> { (lit_kind, id) } - pub fn pos(&self) -> BytePos { - self.pos - } - #[inline] fn src_index(&self, pos: BytePos) -> usize { (pos - self.start_pos).to_usize() @@ -454,12 +436,6 @@ impl<'a> StringReader<'a> { self.str_from_to(start, self.pos) } - /// Creates a Symbol from a given offset to the current offset. - fn symbol_from(&self, start: BytePos) -> Symbol { - debug!("taking an ident from {:?} to {:?}", start, self.pos); - Symbol::intern(self.str_from(start)) - } - /// As symbol_from, with an explicit endpoint. fn symbol_from_to(&self, start: BytePos, end: BytePos) -> Symbol { debug!("taking an ident from {:?} to {:?}", start, end); diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index c08659ec9f6..6233549dc85 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -3,8 +3,8 @@ use super::{StringReader, UnmatchedBrace}; use rustc_ast::token::{self, DelimToken, Token}; use rustc_ast::tokenstream::{ DelimSpan, - IsJoint::{self, *}, - TokenStream, TokenTree, TreeAndJoint, + Spacing::{self, *}, + TokenStream, TokenTree, TreeAndSpacing, }; use rustc_ast_pretty::pprust::token_to_string; use rustc_data_structures::fx::FxHashMap; @@ -12,11 +12,10 @@ use rustc_errors::PResult; use rustc_span::Span; impl<'a> StringReader<'a> { - crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) { + pub(super) fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) { let mut tt_reader = TokenTreesReader { string_reader: self, token: Token::dummy(), - joint_to_prev: Joint, open_braces: Vec::new(), unmatched_braces: Vec::new(), matching_delim_spans: Vec::new(), @@ -32,7 +31,6 @@ impl<'a> StringReader<'a> { struct TokenTreesReader<'a> { string_reader: StringReader<'a>, token: Token, - joint_to_prev: IsJoint, /// Stack of open delimiters and their spans. Used for error message. open_braces: Vec<(token::DelimToken, Span)>, unmatched_braces: Vec<UnmatchedBrace>, @@ -53,7 +51,7 @@ impl<'a> TokenTreesReader<'a> { fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> { let mut buf = TokenStreamBuilder::default(); - self.real_token(); + self.bump(); while self.token != token::Eof { buf.push(self.parse_token_tree()?); } @@ -79,7 +77,7 @@ impl<'a> TokenTreesReader<'a> { } } - fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> { + fn parse_token_tree(&mut self) -> PResult<'a, TreeAndSpacing> { let sm = self.string_reader.sess.source_map(); match self.token.kind { @@ -126,7 +124,7 @@ impl<'a> TokenTreesReader<'a> { // Parse the open delimiter. self.open_braces.push((delim, self.token.span)); - self.real_token(); + self.bump(); // Parse the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user @@ -151,12 +149,9 @@ impl<'a> TokenTreesReader<'a> { } } - match (open_brace, delim) { - //only add braces - (DelimToken::Brace, DelimToken::Brace) => { - self.matching_block_spans.push((open_brace_span, close_brace_span)); - } - _ => {} + //only add braces + if let (DelimToken::Brace, DelimToken::Brace) = (open_brace, delim) { + self.matching_block_spans.push((open_brace_span, close_brace_span)); } if self.open_braces.is_empty() { @@ -171,7 +166,7 @@ impl<'a> TokenTreesReader<'a> { )); } // Parse the closing delimiter. - self.real_token(); + self.bump(); } // Incorrect delimiter. token::CloseDelim(other) => { @@ -217,7 +212,7 @@ impl<'a> TokenTreesReader<'a> { // bar(baz( // } // Incorrect delimiter but matches the earlier `{` if !self.open_braces.iter().any(|&(b, _)| b == other) { - self.real_token(); + self.bump(); } } token::Eof => { @@ -264,37 +259,29 @@ impl<'a> TokenTreesReader<'a> { } _ => { let tt = TokenTree::Token(self.token.take()); - self.real_token(); - let is_joint = self.joint_to_prev == Joint && self.token.is_op(); - Ok((tt, if is_joint { Joint } else { NonJoint })) + let mut spacing = self.bump(); + if !self.token.is_op() { + spacing = Alone; + } + Ok((tt, spacing)) } } } - fn real_token(&mut self) { - self.joint_to_prev = Joint; - loop { - let token = self.string_reader.next_token(); - match token.kind { - token::Whitespace | token::Comment | token::Shebang(_) | token::Unknown(_) => { - self.joint_to_prev = NonJoint; - } - _ => { - self.token = token; - return; - } - } - } + fn bump(&mut self) -> Spacing { + let (spacing, token) = self.string_reader.next_token(); + self.token = token; + spacing } } #[derive(Default)] struct TokenStreamBuilder { - buf: Vec<TreeAndJoint>, + buf: Vec<TreeAndSpacing>, } impl TokenStreamBuilder { - fn push(&mut self, (tree, joint): TreeAndJoint) { + fn push(&mut self, (tree, joint): TreeAndSpacing) { if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() { if let TokenTree::Token(token) = &tree { if let Some(glued) = prev_token.glue(token) { diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index ac395f6cbc2..40e2e34aa05 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -303,7 +303,7 @@ const UNICODE_ARRAY: &[(char, &str, char)] = &[ // However, we should first remove compound tokens like `<<` from `rustc_lexer`, and then add // fancier error recovery to it, as there will be less overall work to do this way. const ASCII_ARRAY: &[(char, &str, Option<token::TokenKind>)] = &[ - (' ', "Space", Some(token::Whitespace)), + (' ', "Space", None), ('_', "Underscore", Some(token::Ident(kw::Underscore, false))), ('-', "Minus/Hyphen", Some(token::BinOp(token::Minus))), (',', "Comma", Some(token::Comma)), @@ -332,7 +332,7 @@ const ASCII_ARRAY: &[(char, &str, Option<token::TokenKind>)] = &[ ('"', "Quotation Mark", None), ]; -crate fn check_for_substitution<'a>( +pub(super) fn check_for_substitution<'a>( reader: &StringReader<'a>, pos: BytePos, ch: char, diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index bc857c97742..21bbdc9ba8d 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -3,12 +3,12 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(bindings_after_at)] -#![feature(try_blocks)] +#![feature(iter_order_by)] #![feature(or_patterns)] use rustc_ast as ast; use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{self, IsJoint, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{self, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; @@ -109,7 +109,7 @@ pub fn maybe_new_parser_from_source_str( } /// Creates a new parser, handling errors as appropriate if the file doesn't exist. -/// If a span is given, that is used on an error as the as the source of the problem. +/// If a span is given, that is used on an error as the source of the problem. pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Span>) -> Parser<'a> { source_file_to_parser(sess, file_to_source_file(sess, path, sp)) } @@ -200,8 +200,13 @@ pub fn maybe_file_to_stream( source_file: Lrc<SourceFile>, override_span: Option<Span>, ) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> { - let srdr = lexer::StringReader::new(sess, source_file, override_span); - let (token_trees, unmatched_braces) = srdr.into_token_trees(); + let src = source_file.src.as_ref().unwrap_or_else(|| { + sess.span_diagnostic + .bug(&format!("cannot lex `source_file` without source: {}", source_file.name)); + }); + + let (token_trees, unmatched_braces) = + lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span); match token_trees { Ok(stream) => Ok((stream, unmatched_braces)), @@ -263,21 +268,32 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke Nonterminal::NtItem(ref item) => { prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span) } + Nonterminal::NtBlock(ref block) => block.tokens.clone(), + Nonterminal::NtStmt(ref stmt) => { + // FIXME: We currently only collect tokens for `:stmt` + // matchers in `macro_rules!` macros. When we start collecting + // tokens for attributes on statements, we will need to prepend + // attributes here + stmt.tokens.clone() + } Nonterminal::NtPat(ref pat) => pat.tokens.clone(), + Nonterminal::NtTy(ref ty) => ty.tokens.clone(), Nonterminal::NtIdent(ident, is_raw) => { Some(tokenstream::TokenTree::token(token::Ident(ident.name, is_raw), ident.span).into()) } Nonterminal::NtLifetime(ident) => { Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into()) } + Nonterminal::NtMeta(ref attr) => attr.tokens.clone(), + Nonterminal::NtPath(ref path) => path.tokens.clone(), + Nonterminal::NtVis(ref vis) => vis.tokens.clone(), Nonterminal::NtTT(ref tt) => Some(tt.clone().into()), - Nonterminal::NtExpr(ref expr) => { + Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => { if expr.tokens.is_none() { debug!("missing tokens for expr {:?}", expr); } prepend_attrs(sess, &expr.attrs, expr.tokens.as_ref(), span) } - _ => None, }; // FIXME(#43081): Avoid this pretty-print + reparse hack @@ -348,9 +364,12 @@ pub fn tokenstream_probably_equal_for_proc_macro( | token::CloseDelim(DelimToken::NoDelim) // The pretty printer collapses many semicolons into one. | token::Semi - // The pretty printer collapses whitespace arbitrarily and can - // introduce whitespace from `NoDelim`. - | token::Whitespace + // We don't preserve leading `|` tokens in patterns, so + // we ignore them entirely + | token::BinOp(token::BinOpToken::Or) + // We don't preserve trailing '+' tokens in trait bounds, + // so we ignore them entirely + | token::BinOp(token::BinOpToken::Plus) // The pretty printer can turn `$crate` into `::crate_name` | token::ModSep = token.kind { return false; @@ -435,20 +454,16 @@ pub fn tokenstream_probably_equal_for_proc_macro( // issue #75734 tracks resolving this. nt_to_tokenstream(nt, sess, *span).into_trees() } else { - TokenStream::new(vec![(tree, IsJoint::NonJoint)]).into_trees() + TokenStream::new(vec![(tree, Spacing::Alone)]).into_trees() } }; // Break tokens after we expand any nonterminals, so that we break tokens // that are produced as a result of nonterminal expansion. - let mut t1 = first.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); - let mut t2 = other.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); - for (t1, t2) in t1.by_ref().zip(t2.by_ref()) { - if !tokentree_probably_equal_for_proc_macro(&t1, &t2, sess) { - return false; - } - } - t1.next().is_none() && t2.next().is_none() + let t1 = first.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); + let t2 = other.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens); + + t1.eq_by(t2, |t1, t2| tokentree_probably_equal_for_proc_macro(&t1, &t2, sess)) } // See comments in `Nonterminal::to_tokenstream` for why we care about @@ -506,8 +521,6 @@ fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool { | (&Pound, &Pound) | (&Dollar, &Dollar) | (&Question, &Question) - | (&Whitespace, &Whitespace) - | (&Comment, &Comment) | (&Eof, &Eof) => true, (&BinOp(a), &BinOp(b)) | (&BinOpEq(a), &BinOpEq(b)) => a == b, @@ -516,8 +529,6 @@ fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool { (&DocComment(a1, a2, a3), &DocComment(b1, b2, b3)) => a1 == b1 && a2 == b2 && a3 == b3, - (&Shebang(a), &Shebang(b)) => a == b, - (&Literal(a), &Literal(b)) => a == b, (&Lifetime(a), &Lifetime(b)) => a == b, diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 4e4429e461f..98f94098bfc 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -162,7 +162,7 @@ impl<'a> Parser<'a> { } else { let path = self.parse_path(PathStyle::Mod)?; let args = self.parse_attr_args()?; - ast::AttrItem { path, args } + ast::AttrItem { path, args, tokens: None } }) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 12efe391fb9..9ab13db4b5f 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -28,7 +28,7 @@ pub(super) fn dummy_arg(ident: Ident) -> Param { span: ident.span, tokens: None, }); - let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID }; + let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID, tokens: None }; Param { attrs: AttrVec::default(), id: ast::DUMMY_NODE_ID, @@ -75,7 +75,12 @@ impl RecoverQPath for Ty { Some(P(self.clone())) } fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self { - Self { span: path.span, kind: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID } + Self { + span: path.span, + kind: TyKind::Path(qself, path), + id: ast::DUMMY_NODE_ID, + tokens: None, + } } } @@ -548,6 +553,52 @@ impl<'a> Parser<'a> { } } + /// When writing a turbofish with multiple type parameters missing the leading `::`, we will + /// encounter a parse error when encountering the first `,`. + pub(super) fn check_mistyped_turbofish_with_multiple_type_params( + &mut self, + mut e: DiagnosticBuilder<'a>, + expr: &mut P<Expr>, + ) -> PResult<'a, ()> { + if let ExprKind::Binary(binop, _, _) = &expr.kind { + if let ast::BinOpKind::Lt = binop.node { + if self.eat(&token::Comma) { + let x = self.parse_seq_to_before_end( + &token::Gt, + SeqSep::trailing_allowed(token::Comma), + |p| p.parse_ty(), + ); + match x { + Ok((_, _, false)) => { + self.bump(); // `>` + match self.parse_expr() { + Ok(_) => { + e.span_suggestion_verbose( + binop.span.shrink_to_lo(), + "use `::<...>` instead of `<...>` to specify type arguments", + "::".to_string(), + Applicability::MaybeIncorrect, + ); + e.emit(); + *expr = self.mk_expr_err(expr.span.to(self.prev_token.span)); + return Ok(()); + } + Err(mut err) => { + err.cancel(); + } + } + } + Err(mut err) => { + err.cancel(); + } + _ => {} + } + } + } + } + Err(e) + } + /// Check to see if a pair of chained operators looks like an attempt at chained comparison, /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or /// parenthesising the leftmost comparison. @@ -896,7 +947,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P<T>> { self.expect(&token::ModSep)?; - let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP }; + let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP, tokens: None }; self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?; path.span = ty_span.to(self.prev_token.span); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f022c628fe2..69d13b5cf53 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1480,7 +1480,7 @@ impl<'a> Parser<'a> { /// Matches `'-' lit | lit` (cf. `ast_validation::AstValidator::check_expr_within_pat`). /// Keep this in sync with `Token::can_begin_literal_maybe_minus`. - pub(super) fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> { + pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> { maybe_whole_expr!(self); let lo = self.token.span; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9143af651df..26ca9980127 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -28,7 +28,7 @@ impl<'a> Parser<'a> { /// Parses a source module as a crate. This is the main entry point for the parser. pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> { let lo = self.token.span; - let (module, attrs) = self.parse_mod(&token::Eof)?; + let (module, attrs) = self.parse_mod(&token::Eof, Unsafe::No)?; let span = lo.to(self.token.span); let proc_macros = Vec::new(); // Filled in by `proc_macro_harness::inject()`. Ok(ast::Crate { attrs, module, span, proc_macros }) @@ -36,27 +36,38 @@ impl<'a> Parser<'a> { /// Parses a `mod <foo> { ... }` or `mod <foo>;` item. fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> { + let unsafety = self.parse_unsafety(); + self.expect_keyword(kw::Mod)?; let id = self.parse_ident()?; let (module, mut inner_attrs) = if self.eat(&token::Semi) { - Default::default() + (Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false }, Vec::new()) } else { self.expect(&token::OpenDelim(token::Brace))?; - self.parse_mod(&token::CloseDelim(token::Brace))? + self.parse_mod(&token::CloseDelim(token::Brace), unsafety)? }; attrs.append(&mut inner_attrs); Ok((id, ItemKind::Mod(module))) } /// Parses the contents of a module (inner attributes followed by module items). - pub fn parse_mod(&mut self, term: &TokenKind) -> PResult<'a, (Mod, Vec<Attribute>)> { + pub fn parse_mod( + &mut self, + term: &TokenKind, + unsafety: Unsafe, + ) -> PResult<'a, (Mod, Vec<Attribute>)> { let lo = self.token.span; let attrs = self.parse_inner_attributes()?; - let module = self.parse_mod_items(term, lo)?; + let module = self.parse_mod_items(term, lo, unsafety)?; Ok((module, attrs)) } /// Given a termination token, parses all of the items in a module. - fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> { + fn parse_mod_items( + &mut self, + term: &TokenKind, + inner_lo: Span, + unsafety: Unsafe, + ) -> PResult<'a, Mod> { let mut items = vec![]; while let Some(item) = self.parse_item()? { items.push(item); @@ -75,7 +86,7 @@ impl<'a> Parser<'a> { let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span }; - Ok(Mod { inner: inner_lo.to(hi), items, inline: true }) + Ok(Mod { inner: inner_lo.to(hi), unsafety, items, inline: true }) } } @@ -176,7 +187,7 @@ impl<'a> Parser<'a> { /// Error in-case a non-inherited visibility was parsed but no item followed. fn error_on_unmatched_vis(&self, vis: &Visibility) { - if let VisibilityKind::Inherited = vis.node { + if let VisibilityKind::Inherited = vis.kind { return; } let vs = pprust::vis_to_string(&vis); @@ -235,8 +246,13 @@ impl<'a> Parser<'a> { self.parse_item_extern_crate()? } else { // EXTERN BLOCK - self.parse_item_foreign_mod(attrs)? + self.parse_item_foreign_mod(attrs, Unsafe::No)? } + } else if self.is_unsafe_foreign_mod() { + // EXTERN BLOCK + let unsafety = self.parse_unsafety(); + self.expect_keyword(kw::Extern)?; + self.parse_item_foreign_mod(attrs, unsafety)? } else if self.is_static_global() { // STATIC ITEM self.bump(); // `static` @@ -256,7 +272,9 @@ impl<'a> Parser<'a> { { // IMPL ITEM self.parse_item_impl(attrs, def())? - } else if self.eat_keyword(kw::Mod) { + } else if self.check_keyword(kw::Mod) + || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod]) + { // MODULE ITEM self.parse_item_mod(attrs)? } else if self.eat_keyword(kw::Type) { @@ -278,7 +296,7 @@ impl<'a> Parser<'a> { } else if self.is_macro_rules_item() { // MACRO_RULES ITEM self.parse_item_macro_rules(vis)? - } else if vis.node.is_pub() && self.isnt_macro_invocation() { + } 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() { @@ -492,7 +510,12 @@ impl<'a> Parser<'a> { { let span = self.prev_token.span.between(self.token.span); self.struct_span_err(span, "missing trait in a trait impl").emit(); - P(Ty { kind: TyKind::Path(None, err_path(span)), span, id: DUMMY_NODE_ID }) + P(Ty { + kind: TyKind::Path(None, err_path(span)), + span, + id: DUMMY_NODE_ID, + tokens: None, + }) } else { self.parse_ty()? }; @@ -764,7 +787,7 @@ impl<'a> Parser<'a> { fn parse_use_tree(&mut self) -> PResult<'a, UseTree> { let lo = self.token.span; - let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo() }; + let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo(), tokens: None }; let kind = if self.check(&token::OpenDelim(token::Brace)) || self.check(&token::BinOp(token::Star)) || self.is_import_coupler() @@ -893,10 +916,14 @@ impl<'a> Parser<'a> { /// extern "C" {} /// extern {} /// ``` - fn parse_item_foreign_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> { + fn parse_item_foreign_mod( + &mut self, + attrs: &mut Vec<Attribute>, + unsafety: Unsafe, + ) -> PResult<'a, ItemInfo> { let abi = self.parse_abi(); // ABI? let items = self.parse_item_list(attrs, |p| p.parse_foreign_item())?; - let module = ast::ForeignMod { abi, items }; + let module = ast::ForeignMod { unsafety, abi, items }; Ok((Ident::invalid(), ItemKind::ForeignMod(module))) } @@ -938,6 +965,15 @@ impl<'a> Parser<'a> { .emit(); } + fn is_unsafe_foreign_mod(&self) -> bool { + self.token.is_keyword(kw::Unsafe) + && self.is_keyword_ahead(1, &[kw::Extern]) + && self.look_ahead( + 2 + self.look_ahead(2, |t| t.can_begin_literal_maybe_minus() as usize), + |t| t.kind == token::OpenDelim(token::Brace), + ) + } + fn is_static_global(&mut self) -> bool { if self.check_keyword(kw::Static) { // Check if this could be a closure. @@ -1015,7 +1051,7 @@ impl<'a> Parser<'a> { // The user intended that the type be inferred, // so treat this as if the user wrote e.g. `const A: _ = expr;`. - P(Ty { kind: TyKind::Infer, span: id.span, id: ast::DUMMY_NODE_ID }) + P(Ty { kind: TyKind::Infer, span: id.span, id: ast::DUMMY_NODE_ID, tokens: None }) } /// Parses an enum declaration. @@ -1382,7 +1418,7 @@ impl<'a> Parser<'a> { /// Item macro invocations or `macro_rules!` definitions need inherited visibility. /// If that's not the case, emit an error. fn complain_if_pub_macro(&self, vis: &Visibility, macro_rules: bool) { - if let VisibilityKind::Inherited = vis.node { + if let VisibilityKind::Inherited = vis.kind { return; } @@ -1552,10 +1588,14 @@ impl<'a> Parser<'a> { // `$qual fn` or `$qual $qual`: || QUALS.iter().any(|&kw| self.check_keyword(kw)) && self.look_ahead(1, |t| { - // ...qualified and then `fn`, e.g. `const fn`. + // `$qual fn`, e.g. `const fn` or `async fn`. t.is_keyword(kw::Fn) - // Two qualifiers. This is enough. Due `async` we need to check that it's reserved. - || t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) && i.is_reserved()) + // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. + || t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) + // Rule out 2015 `const async: T = val`. + && i.is_reserved() + // Rule out unsafe extern block. + && !self.is_unsafe_foreign_mod()) }) // `extern ABI fn` || self.check_keyword(kw::Extern) @@ -1567,9 +1607,9 @@ impl<'a> Parser<'a> { /// up to and including the `fn` keyword. The formal grammar is: /// /// ``` - /// Extern = "extern" StringLit ; + /// Extern = "extern" StringLit? ; /// FnQual = "const"? "async"? "unsafe"? Extern? ; - /// FnFrontMatter = FnQual? "fn" ; + /// FnFrontMatter = FnQual "fn" ; /// ``` pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> { let constness = self.parse_constness(); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index d67ed74bc99..7340c574480 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -15,14 +15,14 @@ pub use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, DelimToken, Token, TokenKind}; -use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndJoint}; +use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe}; use rustc_ast::{Async, MacArgs, MacDelimiter, Mutability, StrLit, Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult}; use rustc_session::parse::ParseSess; -use rustc_span::source_map::{respan, Span, DUMMY_SP}; +use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use tracing::debug; @@ -118,7 +118,7 @@ impl<'a> Drop for Parser<'a> { struct TokenCursor { frame: TokenCursorFrame, stack: Vec<TokenCursorFrame>, - cur_token: Option<TreeAndJoint>, + cur_token: Option<TreeAndSpacing>, collecting: Option<Collecting>, } @@ -136,7 +136,7 @@ struct TokenCursorFrame { struct Collecting { /// Holds the current tokens captured during the most /// recent call to `collect_tokens` - buf: Vec<TreeAndJoint>, + buf: Vec<TreeAndSpacing>, /// The depth of the `TokenCursor` stack at the time /// collection was started. When we encounter a `TokenTree::Delimited`, /// we want to record the `TokenTree::Delimited` itself, @@ -167,7 +167,7 @@ impl TokenCursor { let tree = if !self.frame.open_delim { self.frame.open_delim = true; TokenTree::open_tt(self.frame.span, self.frame.delim).into() - } else if let Some(tree) = self.frame.tree_cursor.next_with_joint() { + } else if let Some(tree) = self.frame.tree_cursor.next_with_spacing() { tree } else if !self.frame.close_delim { self.frame.close_delim = true; @@ -694,9 +694,13 @@ impl<'a> Parser<'a> { Ok(t) => { // Parsed successfully, therefore most probably the code only // misses a separator. + let mut exp_span = self.sess.source_map().next_point(sp); + if self.sess.source_map().is_multiline(exp_span) { + exp_span = sp; + } expect_err .span_suggestion_short( - self.sess.source_map().next_point(sp), + exp_span, &format!("missing `{}`", token_str), token_str, Applicability::MaybeIncorrect, @@ -1014,21 +1018,30 @@ impl<'a> Parser<'a> { /// 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, /// so emit a proper diagnostic. - pub(crate) fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> { + // Public for rustfmt usage. + pub fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> { maybe_whole!(self, NtVis, |x| x); self.expected_tokens.push(TokenType::Keyword(kw::Crate)); if self.is_crate_vis() { self.bump(); // `crate` self.sess.gated_spans.gate(sym::crate_visibility_modifier, self.prev_token.span); - return Ok(respan(self.prev_token.span, VisibilityKind::Crate(CrateSugar::JustCrate))); + return Ok(Visibility { + span: self.prev_token.span, + kind: VisibilityKind::Crate(CrateSugar::JustCrate), + tokens: None, + }); } if !self.eat_keyword(kw::Pub) { // We need a span for our `Spanned<VisibilityKind>`, but there's inherently no // keyword to grab a span from for inherited visibility; an empty span at the // beginning of the current token would seem to be the "Schelling span". - return Ok(respan(self.token.span.shrink_to_lo(), VisibilityKind::Inherited)); + return Ok(Visibility { + span: self.token.span.shrink_to_lo(), + kind: VisibilityKind::Inherited, + tokens: None, + }); } let lo = self.prev_token.span; @@ -1045,7 +1058,11 @@ impl<'a> Parser<'a> { self.bump(); // `crate` self.expect(&token::CloseDelim(token::Paren))?; // `)` let vis = VisibilityKind::Crate(CrateSugar::PubCrate); - return Ok(respan(lo.to(self.prev_token.span), vis)); + return Ok(Visibility { + span: lo.to(self.prev_token.span), + kind: vis, + tokens: None, + }); } else if self.is_keyword_ahead(1, &[kw::In]) { // Parse `pub(in path)`. self.bump(); // `(` @@ -1053,7 +1070,11 @@ impl<'a> Parser<'a> { let path = self.parse_path(PathStyle::Mod)?; // `path` self.expect(&token::CloseDelim(token::Paren))?; // `)` let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID }; - return Ok(respan(lo.to(self.prev_token.span), vis)); + return Ok(Visibility { + span: lo.to(self.prev_token.span), + kind: vis, + tokens: None, + }); } else if self.look_ahead(2, |t| t == &token::CloseDelim(token::Paren)) && self.is_keyword_ahead(1, &[kw::Super, kw::SelfLower]) { @@ -1062,7 +1083,11 @@ impl<'a> Parser<'a> { let path = self.parse_path(PathStyle::Mod)?; // `super`/`self` self.expect(&token::CloseDelim(token::Paren))?; // `)` let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID }; - return Ok(respan(lo.to(self.prev_token.span), vis)); + return Ok(Visibility { + span: lo.to(self.prev_token.span), + kind: vis, + tokens: None, + }); } else if let FollowedByType::No = fbt { // Provide this diagnostic if a type cannot follow; // in particular, if this is not a tuple struct. @@ -1071,7 +1096,7 @@ impl<'a> Parser<'a> { } } - Ok(respan(lo, VisibilityKind::Public)) + Ok(Visibility { span: lo, kind: VisibilityKind::Public, tokens: None }) } /// Recovery for e.g. `pub(something) fn ...` or `struct X { pub(something) y: Z }` @@ -1153,7 +1178,7 @@ impl<'a> Parser<'a> { f: impl FnOnce(&mut Self) -> PResult<'a, R>, ) -> PResult<'a, (R, TokenStream)> { // Record all tokens we parse when parsing this item. - let tokens: Vec<TreeAndJoint> = self.token_cursor.cur_token.clone().into_iter().collect(); + let tokens: Vec<TreeAndSpacing> = self.token_cursor.cur_token.clone().into_iter().collect(); debug!("collect_tokens: starting with {:?}", tokens); // We need special handling for the case where `collect_tokens` is called @@ -1233,6 +1258,10 @@ impl<'a> Parser<'a> { *t == token::OpenDelim(token::Brace) || *t == token::BinOp(token::Star) }) } + + pub fn clear_expected_tokens(&mut self) { + self.expected_tokens.clear(); + } } crate fn make_unclosed_delims_error( diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index f40cd1131d2..15660fd574c 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -111,11 +111,28 @@ impl<'a> Parser<'a> { return Err(self.struct_span_err(self.token.span, "expected an item keyword")); } }, - NonterminalKind::Block => token::NtBlock(self.parse_block()?), - NonterminalKind::Stmt => match self.parse_stmt()? { - Some(s) => token::NtStmt(s), - None => return Err(self.struct_span_err(self.token.span, "expected a statement")), - }, + NonterminalKind::Block => { + let (mut block, tokens) = self.collect_tokens(|this| this.parse_block())?; + // We have have eaten an NtBlock, which could already have tokens + if block.tokens.is_none() { + block.tokens = Some(tokens); + } + token::NtBlock(block) + } + NonterminalKind::Stmt => { + let (stmt, tokens) = self.collect_tokens(|this| this.parse_stmt())?; + match stmt { + Some(mut s) => { + if s.tokens.is_none() { + s.tokens = Some(tokens); + } + token::NtStmt(s) + } + None => { + return Err(self.struct_span_err(self.token.span, "expected a statement")); + } + } + } NonterminalKind::Pat => { let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?; // We have have eaten an NtPat, which could already have tokens @@ -133,8 +150,23 @@ impl<'a> Parser<'a> { } token::NtExpr(expr) } - NonterminalKind::Literal => token::NtLiteral(self.parse_literal_maybe_minus()?), - NonterminalKind::Ty => token::NtTy(self.parse_ty()?), + NonterminalKind::Literal => { + let (mut lit, tokens) = + self.collect_tokens(|this| this.parse_literal_maybe_minus())?; + // We have have eaten a nonterminal, which could already have tokens + if lit.tokens.is_none() { + lit.tokens = Some(tokens); + } + token::NtLiteral(lit) + } + NonterminalKind::Ty => { + let (mut ty, tokens) = self.collect_tokens(|this| this.parse_ty())?; + // We have an eaten an NtTy, which could already have tokens + if ty.tokens.is_none() { + ty.tokens = Some(tokens); + } + token::NtTy(ty) + } // this could be handled like a token, since it is one NonterminalKind::Ident => { if let Some((ident, is_raw)) = get_macro_ident(&self.token) { @@ -146,10 +178,33 @@ impl<'a> Parser<'a> { return Err(self.struct_span_err(self.token.span, msg)); } } - NonterminalKind::Path => token::NtPath(self.parse_path(PathStyle::Type)?), - NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item()?)), + NonterminalKind::Path => { + let (mut path, tokens) = + self.collect_tokens(|this| this.parse_path(PathStyle::Type))?; + // We have have eaten an NtPath, which could already have tokens + if path.tokens.is_none() { + path.tokens = Some(tokens); + } + token::NtPath(path) + } + NonterminalKind::Meta => { + let (mut attr, tokens) = self.collect_tokens(|this| this.parse_attr_item())?; + // We may have eaten a nonterminal, which could already have tokens + if attr.tokens.is_none() { + attr.tokens = Some(tokens); + } + token::NtMeta(P(attr)) + } NonterminalKind::TT => token::NtTT(self.parse_token_tree()), - NonterminalKind::Vis => token::NtVis(self.parse_visibility(FollowedByType::Yes)?), + NonterminalKind::Vis => { + let (mut vis, tokens) = + self.collect_tokens(|this| this.parse_visibility(FollowedByType::Yes))?; + // We may have etan an `NtVis`, which could already have tokens + if vis.tokens.is_none() { + vis.tokens = Some(tokens); + } + token::NtVis(vis) + } NonterminalKind::Lifetime => { if self.check_lifetime() { token::NtLifetime(self.expect_lifetime().ident) diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 54b4df8613f..66ce015d02e 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -64,7 +64,7 @@ impl<'a> Parser<'a> { path_span = path_lo.to(self.prev_token.span); } else { path_span = self.token.span.to(self.token.span); - path = ast::Path { segments: Vec::new(), span: path_span }; + path = ast::Path { segments: Vec::new(), span: path_span, tokens: None }; } // See doc comment for `unmatched_angle_bracket_count`. @@ -81,7 +81,10 @@ impl<'a> Parser<'a> { let qself = QSelf { ty, path_span, position: path.segments.len() }; self.parse_path_segments(&mut path.segments, style)?; - Ok((qself, Path { segments: path.segments, span: lo.to(self.prev_token.span) })) + Ok(( + qself, + Path { segments: path.segments, span: lo.to(self.prev_token.span), tokens: None }, + )) } /// Recover from an invalid single colon, when the user likely meant a qualified path. @@ -144,7 +147,7 @@ impl<'a> Parser<'a> { } self.parse_path_segments(&mut segments, style)?; - Ok(Path { segments, span: lo.to(self.prev_token.span) }) + Ok(Path { segments, span: lo.to(self.prev_token.span), tokens: None }) } pub(super) fn parse_path_segments( diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index ac067cb0eab..fd1c6b25aec 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -10,7 +10,7 @@ use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; use rustc_ast::util::classify; -use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacStmtStyle}; +use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacCallStmt, MacStmtStyle}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKind, DUMMY_NODE_ID}; use rustc_errors::{Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; @@ -21,7 +21,8 @@ use std::mem; impl<'a> Parser<'a> { /// Parses a statement. This stops just before trailing semicolons on everything but items. /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. - pub(super) fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> { + // Public for rustfmt usage. + pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> { Ok(self.parse_stmt_without_recovery().unwrap_or_else(|mut e| { e.emit(); self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); @@ -106,7 +107,7 @@ impl<'a> Parser<'a> { let kind = if delim == token::Brace || self.token == token::Semi || self.token == token::Eof { - StmtKind::MacCall(P((mac, style, attrs))) + StmtKind::MacCall(P(MacCallStmt { mac, style, attrs })) } else { // Since none of the above applied, this is an expression statement macro. let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new()); @@ -363,7 +364,7 @@ impl<'a> Parser<'a> { let mut eat_semi = true; match stmt.kind { // Expression without semicolon. - StmtKind::Expr(ref expr) + StmtKind::Expr(ref mut expr) if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) => { // Just check for errors and recover; do not eat semicolon yet. @@ -387,15 +388,29 @@ impl<'a> Parser<'a> { ); } } - e.emit(); - self.recover_stmt(); + if let Err(mut e) = + self.check_mistyped_turbofish_with_multiple_type_params(e, expr) + { + e.emit(); + self.recover_stmt(); + } // Don't complain about type errors in body tail after parse error (#57383). let sp = expr.span.to(self.prev_token.span); - stmt.kind = StmtKind::Expr(self.mk_expr_err(sp)); + *expr = self.mk_expr_err(sp); } } - StmtKind::Local(..) => { - self.expect_semi()?; + StmtKind::Local(ref mut local) => { + if let Err(e) = self.expect_semi() { + // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover. + match &mut local.init { + Some(ref mut expr) => { + self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?; + // We found `foo<bar, baz>`, have we fully recovered? + self.expect_semi()?; + } + None => return Err(e), + } + } eat_semi = false; } StmtKind::Empty => eat_semi = false, @@ -410,11 +425,11 @@ impl<'a> Parser<'a> { } pub(super) fn mk_block(&self, stmts: Vec<Stmt>, rules: BlockCheckMode, span: Span) -> P<Block> { - P(Block { stmts, id: DUMMY_NODE_ID, rules, span }) + P(Block { stmts, id: DUMMY_NODE_ID, rules, span, tokens: None }) } pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt { - Stmt { id: DUMMY_NODE_ID, kind, span } + Stmt { id: DUMMY_NODE_ID, kind, span, tokens: None } } fn mk_stmt_err(&self, span: Span) -> Stmt { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 4356850818e..d42a786a18f 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -67,7 +67,7 @@ impl<'a> Parser<'a> { /// Parse a type suitable for a function or function pointer parameter. /// The difference from `parse_ty` is that this version allows `...` - /// (`CVarArgs`) at the top level of the the type. + /// (`CVarArgs`) at the top level of the type. pub(super) fn parse_ty_for_param(&mut self) -> PResult<'a, P<Ty>> { self.parse_ty_common(AllowPlus::Yes, RecoverQPath::Yes, AllowCVariadic::Yes) } @@ -276,8 +276,34 @@ impl<'a> Parser<'a> { } fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> { - let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None }; + let and_span = self.prev_token.span; + let mut opt_lifetime = + if self.check_lifetime() { Some(self.expect_lifetime()) } else { None }; let mutbl = self.parse_mutability(); + if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() { + // A lifetime is invalid here: it would be part of a bare trait bound, which requires + // it to be followed by a plus, but we disallow plus in the pointee type. + // So we can handle this case as an error here, and suggest `'a mut`. + // If there *is* a plus next though, handling the error later provides better suggestions + // (like adding parentheses) + if !self.look_ahead(1, |t| t.is_like_plus()) { + let lifetime_span = self.token.span; + let span = and_span.to(lifetime_span); + + let mut err = self.struct_span_err(span, "lifetime must precede `mut`"); + if let Ok(lifetime_src) = self.span_to_snippet(lifetime_span) { + err.span_suggestion( + span, + "place the lifetime before `mut`", + format!("&{} mut", lifetime_src), + Applicability::MaybeIncorrect, + ); + } + err.emit(); + + opt_lifetime = Some(self.expect_lifetime()); + } + } let ty = self.parse_ty_no_plus()?; Ok(TyKind::Rptr(opt_lifetime, MutTy { ty, mutbl })) } @@ -626,6 +652,6 @@ impl<'a> Parser<'a> { } pub(super) fn mk_ty(&self, span: Span, kind: TyKind) -> P<Ty> { - P(Ty { kind, span, id: ast::DUMMY_NODE_ID }) + P(Ty { kind, span, id: ast::DUMMY_NODE_ID, tokens: None }) } } diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index e07b8b86aef..25e3e67e28e 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -5,14 +5,12 @@ //! generated instead. #![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", + html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))) )] #![feature(nll)] #![feature(or_patterns)] -#![feature(rustc_private)] -#![feature(unicode_internals)] #![feature(bool_to_option)] pub use Alignment::*; @@ -527,12 +525,9 @@ impl<'a> Parser<'a> { // fill character if let Some(&(_, c)) = self.cur.peek() { - match self.cur.clone().nth(1) { - Some((_, '>' | '<' | '^')) => { - spec.fill = Some(c); - self.cur.next(); - } - _ => {} + if let Some((_, '>' | '<' | '^')) = self.cur.clone().nth(1) { + spec.fill = Some(c); + self.cur.next(); } } // Alignment diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 832cde86d0b..efe947daa28 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -66,12 +66,26 @@ impl CheckAttrVisitor<'tcx> { } else if self.tcx.sess.check_name(attr, sym::marker) { self.check_marker(attr, span, target) } else if self.tcx.sess.check_name(attr, sym::target_feature) { - self.check_target_feature(attr, span, target) + self.check_target_feature(hir_id, attr, span, target) } else if self.tcx.sess.check_name(attr, sym::track_caller) { self.check_track_caller(&attr.span, attrs, span, target) } else if self.tcx.sess.check_name(attr, sym::doc) { self.check_doc_alias(attr, hir_id, target) + } else if self.tcx.sess.check_name(attr, sym::no_link) { + self.check_no_link(&attr, span, target) + } else if self.tcx.sess.check_name(attr, sym::export_name) { + self.check_export_name(&attr, span, target) } else { + // lint-only checks + if self.tcx.sess.check_name(attr, sym::cold) { + self.check_cold(hir_id, attr, span, target); + } else if self.tcx.sess.check_name(attr, sym::link_name) { + self.check_link_name(hir_id, attr, span, target); + } else if self.tcx.sess.check_name(attr, sym::link_section) { + self.check_link_section(hir_id, attr, span, target); + } else if self.tcx.sess.check_name(attr, sym::no_mangle) { + self.check_no_mangle(hir_id, attr, span, target); + } true }; } @@ -109,12 +123,12 @@ impl CheckAttrVisitor<'tcx> { lint.build("`#[inline]` is ignored on constants") .warn( "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", + being phased out; it will become a hard error in \ + a future release!", ) .note( "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \ - for more information", + for more information", ) .emit(); }); @@ -153,7 +167,7 @@ impl CheckAttrVisitor<'tcx> { .emit(); false } - Target::Fn | Target::Method(..) | Target::ForeignFn => true, + Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true, _ => { struct_span_err!( self.tcx.sess, @@ -202,10 +216,31 @@ impl CheckAttrVisitor<'tcx> { } /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid. - fn check_target_feature(&self, attr: &Attribute, span: &Span, target: Target) -> bool { + fn check_target_feature( + &self, + hir_id: HirId, + attr: &Attribute, + span: &Span, + target: Target, + ) -> bool { match target { Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, + // FIXME: #[target_feature] was previously erroneously allowed on statements and some + // crates used this, so only emit a warning. + Target::Statement => { + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build("attribute should be applied to a function") + .warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ) + .span_label(*span, "not a function") + .emit(); + }); + true + } _ => { self.tcx .sess @@ -250,13 +285,23 @@ impl CheckAttrVisitor<'tcx> { None } } + Target::AssocConst => { + let parent_hir_id = self.tcx.hir().get_parent_item(hir_id); + let containing_item = self.tcx.hir().expect_item(parent_hir_id); + // We can't link to trait impl's consts. + let err = "associated constant in trait implementation block"; + match containing_item.kind { + ItemKind::Impl { of_trait: Some(_), .. } => Some(err), + _ => None, + } + } _ => None, } { self.tcx .sess .struct_span_err( meta.span(), - &format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err,), + &format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err), ) .emit(); } @@ -267,6 +312,136 @@ impl CheckAttrVisitor<'tcx> { true } + /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid. + fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { + match target { + Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {} + _ => { + // FIXME: #[cold] was previously allowed on non-functions and some crates used + // this, so only emit a warning. + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build("attribute should be applied to a function") + .warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ) + .span_label(*span, "not a function") + .emit(); + }); + } + } + } + + /// Checks if `#[link_name]` is applied to an item other than a foreign function or static. + fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { + match target { + Target::ForeignFn | Target::ForeignStatic => {} + _ => { + // FIXME: #[cold] was previously allowed on non-functions/statics and some crates + // used this, so only emit a warning. + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + let mut diag = + lint.build("attribute should be applied to a foreign function or static"); + diag.warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ); + + // See issue #47725 + if let Target::ForeignMod = target { + if let Some(value) = attr.value_str() { + diag.span_help( + attr.span, + &format!(r#"try `#[link(name = "{}")]` instead"#, value), + ); + } else { + diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#); + } + } + + diag.span_label(*span, "not a foreign function or static"); + diag.emit(); + }); + } + } + } + + /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid. + fn check_no_link(&self, attr: &Attribute, span: &Span, target: Target) -> bool { + if target == Target::ExternCrate { + true + } else { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to an `extern crate` item") + .span_label(*span, "not an `extern crate` item") + .emit(); + false + } + } + + /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid. + fn check_export_name(&self, attr: &Attribute, span: &Span, target: Target) -> bool { + match target { + Target::Static | Target::Fn | Target::Method(..) => true, + _ => { + self.tcx + .sess + .struct_span_err( + attr.span, + "attribute should be applied to a function or static", + ) + .span_label(*span, "not a function or static") + .emit(); + false + } + } + } + + /// Checks if `#[link_section]` is applied to a function or static. + fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { + match target { + Target::Static | Target::Fn | Target::Method(..) => {} + _ => { + // FIXME: #[link_section] was previously allowed on non-functions/statics and some + // crates used this, so only emit a warning. + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build("attribute should be applied to a function or static") + .warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ) + .span_label(*span, "not a function or static") + .emit(); + }); + } + } + } + + /// Checks if `#[no_mangle]` is applied to a function or static. + fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { + match target { + Target::Static | Target::Fn | Target::Method(..) => {} + _ => { + // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some + // crates used this, so only emit a warning. + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build("attribute should be applied to a function or static") + .warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ) + .span_label(*span, "not a function or static") + .emit(); + }); + } + } + } + /// Checks if the `#[repr]` attributes on `item` are valid. fn check_repr( &self, @@ -311,7 +486,11 @@ impl CheckAttrVisitor<'tcx> { } sym::simd => { is_simd = true; - if target != Target::Struct { ("a", "struct") } else { continue } + if target != Target::Struct { + ("a", "struct") + } else { + continue; + } } sym::transparent => { is_transparent = true; @@ -348,7 +527,11 @@ impl CheckAttrVisitor<'tcx> { | sym::isize | sym::usize => { int_reprs += 1; - if target != Target::Enum { ("an", "enum") } else { continue } + if target != Target::Enum { + ("an", "enum") + } else { + continue; + } } _ => continue, }; @@ -411,10 +594,8 @@ impl CheckAttrVisitor<'tcx> { fn check_stmt_attributes(&self, stmt: &hir::Stmt<'_>) { // When checking statements ignore expressions, they will be checked later if let hir::StmtKind::Local(ref l) = stmt.kind { + self.check_attributes(l.hir_id, &l.attrs, &stmt.span, Target::Statement, None); for attr in l.attrs.iter() { - if self.tcx.sess.check_name(attr, sym::inline) { - self.check_inline(l.hir_id, attr, &stmt.span, Target::Statement); - } if self.tcx.sess.check_name(attr, sym::repr) { self.emit_repr_error( attr.span, @@ -432,10 +613,8 @@ impl CheckAttrVisitor<'tcx> { hir::ExprKind::Closure(..) => Target::Closure, _ => Target::Expression, }; + self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None); for attr in expr.attrs.iter() { - if self.tcx.sess.check_name(attr, sym::inline) { - self.check_inline(expr.hir_id, attr, &expr.span, target); - } if self.tcx.sess.check_name(attr, sym::repr) { self.emit_repr_error( attr.span, diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 01da33ddd2e..fe6653e98da 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -104,7 +104,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { if let Some(t) = t { self.check_def_id(t); } - if let Some(i) = i { + if let Some((i, _)) = i { self.check_def_id(i); } } @@ -124,7 +124,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } fn handle_field_access(&mut self, lhs: &hir::Expr<'_>, hir_id: hir::HirId) { - match self.typeck_results().expr_ty_adjusted(lhs).kind { + match self.typeck_results().expr_ty_adjusted(lhs).kind() { ty::Adt(def, _) => { let index = self.tcx.field_index(hir_id, self.typeck_results()); self.insert_def_id(def.non_enum_variant().fields[index].did); @@ -140,7 +140,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { res: Res, pats: &[hir::FieldPat<'_>], ) { - let variant = match self.typeck_results().node_type(lhs.hir_id).kind { + let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { ty::Adt(adt, _) => adt.variant_of_res(res), _ => span_bug!(lhs.span, "non-ADT in struct pattern"), }; @@ -269,7 +269,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { hir::ExprKind::Struct(ref qpath, ref fields, _) => { let res = self.typeck_results().qpath_res(qpath, expr.hir_id); self.handle_res(res); - if let ty::Adt(ref adt, _) = self.typeck_results().expr_ty(expr).kind { + if let ty::Adt(ref adt, _) = self.typeck_results().expr_ty(expr).kind() { self.mark_as_used_if_union(adt, fields); } } diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index df0f9f157ae..94592935c7f 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -12,11 +12,11 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::Session; +use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::{sym, Symbol}; struct DiagnosticItemCollector<'tcx> { @@ -100,6 +100,18 @@ fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> FxHashMap<Symbol, DefId> { // Collect diagnostic items in this crate. tcx.hir().krate().visit_all_item_likes(&mut collector); + // FIXME(visit_all_item_likes): Foreign items are not visited + // here, so we have to manually look at them for now. + for foreign_module in tcx.foreign_modules(LOCAL_CRATE) { + for &foreign_item in foreign_module.foreign_items.iter() { + match tcx.hir().get(tcx.hir().local_def_id_to_hir_id(foreign_item.expect_local())) { + hir::Node::ForeignItem(item) => { + collector.observe_item(item.attrs, item.hir_id); + } + item => bug!("unexpected foreign item {:?}", item), + } + } + } collector.items } diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index ebe231009d3..79f1c2b9da8 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -35,7 +35,7 @@ struct ExprVisitor<'tcx> { /// If the type is `Option<T>`, it will return `T`, otherwise /// the type itself. Works on most `Option`-like types. fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - let (def, substs) = match ty.kind { + let (def, substs) = match *ty.kind() { ty::Adt(def, substs) => (def, substs), _ => return ty, }; @@ -81,7 +81,7 @@ impl ExprVisitor<'tcx> { // Special-case transmutting from `typeof(function)` and // `Option<typeof(function)>` to present a clearer error. let from = unpack_option_like(self.tcx, from); - if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (&from.kind, sk_to) { + if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) { if size_to == Pointer.size(&self.tcx) { struct_span_err!(self.tcx.sess, span, E0591, "can't transmute zero-sized type") .note(&format!("source type: {}", from)) @@ -127,7 +127,7 @@ impl ExprVisitor<'tcx> { if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) { return true; } - if let ty::Foreign(..) = ty.kind { + if let ty::Foreign(..) = ty.kind() { return true; } false @@ -149,7 +149,7 @@ impl ExprVisitor<'tcx> { 64 => InlineAsmType::I64, _ => unreachable!(), }; - let asm_ty = match ty.kind { + let asm_ty = match *ty.kind() { ty::Never | ty::Error(_) => return None, ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8), ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16), @@ -166,7 +166,7 @@ impl ExprVisitor<'tcx> { ty::Adt(adt, substs) if adt.repr.simd() => { let fields = &adt.non_enum_variant().fields; let elem_ty = fields[0].ty(self.tcx, substs); - match elem_ty.kind { + match elem_ty.kind() { ty::Never | ty::Error(_) => return None, ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => { Some(InlineAsmType::VecI8(fields.len() as u64)) @@ -374,7 +374,7 @@ impl ExprVisitor<'tcx> { } hir::InlineAsmOperand::Const { ref expr } => { let ty = self.typeck_results.expr_ty_adjusted(expr); - match ty.kind { + match ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Float(_) => {} _ => { let msg = diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index be4c542ec3a..c14d6aace87 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -4,7 +4,7 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(in_band_lifetimes)] #![feature(nll)] #![feature(or_patterns)] diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 55525586479..db9495201cf 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -958,7 +958,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } let ty = self.typeck_results.node_type(id); - match ty.kind { + match ty.kind() { ty::Closure(_def_id, substs) => match substs.as_closure().kind() { ty::ClosureKind::Fn => {} ty::ClosureKind::FnMut => {} diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 91edc7d9db7..4ca52f405fb 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -9,13 +9,14 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Generics, HirId, Item, StructField, Variant}; +use rustc_hir::{Generics, HirId, Item, StructField, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::map::Map; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability::{DeprecationEntry, Index}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint; +use rustc_session::lint::builtin::INEFFECTIVE_UNSTABLE_TRAIT_IMPL; use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; @@ -538,7 +539,37 @@ impl Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl { of_trait: Some(ref t), items, .. } => { + hir::ItemKind::Impl { of_trait: Some(ref t), self_ty, items, .. } => { + if self.tcx.features().staged_api { + // If this impl block has an #[unstable] attribute, give an + // error if all involved types and traits are stable, because + // it will have no effect. + // See: https://github.com/rust-lang/rust/issues/55436 + if let (Some(Stability { level: attr::Unstable { .. }, .. }), _) = + attr::find_stability(&self.tcx.sess, &item.attrs, item.span) + { + let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; + c.visit_ty(self_ty); + c.visit_trait_ref(t); + if c.fully_stable { + let span = item + .attrs + .iter() + .find(|a| a.has_name(sym::unstable)) + .map_or(item.span, |a| a.span); + self.tcx.struct_span_lint_hir( + INEFFECTIVE_UNSTABLE_TRAIT_IMPL, + item.hir_id, + span, + |lint| lint + .build("an `#[unstable]` annotation here has no effect") + .note("see issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information") + .emit() + ); + } + } + } + if let Res::Def(DefKind::Trait, trait_did) = t.path.res { for impl_item_ref in items { let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); @@ -598,6 +629,44 @@ impl Visitor<'tcx> for Checker<'tcx> { } } +struct CheckTraitImplStable<'tcx> { + tcx: TyCtxt<'tcx>, + fully_stable: bool, +} + +impl Visitor<'tcx> for CheckTraitImplStable<'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } + + fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _id: hir::HirId) { + if let Some(def_id) = path.res.opt_def_id() { + if let Some(stab) = self.tcx.lookup_stability(def_id) { + self.fully_stable &= stab.level.is_stable(); + } + } + intravisit::walk_path(self, path) + } + + fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) { + if let Res::Def(DefKind::Trait, trait_did) = t.path.res { + if let Some(stab) = self.tcx.lookup_stability(trait_did) { + self.fully_stable &= stab.level.is_stable(); + } + } + intravisit::walk_trait_ref(self, t) + } + + fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { + if let TyKind::Never = t.kind { + self.fully_stable = false; + } + intravisit::walk_ty(self, t) + } +} + /// Given the list of enabled features that were not language features (i.e., that /// were expected to be library features), and the list of features used from /// libraries, identify activated features that don't exist and error about them. diff --git a/compiler/rustc_plugin_impl/src/lib.rs b/compiler/rustc_plugin_impl/src/lib.rs index 1eb65dd96ba..5bf4d300e9e 100644 --- a/compiler/rustc_plugin_impl/src/lib.rs +++ b/compiler/rustc_plugin_impl/src/lib.rs @@ -6,7 +6,7 @@ //! feature](https://doc.rust-lang.org/nightly/unstable-book/language-features/plugin.html) //! of the Unstable Book for some examples. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(nll)] #![recursion_limit = "256"] diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index deb4277cb38..8d1b826ea35 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1,7 +1,6 @@ -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(in_band_lifetimes)] #![feature(nll)] -#![feature(or_patterns)] #![recursion_limit = "256"] use rustc_attr as attr; @@ -97,6 +96,15 @@ where ty.visit_with(self) } ty::PredicateAtom::RegionOutlives(..) => false, + ty::PredicateAtom::ConstEvaluatable(..) + if self.def_id_visitor.tcx().features().const_evaluatable_checked => + { + // FIXME(const_evaluatable_checked): If the constant used here depends on a + // private function we may have to do something here... + // + // For now, let's just pretend that everything is fine. + false + } _ => bug!("unexpected predicate: {:?}", predicate), } } @@ -119,7 +127,7 @@ where fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { let tcx = self.def_id_visitor.tcx(); // InternalSubsts are not visited here because they are visited below in `super_visit_with`. - match ty.kind { + match *ty.kind() { ty::Adt(&ty::AdtDef { did: def_id, .. }, ..) | ty::Foreign(def_id) | ty::FnDef(def_id, ..) @@ -134,7 +142,7 @@ where // Default type visitor doesn't visit signatures of fn types. // Something like `fn() -> Priv {my_func}` is considered a private type even if // `my_func` is public, so we need to visit signatures. - if let ty::FnDef(..) = ty.kind { + if let ty::FnDef(..) = ty.kind() { if tcx.fn_sig(def_id).visit_with(self) { return true; } diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 7defb00a881..f38d62dec00 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -17,5 +17,5 @@ rustc_macros = { path = "../rustc_macros" } rustc_index = { path = "../rustc_index" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } -parking_lot = "0.10" +parking_lot = "0.11" smallvec = { version = "1.0", 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 761724be57d..a48d002b2a3 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -218,7 +218,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { speculative: bool, ) -> Result<ty::Visibility, VisResolutionError<'ast>> { let parent_scope = &self.parent_scope; - match vis.node { + match vis.kind { ast::VisibilityKind::Public => Ok(ty::Visibility::Public), ast::VisibilityKind::Crate(..) => { Ok(ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX))) @@ -395,7 +395,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // so prefixes are prepended with crate root segment if necessary. // The root is prepended lazily, when the first non-empty prefix or terminating glob // appears, so imports in braced groups can have roots prepended independently. - let is_glob = if let ast::UseTreeKind::Glob = use_tree.kind { true } else { false }; + let is_glob = matches!(use_tree.kind, ast::UseTreeKind::Glob); let crate_root = match prefix_iter.peek() { Some(seg) if !seg.ident.is_path_segment_keyword() && seg.ident.span.rust_2015() => { Some(seg.ident.span.ctxt()) @@ -796,23 +796,26 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { vis }; + let mut ret_fields = Vec::with_capacity(vdata.fields().len()); + for field in vdata.fields() { // NOTE: The field may be an expansion placeholder, but expansion sets // correct visibilities for unnamed field placeholders specifically, so the // constructor visibility should still be determined correctly. - if let Ok(field_vis) = self.resolve_visibility_speculative(&field.vis, true) - { - if ctor_vis.is_at_least(field_vis, &*self.r) { - ctor_vis = field_vis; - } + let field_vis = self + .resolve_visibility_speculative(&field.vis, true) + .unwrap_or(ty::Visibility::Public); + if ctor_vis.is_at_least(field_vis, &*self.r) { + ctor_vis = field_vis; } + ret_fields.push(field_vis); } let ctor_res = Res::Def( DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)), self.r.local_def_id(ctor_node_id).to_def_id(), ); self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion)); - self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis)); + self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis, ret_fields)); } } @@ -964,7 +967,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { Res::Def(DefKind::Ctor(CtorOf::Struct, ..), def_id) => { let parent = cstore.def_key(def_id).parent; if let Some(struct_def_id) = parent.map(|index| DefId { index, ..def_id }) { - self.r.struct_constructors.insert(struct_def_id, (res, vis)); + self.r.struct_constructors.insert(struct_def_id, (res, vis, vec![])); } } _ => {} diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 5624a6b6acc..89ce89b2e9a 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -105,7 +105,7 @@ impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> { // because this means that they were generated in some fashion by the // compiler and we don't need to consider them. if let ast::ItemKind::Use(..) = item.kind { - if item.vis.node.is_pub() || item.span.is_dummy() { + if item.vis.kind.is_pub() || item.span.is_dummy() { return; } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 48e1068b8da..612bc3e7491 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -112,7 +112,7 @@ impl<'a> Resolver<'a> { match outer_res { Res::SelfTy(maybe_trait_defid, maybe_impl_defid) => { if let Some(impl_span) = - maybe_impl_defid.and_then(|def_id| self.opt_span(def_id)) + maybe_impl_defid.and_then(|(def_id, _)| self.opt_span(def_id)) { err.span_label( reduce_impl_span_to_impl_keyword(sm, impl_span), @@ -466,7 +466,7 @@ impl<'a> Resolver<'a> { ); err } - ResolutionError::ParamInNonTrivialAnonConst(name) => { + ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => { let mut err = self.session.struct_span_err( span, "generic parameters must not be used inside of non trivial constant values", @@ -478,9 +478,17 @@ impl<'a> Resolver<'a> { name ), ); - err.help( - &format!("it is currently only allowed to use either `{0}` or `{{ {0} }}` as generic constants", name) - ); + + if is_type { + err.note("type parameters are currently not permitted in anonymous constants"); + } else { + err.help( + &format!("it is currently only allowed to use either `{0}` or `{{ {0} }}` as generic constants", + name + ) + ); + } + err } ResolutionError::SelfInTyParamDefault => { @@ -794,7 +802,7 @@ impl<'a> Resolver<'a> { } segms.push(ast::PathSegment::from_ident(ident)); - let path = Path { span: name_binding.span, segments: segms }; + let path = Path { span: name_binding.span, segments: segms, tokens: None }; let did = match res { Res::Def(DefKind::Ctor(..), did) => this.parent(did), _ => res.opt_def_id(), diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index d113eb22aba..2c01934b490 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -110,6 +110,9 @@ crate enum RibKind<'a> { ItemRibKind(HasGenericParams), /// We're in a constant item. Can't refer to dynamic stuff. + /// + /// The `bool` indicates if this constant may reference generic parameters + /// and is used to only allow generic parameters to be used in trivial constant expressions. ConstantItemRibKind(bool), /// We passed through a module. @@ -188,7 +191,7 @@ crate enum PathSource<'a> { // Paths in struct expressions and patterns `Path { .. }`. Struct, // Paths in tuple struct patterns `Path(..)`. - TupleStruct(Span), + TupleStruct(Span, &'a [Span]), // `m::A::B` in `<T as m::A>::B::C`. TraitItem(Namespace), } @@ -197,7 +200,7 @@ impl<'a> PathSource<'a> { fn namespace(self) -> Namespace { match self { PathSource::Type | PathSource::Trait(_) | PathSource::Struct => TypeNS, - PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(_) => ValueNS, + PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) => ValueNS, PathSource::TraitItem(ns) => ns, } } @@ -208,7 +211,7 @@ impl<'a> PathSource<'a> { | PathSource::Expr(..) | PathSource::Pat | PathSource::Struct - | PathSource::TupleStruct(_) => true, + | PathSource::TupleStruct(..) => true, PathSource::Trait(_) | PathSource::TraitItem(..) => false, } } @@ -219,7 +222,7 @@ impl<'a> PathSource<'a> { PathSource::Trait(_) => "trait", PathSource::Pat => "unit struct, unit variant or constant", PathSource::Struct => "struct, variant or union type", - PathSource::TupleStruct(_) => "tuple struct or tuple variant", + PathSource::TupleStruct(..) => "tuple struct or tuple variant", PathSource::TraitItem(ns) => match ns { TypeNS => "associated type", ValueNS => "method or associated constant", @@ -305,7 +308,7 @@ impl<'a> PathSource<'a> { | Res::SelfCtor(..) => true, _ => false, }, - PathSource::TupleStruct(_) => match res { + PathSource::TupleStruct(..) => match res { Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..) => true, _ => false, }, @@ -340,8 +343,8 @@ impl<'a> PathSource<'a> { (PathSource::Struct, false) => error_code!(E0422), (PathSource::Expr(..), true) => error_code!(E0423), (PathSource::Expr(..), false) => error_code!(E0425), - (PathSource::Pat | PathSource::TupleStruct(_), true) => error_code!(E0532), - (PathSource::Pat | PathSource::TupleStruct(_), false) => error_code!(E0531), + (PathSource::Pat | PathSource::TupleStruct(..), true) => error_code!(E0532), + (PathSource::Pat | PathSource::TupleStruct(..), false) => error_code!(E0531), (PathSource::TraitItem(..), true) => error_code!(E0575), (PathSource::TraitItem(..), false) => error_code!(E0576), } @@ -378,6 +381,9 @@ struct DiagnosticMetadata<'ast> { /// Only used for better errors on `let <pat>: <expr, not type>;`. current_let_binding: Option<(Span, Option<Span>, Option<Span>)>, + + /// Used to detect possible `if let` written without `let` and to provide structured suggestion. + in_if_condition: Option<&'ast Expr>, } struct LateResolutionVisitor<'a, 'b, 'ast> { @@ -403,12 +409,12 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { /// /// In particular, rustdoc uses this to avoid giving errors for `cfg()` items. /// In most cases this will be `None`, in which case errors will always be reported. - /// If it is `Some(_)`, then it will be updated when entering a nested function or trait body. + /// If it is `true`, then it will be updated when entering a nested function or trait body. in_func_body: bool, } /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. -impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { +impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { 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. @@ -656,7 +662,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } } -impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { +impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn new(resolver: &'b mut Resolver<'a>) -> LateResolutionVisitor<'a, 'b, 'ast> { // During late resolution we only track the module component of the parent scope, // although it may be useful to track other components as well for diagnostics. @@ -845,7 +851,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.with_current_self_item(item, |this| { this.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { let item_def_id = this.r.local_def_id(item.id).to_def_id(); - this.with_self_rib(Res::SelfTy(None, Some(item_def_id)), |this| { + this.with_self_rib(Res::SelfTy(None, Some((item_def_id, false))), |this| { visit::walk_item(this, item); }); }); @@ -1028,7 +1034,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let mut add_bindings_for_ns = |ns| { let parent_rib = self.ribs[ns] .iter() - .rfind(|r| if let ItemRibKind(_) = r.kind { true } else { false }) + .rfind(|r| matches!(r.kind, ItemRibKind(_))) .expect("associated item outside of an item"); seen_bindings .extend(parent_rib.bindings.iter().map(|(ident, _)| (*ident, ident.span))); @@ -1212,7 +1218,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Resolve the trait reference, if necessary. this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| { let item_def_id = this.r.local_def_id(item_id).to_def_id(); - this.with_self_rib(Res::SelfTy(trait_id, Some(item_def_id)), |this| { + this.with_self_rib(Res::SelfTy(trait_id, Some((item_def_id, false))), |this| { if let Some(trait_ref) = opt_trait_reference.as_ref() { // Resolve type arguments in the trait path. visit::walk_trait_ref(this, trait_ref); @@ -1536,8 +1542,16 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings)); self.r.record_partial_res(pat.id, PartialRes::new(res)); } - PatKind::TupleStruct(ref path, ..) => { - self.smart_resolve_path(pat.id, None, path, PathSource::TupleStruct(pat.span)); + PatKind::TupleStruct(ref path, ref sub_patterns) => { + self.smart_resolve_path( + pat.id, + None, + path, + PathSource::TupleStruct( + pat.span, + self.r.arenas.alloc_pattern_spans(sub_patterns.iter().map(|p| p.span)), + ), + ); } PatKind::Path(ref qself, ref path) => { self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat); @@ -1964,7 +1978,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if qself.is_none() { let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident); - let path = Path { segments: path.iter().map(path_seg).collect(), span }; + let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None }; if let Ok((_, res)) = self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false) { @@ -2199,7 +2213,9 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ExprKind::If(ref cond, ref then, ref opt_else) => { self.with_rib(ValueNS, NormalRibKind, |this| { + let old = this.diagnostic_metadata.in_if_condition.replace(cond); this.visit_expr(cond); + this.diagnostic_metadata.in_if_condition = old; this.visit_block(then); }); if let Some(expr) = opt_else { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index d392967af38..ced272e474d 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -83,13 +83,14 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str let enum_path = ast::Path { span: suggestion.path.span, segments: suggestion.path.segments[0..path_len - 1].to_vec(), + tokens: None, }; let enum_path_string = path_names_to_string(&enum_path); (variant_path_string, enum_path_string) } -impl<'a> LateResolutionVisitor<'a, '_, '_> { +impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn def_span(&self, def_id: DefId) -> Option<Span> { match def_id.krate { LOCAL_CRATE => self.r.opt_span(def_id), @@ -176,6 +177,19 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { let code = source.error_code(res.is_some()); let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code); + match (source, self.diagnostic_metadata.in_if_condition) { + (PathSource::Expr(_), Some(Expr { span, kind: ExprKind::Assign(..), .. })) => { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "you might have meant to use pattern matching", + "let ".to_string(), + Applicability::MaybeIncorrect, + ); + self.r.session.if_let_suggestions.borrow_mut().insert(*span); + } + _ => {} + } + let is_assoc_fn = self.self_type_is_available(span); // Emit help message for fake-self from other languages (e.g., `this` in Javascript). if ["this", "my"].contains(&&*item_str.as_str()) && is_assoc_fn { @@ -609,12 +623,12 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { ); } } - PathSource::Expr(_) | PathSource::TupleStruct(_) | PathSource::Pat => { + PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => { let span = match &source { PathSource::Expr(Some(Expr { span, kind: ExprKind::Call(_, _), .. })) - | PathSource::TupleStruct(span) => { + | PathSource::TupleStruct(span, _) => { // We want the main underline to cover the suggested code as well for // cleaner output. err.set_span(*span); @@ -626,7 +640,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { err.span_label(span, &format!("`{}` defined here", path_str)); } let (tail, descr, applicability) = match source { - PathSource::Pat | PathSource::TupleStruct(_) => { + PathSource::Pat | PathSource::TupleStruct(..) => { ("", "pattern", Applicability::MachineApplicable) } _ => (": val", "literal", Applicability::HasPlaceholders), @@ -691,7 +705,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { } ( Res::Def(DefKind::Enum, def_id), - PathSource::TupleStruct(_) | PathSource::Expr(..), + PathSource::TupleStruct(..) | PathSource::Expr(..), ) => { if self .diagnostic_metadata @@ -731,15 +745,50 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { } } (Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => { - if let Some((ctor_def, ctor_vis)) = self.r.struct_constructors.get(&def_id).cloned() + if let Some((ctor_def, ctor_vis, fields)) = + self.r.struct_constructors.get(&def_id).cloned() { let accessible_ctor = self.r.is_accessible_from(ctor_vis, self.parent_scope.module); if is_expected(ctor_def) && !accessible_ctor { - err.span_label( - span, - "constructor is not visible here due to private fields".to_string(), - ); + let mut better_diag = false; + if let PathSource::TupleStruct(_, pattern_spans) = source { + if pattern_spans.len() > 0 && fields.len() == pattern_spans.len() { + let non_visible_spans: Vec<Span> = fields + .iter() + .zip(pattern_spans.iter()) + .filter_map(|(vis, span)| { + match self + .r + .is_accessible_from(*vis, self.parent_scope.module) + { + true => None, + false => Some(*span), + } + }) + .collect(); + // Extra check to be sure + if non_visible_spans.len() > 0 { + let mut m: rustc_span::MultiSpan = + non_visible_spans.clone().into(); + non_visible_spans.into_iter().for_each(|s| { + m.push_span_label(s, "private field".to_string()) + }); + err.span_note( + m, + "constructor is not visible here due to private fields", + ); + better_diag = true; + } + } + } + + if !better_diag { + err.span_label( + span, + "constructor is not visible here due to private fields".to_string(), + ); + } } } else { bad_struct_syntax_suggestion(def_id); @@ -1052,7 +1101,8 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { path_segments.push(ast::PathSegment::from_ident(ident)); let module_def_id = module.def_id().unwrap(); if module_def_id == def_id { - let path = Path { span: name_binding.span, segments: path_segments }; + let path = + Path { span: name_binding.span, segments: path_segments, tokens: None }; result = Some(( module, ImportSuggestion { @@ -1082,7 +1132,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { if let Res::Def(DefKind::Variant, _) = name_binding.res() { let mut segms = enum_import_suggestion.path.segments.clone(); segms.push(ast::PathSegment::from_ident(ident)); - variants.push(Path { span: name_binding.span, segments: segms }); + variants.push(Path { span: name_binding.span, segments: segms, tokens: None }); } }); variants @@ -1368,13 +1418,13 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { if snippet.starts_with('&') && !snippet.starts_with("&'") { introduce_suggestion .push((param.span, format!("&{} {}", lt_name, &snippet[1..]))); - } else if snippet.starts_with("&'_ ") { + } else if let Some(stripped) = snippet.strip_prefix("&'_ ") { introduce_suggestion - .push((param.span, format!("&{} {}", lt_name, &snippet[4..]))); + .push((param.span, format!("&{} {}", lt_name, stripped))); } } } - introduce_suggestion.push((*for_span, for_sugg.to_string())); + introduce_suggestion.push((*for_span, for_sugg)); introduce_suggestion.push((span, formatter(<_name))); err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect); } @@ -1484,7 +1534,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } }; - let lifetime_names: Vec<_> = lifetime_names.into_iter().collect(); + let lifetime_names: Vec<_> = lifetime_names.iter().collect(); match (&lifetime_names[..], snippet.as_deref()) { ([name], Some("&")) => { suggest_existing(err, &name.as_str()[..], &|name| format!("&{} ", name)); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 5892edf7652..283db1404d0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -8,7 +8,7 @@ //! //! Type-relative name resolution (methods, fields, associated items) happens in `librustc_typeck`. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(nll)] @@ -221,7 +221,7 @@ enum ResolutionError<'a> { /// generic parameters must not be used inside of non trivial constant values. /// /// This error is only emitted when using `min_const_generics`. - ParamInNonTrivialAnonConst(Symbol), + ParamInNonTrivialAnonConst { name: Symbol, is_type: bool }, /// Error E0735: type parameters with a default cannot use `Self` SelfInTyParamDefault, /// Error E0767: use of unreachable label @@ -534,11 +534,8 @@ impl<'a> ModuleData<'a> { if ns != TypeNS { return; } - match binding.res() { - Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => { - collected_traits.push((name, binding)) - } - _ => (), + if let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = binding.res() { + collected_traits.push((name, binding)) } }); *traits = Some(collected_traits.into_boxed_slice()); @@ -867,6 +864,12 @@ pub struct ExternPreludeEntry<'a> { pub introduced_by_item: bool, } +/// Used for better errors for E0773 +enum BuiltinMacroState { + NotYetSeen(SyntaxExtension), + AlreadySeen(Span), +} + /// The main resolver class. /// /// This is the visitor that walks the whole crate. @@ -960,7 +963,7 @@ pub struct Resolver<'a> { crate_loader: CrateLoader<'a>, macro_names: FxHashSet<Ident>, - builtin_macros: FxHashMap<Symbol, SyntaxExtension>, + builtin_macros: FxHashMap<Symbol, BuiltinMacroState>, registered_attrs: FxHashSet<Ident>, registered_tools: FxHashSet<Ident>, macro_use_prelude: FxHashMap<Symbol, &'a NameBinding<'a>>, @@ -999,7 +1002,8 @@ pub struct Resolver<'a> { /// Table for mapping struct IDs into struct constructor IDs, /// it's not used during normal resolution, only for better error reporting. - struct_constructors: DefIdMap<(Res, ty::Visibility)>, + /// Also includes of list of each fields visibility + struct_constructors: DefIdMap<(Res, ty::Visibility, Vec<ty::Visibility>)>, /// Features enabled for this crate. active_features: FxHashSet<Symbol>, @@ -1036,6 +1040,7 @@ pub struct ResolverArenas<'a> { name_resolutions: TypedArena<RefCell<NameResolution<'a>>>, macro_rules_bindings: TypedArena<MacroRulesBinding<'a>>, ast_paths: TypedArena<ast::Path>, + pattern_spans: TypedArena<Span>, } impl<'a> ResolverArenas<'a> { @@ -1067,6 +1072,9 @@ impl<'a> ResolverArenas<'a> { fn alloc_ast_paths(&'a self, paths: &[ast::Path]) -> &'a [ast::Path] { self.ast_paths.alloc_from_iter(paths.iter().cloned()) } + fn alloc_pattern_spans(&'a self, spans: impl Iterator<Item = Span>) -> &'a [Span] { + self.pattern_spans.alloc_from_iter(spans) + } } impl<'a> AsMut<Resolver<'a>> for Resolver<'a> { @@ -2407,7 +2415,14 @@ impl<'a> Resolver<'a> { (format!("maybe a missing crate `{}`?", ident), None) } } else if i == 0 { - (format!("use of undeclared type or module `{}`", ident), None) + if ident + .name + .with(|n| n.chars().next().map_or(false, |c| c.is_ascii_uppercase())) + { + (format!("use of undeclared type `{}`", ident), None) + } else { + (format!("use of undeclared crate or module `{}`", ident), None) + } } else { let mut msg = format!("could not find `{}` in `{}`", ident, path[i - 1].ident); @@ -2521,7 +2536,7 @@ impl<'a> Resolver<'a> { &mut self, rib_index: usize, rib_ident: Ident, - res: Res, + mut res: Res, record_used: bool, span: Span, all_ribs: &[Rib<'a>], @@ -2611,13 +2626,23 @@ impl<'a> Resolver<'a> { ConstantItemRibKind(trivial) => { // HACK(min_const_generics): We currently only allow `N` or `{ N }`. if !trivial && self.session.features_untracked().min_const_generics { - if record_used { - self.report_error( - span, - ResolutionError::ParamInNonTrivialAnonConst(rib_ident.name), - ); + // HACK(min_const_generics): If we encounter `Self` in an anonymous constant + // we can't easily tell if it's generic at this stage, so we instead remember + // this and then enforce the self type to be concrete later on. + if let Res::SelfTy(trait_def, Some((impl_def, _))) = res { + res = Res::SelfTy(trait_def, Some((impl_def, true))); + } else { + if record_used { + self.report_error( + span, + ResolutionError::ParamInNonTrivialAnonConst { + name: rib_ident.name, + is_type: true, + }, + ); + } + return Res::Err; } - return Res::Err; } if in_ty_param_default { @@ -2691,7 +2716,10 @@ impl<'a> Resolver<'a> { if record_used { self.report_error( span, - ResolutionError::ParamInNonTrivialAnonConst(rib_ident.name), + ResolutionError::ParamInNonTrivialAnonConst { + name: rib_ident.name, + is_type: false, + }, ); } return Res::Err; @@ -3176,6 +3204,7 @@ impl<'a> Resolver<'a> { .chain(path_str.split("::").skip(1).map(Ident::from_str)) .map(|i| self.new_ast_path_segment(i)) .collect(), + tokens: None, } } else { ast::Path { @@ -3185,6 +3214,7 @@ impl<'a> Resolver<'a> { .map(Ident::from_str) .map(|i| self.new_ast_path_segment(i)) .collect(), + tokens: None, } }; let module = self.get_module(module_id); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 51518d63ae9..bea71389647 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -3,7 +3,7 @@ use crate::imports::ImportResolver; use crate::Namespace::*; -use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy}; +use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BuiltinMacroState, Determinacy}; use crate::{CrateLint, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding}; use rustc_ast::{self as ast, NodeId}; @@ -11,6 +11,7 @@ use rustc_ast_lowering::ResolverAstLowering; use rustc_ast_pretty::pprust; use rustc_attr::StabilityLevel; use rustc_data_structures::fx::FxHashSet; +use rustc_errors::struct_span_err; use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand, SyntaxExtension}; use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{AstFragment, AstFragmentKind, Invocation, InvocationKind}; @@ -166,7 +167,7 @@ impl<'a> ResolverExpand for Resolver<'a> { } fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension) { - if self.builtin_macros.insert(ident.name, ext).is_some() { + if self.builtin_macros.insert(ident.name, BuiltinMacroState::NotYetSeen(ext)).is_some() { self.session .span_err(ident.span, &format!("built-in macro `{}` was already defined", ident)); } @@ -1076,10 +1077,23 @@ impl<'a> Resolver<'a> { if result.is_builtin { // The macro was marked with `#[rustc_builtin_macro]`. - if let Some(ext) = self.builtin_macros.remove(&item.ident.name) { + if let Some(builtin_macro) = self.builtin_macros.get_mut(&item.ident.name) { // The macro is a built-in, replace its expander function // while still taking everything else from the source code. - result.kind = ext.kind; + // If we already loaded this builtin macro, give a better error message than 'no such builtin macro'. + match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) { + BuiltinMacroState::NotYetSeen(ext) => result.kind = ext.kind, + BuiltinMacroState::AlreadySeen(span) => { + struct_span_err!( + self.session, + item.span, + E0773, + "attempted to define built-in macro more than once" + ) + .span_note(span, "previously defined here") + .emit(); + } + } } else { let msg = format!("cannot find a built-in macro with name `{}`", item.ident); self.session.span_err(item.span, &msg); diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index 629051c1820..f6434689fec 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -1,4 +1,4 @@ -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(nll)] #![feature(or_patterns)] #![recursion_limit = "256"] @@ -21,7 +21,7 @@ use rustc_hir_pretty::{enum_def_to_string, fn_to_string, ty_to_string}; use rustc_middle::hir::map::Map; use rustc_middle::middle::cstore::ExternCrate; use rustc_middle::middle::privacy::AccessLevels; -use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_middle::ty::{self, print::with_no_trimmed_paths, DefIdTree, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, Input, OutputType}; use rustc_session::output::{filename_for_metadata, out_filename}; @@ -438,7 +438,7 @@ impl<'tcx> SaveContext<'tcx> { .next() .map(|item| item.def_id); } - qualname.push_str(">"); + qualname.push('>'); (qualname, trait_id, decl_id, docs, attrs) } @@ -524,12 +524,12 @@ impl<'tcx> SaveContext<'tcx> { pub fn get_expr_data(&self, expr: &hir::Expr<'_>) -> Option<Data> { let ty = self.typeck_results().expr_ty_adjusted_opt(expr)?; - if matches!(ty.kind, ty::Error(_)) { + if matches!(ty.kind(), ty::Error(_)) { return None; } match expr.kind { hir::ExprKind::Field(ref sub_ex, ident) => { - match self.typeck_results().expr_ty_adjusted(&sub_ex).kind { + match self.typeck_results().expr_ty_adjusted(&sub_ex).kind() { ty::Adt(def, _) if !def.is_enum() => { let variant = &def.non_enum_variant(); filter!(self.span_utils, ident.span); @@ -551,7 +551,7 @@ impl<'tcx> SaveContext<'tcx> { } } } - hir::ExprKind::Struct(qpath, ..) => match ty.kind { + hir::ExprKind::Struct(qpath, ..) => match ty.kind() { ty::Adt(def, _) => { let sub_span = qpath.last_segment_span(); filter!(self.span_utils, sub_span); @@ -989,32 +989,34 @@ pub fn process_crate<'l, 'tcx, H: SaveHandler>( config: Option<Config>, mut handler: H, ) { - tcx.dep_graph.with_ignore(|| { - info!("Dumping crate {}", cratename); - - // Privacy checking requires and is done after type checking; use a - // fallback in case the access levels couldn't have been correctly computed. - let access_levels = match tcx.sess.compile_status() { - Ok(..) => tcx.privacy_access_levels(LOCAL_CRATE), - Err(..) => tcx.arena.alloc(AccessLevels::default()), - }; + with_no_trimmed_paths(|| { + tcx.dep_graph.with_ignore(|| { + info!("Dumping crate {}", cratename); + + // Privacy checking requires and is done after type checking; use a + // fallback in case the access levels couldn't have been correctly computed. + let access_levels = match tcx.sess.compile_status() { + Ok(..) => tcx.privacy_access_levels(LOCAL_CRATE), + Err(..) => tcx.arena.alloc(AccessLevels::default()), + }; - let save_ctxt = SaveContext { - tcx, - maybe_typeck_results: None, - access_levels: &access_levels, - span_utils: SpanUtils::new(&tcx.sess), - config: find_config(config), - impl_counter: Cell::new(0), - }; + let save_ctxt = SaveContext { + tcx, + maybe_typeck_results: None, + access_levels: &access_levels, + span_utils: SpanUtils::new(&tcx.sess), + config: find_config(config), + impl_counter: Cell::new(0), + }; - let mut visitor = DumpVisitor::new(save_ctxt); + let mut visitor = DumpVisitor::new(save_ctxt); - visitor.dump_crate_info(cratename, tcx.hir().krate()); - visitor.dump_compilation_options(input, cratename); - visitor.process_crate(tcx.hir().krate()); + visitor.dump_crate_info(cratename, tcx.hir().krate()); + visitor.dump_compilation_options(input, cratename); + visitor.process_crate(tcx.hir().krate()); - handler.save(&visitor.save_ctxt, &visitor.analysis()) + handler.save(&visitor.save_ctxt, &visitor.analysis()) + }) }) } diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index 6dd7f89d594..747e198cd93 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -497,7 +497,7 @@ impl<'hir> Sig for hir::Item<'hir> { sig.text.push_str(&bounds_to_string(bounds)); } // FIXME where clause - sig.text.push_str(";"); + sig.text.push(';'); Ok(sig) } diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index 265b3b95e95..ed48fbf40ac 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -5,7 +5,7 @@ Core encoding and decoding interfaces. */ #![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", + html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", html_playground_url = "https://play.rust-lang.org/", test(attr(allow(unused_variables), deny(warnings))) )] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 1808a0ca59b..ab96b0333f4 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -12,6 +12,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::impl_stable_hash_via_hash; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_target::abi::{Align, TargetDataLayout}; use rustc_target::spec::{Target, TargetTriple}; use crate::parse::CrateConfig; @@ -163,6 +164,21 @@ pub enum LtoCli { Unspecified, } +/// The different settings that the `-Z dump_mir_spanview` flag can have. `Statement` generates a +/// document highlighting each span of every statement (including terminators). `Terminator` and +/// `Block` highlight a single span per `BasicBlock`: the span of the block's `Terminator`, or a +/// computed span for the block, representing the entire range, covering the block's terminator and +/// all of its statements. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum MirSpanview { + /// Default `-Z dump_mir_spanview` or `-Z dump_mir_spanview=statement` + Statement, + /// `-Z dump_mir_spanview=terminator` + Terminator, + /// `-Z dump_mir_spanview=block` + Block, +} + #[derive(Clone, PartialEq, Hash)] pub enum LinkerPluginLto { LinkerPlugin(PathBuf), @@ -313,6 +329,23 @@ impl Default for ErrorOutputType { } } +/// Parameter to control path trimming. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum TrimmedDefPaths { + /// `try_print_trimmed_def_path` never prints a trimmed path and never calls the expensive query + Never, + /// `try_print_trimmed_def_path` calls the expensive query, the query doesn't call `delay_good_path_bug` + Always, + /// `try_print_trimmed_def_path` calls the expensive query, the query calls `delay_good_path_bug` + 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. @@ -549,9 +582,9 @@ impl OutputFilenames { if !ext.is_empty() { if !extension.is_empty() { - extension.push_str("."); + extension.push('.'); extension.push_str(RUST_CGU_EXT); - extension.push_str("."); + extension.push('.'); } extension.push_str(ext); @@ -606,6 +639,7 @@ impl Default for Options { unstable_features: UnstableFeatures::Disallow, debug_assertions: true, actually_rustdoc: false, + trimmed_def_paths: TrimmedDefPaths::default(), cli_forced_codegen_units: None, cli_forced_thinlto_off: false, remap_path_prefix: Vec::new(), @@ -715,6 +749,9 @@ pub fn default_configuration(sess: &Session) -> CrateConfig { let min_atomic_width = sess.target.target.min_atomic_width(); let max_atomic_width = sess.target.target.max_atomic_width(); let atomic_cas = sess.target.target.options.atomic_cas; + let layout = TargetDataLayout::parse(&sess.target.target).unwrap_or_else(|err| { + sess.fatal(&err); + }); let mut ret = FxHashSet::default(); ret.reserve(6); // the minimum number of insertions @@ -736,18 +773,27 @@ pub fn default_configuration(sess: &Session) -> CrateConfig { if sess.target.target.options.has_elf_tls { ret.insert((sym::target_thread_local, None)); } - for &i in &[8, 16, 32, 64, 128] { + for &(i, align) in &[ + (8, layout.i8_align.abi), + (16, layout.i16_align.abi), + (32, layout.i32_align.abi), + (64, layout.i64_align.abi), + (128, layout.i128_align.abi), + ] { if i >= min_atomic_width && i <= max_atomic_width { - let mut insert_atomic = |s| { + let mut insert_atomic = |s, align: Align| { ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s)))); if atomic_cas { ret.insert((sym::target_has_atomic, Some(Symbol::intern(s)))); } + if align.bits() == i { + ret.insert((sym::target_has_atomic_equal_alignment, Some(Symbol::intern(s)))); + } }; let s = i.to_string(); - insert_atomic(&s); + insert_atomic(&s, align); if &s == wordsz { - insert_atomic("ptr"); + insert_atomic("ptr", layout.pointer_align.abi); } } } @@ -785,10 +831,11 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo user_cfg } -pub fn build_target_config(opts: &Options, error_format: ErrorOutputType) -> Config { - let target = Target::search(&opts.target_triple).unwrap_or_else(|e| { +pub fn build_target_config(opts: &Options, target_override: Option<Target>) -> Config { + let target_result = target_override.map_or_else(|| Target::search(&opts.target_triple), Ok); + let target = target_result.unwrap_or_else(|e| { early_error( - error_format, + opts.error_format, &format!( "Error loading target specification: {}. \ Use `--print target-list` for a list of built-in targets", @@ -802,7 +849,7 @@ pub fn build_target_config(opts: &Options, error_format: ErrorOutputType) -> Con "32" => 32, "64" => 64, w => early_error( - error_format, + opts.error_format, &format!( "target specification was invalid: \ unrecognized target-pointer-width {}", @@ -1709,6 +1756,10 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } + if debugging_opts.experimental_coverage { + debugging_opts.instrument_coverage = true; + } + if debugging_opts.instrument_coverage { if cg.profile_generate.enabled() || cg.profile_use.is_some() { early_error( @@ -1718,20 +1769,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } - // `-Z instrument-coverage` implies: - // * `-Z symbol-mangling-version=v0` - to ensure consistent and reversible name mangling. - // Note, LLVM coverage tools can analyze coverage over multiple runs, including some - // changes to source code; so mangled names must be consistent across compilations. - // * `-C link-dead-code` - so unexecuted code is still counted as zero, rather than be - // optimized out. Note that instrumenting dead code can be explicitly disabled with: - // `-Z instrument-coverage -C link-dead-code=no`. + // `-Z instrument-coverage` implies `-Z symbol-mangling-version=v0` - to ensure consistent + // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over + // multiple runs, including some changes to source code; so mangled names must be consistent + // across compilations. debugging_opts.symbol_mangling_version = SymbolManglingVersion::V0; - if cg.link_dead_code == None { - // FIXME(richkadel): Investigate if the `instrument-coverage` implementation can - // inject ["zero counters"](https://llvm.org/docs/CoverageMappingFormat.html#counter) - // in the coverage map when "dead code" is removed, rather than forcing `link-dead-code`. - cg.link_dead_code = Some(true); - } + } + + if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") { + debugging_opts.graphviz_font = graphviz_font; } if !cg.embed_bitcode { @@ -1805,6 +1851,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { unstable_features: UnstableFeatures::from_environment(), debug_assertions, actually_rustdoc: false, + trimmed_def_paths: TrimmedDefPaths::default(), cli_forced_codegen_units: codegen_units, cli_forced_thinlto_off: disable_thinlto, remap_path_prefix, @@ -2051,7 +2098,7 @@ crate mod dep_tracking { use super::{ CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, OutputTypes, Passes, SanitizerSet, SourceFileHashAlgorithm, SwitchWithOptPath, - SymbolManglingVersion, + SymbolManglingVersion, TrimmedDefPaths, }; use crate::lint; use crate::utils::NativeLibKind; @@ -2132,6 +2179,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); impl_dep_tracking_hash_via_hash!(Option<SourceFileHashAlgorithm>); + impl_dep_tracking_hash_via_hash!(TrimmedDefPaths); impl_dep_tracking_hash_for_sortable_vec_of!(String); impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index c2ea141a06f..a808261798d 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,4 +1,5 @@ #![feature(crate_visibility_modifier)] +#![feature(once_cell)] #![feature(or_patterns)] #[macro_use] diff --git a/compiler/rustc_session/src/lint.rs b/compiler/rustc_session/src/lint.rs index 0dcbee08abe..62e021d5e45 100644 --- a/compiler/rustc_session/src/lint.rs +++ b/compiler/rustc_session/src/lint.rs @@ -65,9 +65,15 @@ pub struct Lint { /// /// The name is written with underscores, e.g., "unused_imports". /// On the command line, underscores become dashes. + /// + /// See https://rustc-dev-guide.rust-lang.org/diagnostics.html#lint-naming + /// for naming guidelines. pub name: &'static str, /// Default level for the lint. + /// + /// See https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-levels + /// for guidelines on choosing a default level. pub default_level: Level, /// Description of the lint or the issue it detects. @@ -275,17 +281,60 @@ impl LintBuffer { } /// Declares a static item of type `&'static Lint`. +/// +/// See https://rustc-dev-guide.rust-lang.org/diagnostics.html for documentation +/// and guidelines on writing lints. +/// +/// The macro call should start with a doc comment explaining the lint +/// which will be embedded in the rustc user documentation book. It should +/// be written in markdown and have a format that looks like this: +/// +/// ```rust,ignore (doc-example) +/// /// The `my_lint_name` lint detects [short explanation here]. +/// /// +/// /// ### Example +/// /// +/// /// ```rust +/// /// [insert a concise example that triggers the lint] +/// /// ``` +/// /// +/// /// {{produces}} +/// /// +/// /// ### Explanation +/// /// +/// /// This should be a detailed explanation of *why* the lint exists, +/// /// and also include suggestions on how the user should fix the problem. +/// /// Try to keep the text simple enough that a beginner can understand, +/// /// and include links to other documentation for terminology that a +/// /// beginner may not be familiar with. If this is "allow" by default, +/// /// it should explain why (are there false positives or other issues?). If +/// /// this is a future-incompatible lint, it should say so, with text that +/// /// looks roughly like this: +/// /// +/// /// This is a [future-incompatible] lint to transition this to a hard +/// /// error in the future. See [issue #xxxxx] for more details. +/// /// +/// /// [issue #xxxxx]: https://github.com/rust-lang/rust/issues/xxxxx +/// ``` +/// +/// The `{{produces}}` tag will be automatically replaced with the output from +/// the example by the build system. You can build and view the rustc book +/// with `x.py doc --stage=1 src/doc/rustc --open`. If the lint example is too +/// complex to run as a simple example (for example, it needs an extern +/// crate), mark it with `ignore` and manually paste the expected output below +/// the example. #[macro_export] macro_rules! declare_lint { - ($vis: vis $NAME: ident, $Level: ident, $desc: expr) => ( + ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => ( $crate::declare_lint!( - $vis $NAME, $Level, $desc, + $(#[$attr])* $vis $NAME, $Level, $desc, ); ); - ($vis: vis $NAME: ident, $Level: ident, $desc: expr, + ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, $(@future_incompatible = $fi:expr;)? $(@feature_gate = $gate:expr;)? $($v:ident),*) => ( + $(#[$attr])* $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { name: stringify!($NAME), default_level: $crate::lint::$Level, @@ -298,9 +347,10 @@ macro_rules! declare_lint { ..$crate::lint::Lint::default_fields_for_macro() }; ); - ($vis: vis $NAME: ident, $Level: ident, $desc: expr, + ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, $lint_edition: expr => $edition_level: ident ) => ( + $(#[$attr])* $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { name: stringify!($NAME), default_level: $crate::lint::$Level, diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs index 2db4d2a7f51..562df176b14 100644 --- a/compiler/rustc_session/src/lint/builtin.rs +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -5,11 +5,37 @@ //! lints are all available in `rustc_lint::builtin`. use crate::lint::FutureIncompatibleInfo; -use crate::{declare_lint, declare_lint_pass}; +use crate::{declare_lint, declare_lint_pass, declare_tool_lint}; use rustc_span::edition::Edition; use rustc_span::symbol::sym; declare_lint! { + /// The `ill_formed_attribute_input` lint detects ill-formed attribute + /// inputs that were previously accepted and used in practice. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[inline = "this is not valid"] + /// fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previously, inputs for many built-in attributes weren't validated and + /// nonsensical attribute inputs were accepted. After validation was + /// added, it was determined that some existing projects made use of these + /// invalid forms. This is a [future-incompatible] lint to transition this + /// to a hard error in the future. See [issue #57571] for more details. + /// + /// Check the [attribute reference] for details on the valid inputs for + /// attributes. + /// + /// [issue #57571]: https://github.com/rust-lang/rust/issues/57571 + /// [attribute reference]: https://doc.rust-lang.org/nightly/reference/attributes.html + /// [future-incompatible]: ../index.md#future-incompatible-lints pub ILL_FORMED_ATTRIBUTE_INPUT, Deny, "ill-formed attribute inputs that were previously accepted and used in practice", @@ -21,6 +47,32 @@ declare_lint! { } declare_lint! { + /// The `conflicting_repr_hints` lint detects [`repr` attributes] with + /// conflicting hints. + /// + /// [`repr` attributes]: https://doc.rust-lang.org/reference/type-layout.html#representations + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[repr(u32, u64)] + /// enum Foo { + /// Variant1, + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler incorrectly accepted these conflicting representations in + /// the past. This is a [future-incompatible] lint to transition this to a + /// hard error in the future. See [issue #68585] for more details. + /// + /// To correct the issue, remove one of the conflicting hints. + /// + /// [issue #68585]: https://github.com/rust-lang/rust/issues/68585 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub CONFLICTING_REPR_HINTS, Deny, "conflicts between `#[repr(..)]` hints that were previously accepted and used in practice", @@ -31,30 +83,198 @@ declare_lint! { } declare_lint! { + /// The `meta_variable_misuse` lint detects possible meta-variable misuse + /// in macro definitions. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(meta_variable_misuse)] + /// + /// macro_rules! foo { + /// () => {}; + /// ($( $i:ident = $($j:ident),+ );*) => { $( $( $i = $k; )+ )* }; + /// } + /// + /// fn main() { + /// foo!(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// There are quite a few different ways a [`macro_rules`] macro can be + /// improperly defined. Many of these errors were previously only detected + /// when the macro was expanded or not at all. This lint is an attempt to + /// catch some of these problems when the macro is *defined*. + /// + /// This lint is "allow" by default because it may have false positives + /// and other issues. See [issue #61053] for more details. + /// + /// [`macro_rules`]: https://doc.rust-lang.org/reference/macros-by-example.html + /// [issue #61053]: https://github.com/rust-lang/rust/issues/61053 pub META_VARIABLE_MISUSE, Allow, "possible meta-variable misuse at macro definition" } declare_lint! { + /// The `incomplete_include` lint detects the use of the [`include!`] + /// macro with a file that contains more than one expression. + /// + /// [`include!`]: https://doc.rust-lang.org/std/macro.include.html + /// + /// ### Example + /// + /// ```rust,ignore (needs separate file) + /// fn main() { + /// include!("foo.txt"); + /// } + /// ``` + /// + /// where the file `foo.txt` contains: + /// + /// ```text + /// println!("hi!"); + /// ``` + /// + /// produces: + /// + /// ```text + /// error: include macro expected single expression in source + /// --> foo.txt:1:14 + /// | + /// 1 | println!("1"); + /// | ^ + /// | + /// = note: `#[deny(incomplete_include)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// The [`include!`] macro is currently only intended to be used to + /// include a single [expression] or multiple [items]. Historically it + /// would ignore any contents after the first expression, but that can be + /// confusing. In the example above, the `println!` expression ends just + /// before the semicolon, making the semicolon "extra" information that is + /// ignored. Perhaps even more surprising, if the included file had + /// multiple print statements, the subsequent ones would be ignored! + /// + /// One workaround is to place the contents in braces to create a [block + /// expression]. Also consider alternatives, like using functions to + /// encapsulate the expressions, or use [proc-macros]. + /// + /// This is a lint instead of a hard error because existing projects were + /// found to hit this error. To be cautious, it is a lint for now. The + /// future semantics of the `include!` macro are also uncertain, see + /// [issue #35560]. + /// + /// [items]: https://doc.rust-lang.org/reference/items.html + /// [expression]: https://doc.rust-lang.org/reference/expressions.html + /// [block expression]: https://doc.rust-lang.org/reference/expressions/block-expr.html + /// [proc-macros]: https://doc.rust-lang.org/reference/procedural-macros.html + /// [issue #35560]: https://github.com/rust-lang/rust/issues/35560 pub INCOMPLETE_INCLUDE, Deny, "trailing content in included file" } declare_lint! { + /// The `arithmetic_overflow` lint detects that an arithmetic operation + /// will [overflow]. + /// + /// [overflow]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow + /// + /// ### Example + /// + /// ```rust,compile_fail + /// 1_i32 << 32; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is very likely a mistake to perform an arithmetic operation that + /// overflows its value. If the compiler is able to detect these kinds of + /// overflows at compile-time, it will trigger this lint. Consider + /// adjusting the expression to avoid overflow, or use a data type that + /// will not overflow. pub ARITHMETIC_OVERFLOW, Deny, "arithmetic operation overflows" } declare_lint! { + /// The `unconditional_panic` lint detects an operation that will cause a + /// panic at runtime. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # #![allow(unused)] + /// let x = 1 / 0; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint detects code that is very likely incorrect. When possible, + /// the compiler will attempt to detect situations where code can be + /// evaluated at compile-time to generate more efficient code. While + /// evaluating such code, if it detects that the code will unconditionally + /// panic, this usually indicates that it is doing something incorrectly. + /// If this lint is allowed, then the code will not be evaluated at + /// compile-time, and instead continue to generate code to evaluate at + /// runtime, which may panic during runtime. pub UNCONDITIONAL_PANIC, Deny, "operation will cause a panic at runtime" } declare_lint! { + /// The `const_err` lint detects an erroneous expression while doing + /// constant evaluation. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![allow(unconditional_panic)] + /// let x: &'static i32 = &(1 / 0); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint detects code that is very likely incorrect. If this lint is + /// allowed, then the code will not be evaluated at compile-time, and + /// instead continue to generate code to evaluate at runtime, which may + /// panic during runtime. + /// + /// Note that this lint may trigger in either inside or outside of a + /// [const context]. Outside of a [const context], the compiler can + /// sometimes evaluate an expression at compile-time in order to generate + /// more efficient code. As the compiler becomes better at doing this, it + /// needs to decide what to do when it encounters code that it knows for + /// certain will panic or is otherwise incorrect. Making this a hard error + /// would prevent existing code that exhibited this behavior from + /// compiling, breaking backwards-compatibility. However, this is almost + /// certainly incorrect code, so this is a deny-by-default lint. For more + /// details, see [RFC 1229] and [issue #28238]. + /// + /// Note that there are several other more specific lints associated with + /// compile-time evaluation, such as [`arithmetic_overflow`], + /// [`unconditional_panic`]. + /// + /// [const context]: https://doc.rust-lang.org/reference/const_eval.html#const-context + /// [RFC 1229]: https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md + /// [issue #28238]: https://github.com/rust-lang/rust/issues/28238 + /// [`arithmetic_overflow`]: deny-by-default.html#arithmetic-overflow + /// [`unconditional_panic`]: deny-by-default.html#unconditional-panic pub CONST_ERR, Deny, "constant evaluation detected erroneous expression", @@ -62,18 +282,105 @@ declare_lint! { } declare_lint! { + /// The `unused_imports` lint detects imports that are never used. + /// + /// ### Example + /// + /// ```rust + /// use std::collections::HashMap; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused imports may signal a mistake or unfinished code, and clutter + /// the code, and should be removed. If you intended to re-export the item + /// to make it available outside of the module, add a visibility modifier + /// like `pub`. pub UNUSED_IMPORTS, Warn, "imports that are never used" } declare_lint! { + /// The `unused_extern_crates` lint guards against `extern crate` items + /// that are never used. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_extern_crates)] + /// extern crate proc_macro; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `extern crate` items that are unused have no effect and should be + /// removed. Note that there are some cases where specifying an `extern + /// crate` is desired for the side effect of ensuring the given crate is + /// linked, even though it is not otherwise directly referenced. The lint + /// can be silenced by aliasing the crate to an underscore, such as + /// `extern crate foo as _`. Also note that it is no longer idiomatic to + /// use `extern crate` in the [2018 edition], as extern crates are now + /// automatically added in scope. + /// + /// This lint is "allow" by default because it can be noisy, and produce + /// false-positives. If a dependency is being removed from a project, it + /// is recommended to remove it from the build configuration (such as + /// `Cargo.toml`) to ensure stale build entries aren't left behind. + /// + /// [2018 edition]: https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-clarity.html#no-more-extern-crate pub UNUSED_EXTERN_CRATES, Allow, "extern crates that are never used" } declare_lint! { + /// The `unused_crate_dependencies` lint detects crate dependencies that + /// are never used. + /// + /// ### Example + /// + /// ```rust,ignore (needs extern crate) + /// #![deny(unused_crate_dependencies)] + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: external crate `regex` unused in `lint_example`: remove the dependency or add `use regex as _;` + /// | + /// note: the lint level is defined here + /// --> src/lib.rs:1:9 + /// | + /// 1 | #![deny(unused_crate_dependencies)] + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^ + /// ``` + /// + /// ### Explanation + /// + /// After removing the code that uses a dependency, this usually also + /// requires removing the dependency from the build configuration. + /// However, sometimes that step can be missed, which leads to time wasted + /// building dependencies that are no longer used. This lint can be + /// enabled to detect dependencies that are never used (more specifically, + /// any dependency passed with the `--extern` command-line flag that is + /// never referenced via [`use`], [`extern crate`], or in any [path]). + /// + /// This lint is "allow" by default because it can provide false positives + /// depending on how the build system is configured. For example, when + /// using Cargo, a "package" consists of multiple crates (such as a + /// library and a binary), but the dependencies are defined for the + /// package as a whole. If there is a dependency that is only used in the + /// binary, but not the library, then the lint will be incorrectly issued + /// in the library. + /// + /// [path]: https://doc.rust-lang.org/reference/paths.html + /// [`use`]: https://doc.rust-lang.org/reference/items/use-declarations.html + /// [`extern crate`]: https://doc.rust-lang.org/reference/items/extern-crates.html pub UNUSED_CRATE_DEPENDENCIES, Allow, "crate dependencies that are never used", @@ -81,42 +388,174 @@ declare_lint! { } declare_lint! { + /// The `unused_qualifications` lint detects unnecessarily qualified + /// names. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_qualifications)] + /// mod foo { + /// pub fn bar() {} + /// } + /// + /// fn main() { + /// use foo::bar; + /// foo::bar(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// If an item from another module is already brought into scope, then + /// there is no need to qualify it in this case. You can call `bar()` + /// directly, without the `foo::`. + /// + /// This lint is "allow" by default because it is somewhat pedantic, and + /// doesn't indicate an actual problem, but rather a stylistic choice, and + /// can be noisy when refactoring or moving around code. pub UNUSED_QUALIFICATIONS, Allow, "detects unnecessarily qualified names" } declare_lint! { + /// The `unknown_lints` lint detects unrecognized lint attribute. + /// + /// ### Example + /// + /// ```rust + /// #![allow(not_a_real_lint)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to specify a lint that does not exist. Check + /// the spelling, and check the lint listing for the correct name. Also + /// consider if you are using an old version of the compiler, and the lint + /// is only available in a newer version. pub UNKNOWN_LINTS, Warn, "unrecognized lint attribute" } declare_lint! { + /// The `unused_variables` lint detects variables which are not used in + /// any way. + /// + /// ### Example + /// + /// ```rust + /// let x = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused variables may signal a mistake or unfinished code. To silence + /// the warning for the individual variable, prefix it with an underscore + /// such as `_x`. pub UNUSED_VARIABLES, Warn, "detect variables which are not used in any way" } declare_lint! { + /// The `unused_assignments` lint detects assignments that will never be read. + /// + /// ### Example + /// + /// ```rust + /// let mut x = 5; + /// x = 6; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused assignments may signal a mistake or unfinished code. If the + /// variable is never used after being assigned, then the assignment can + /// be removed. Variables with an underscore prefix such as `_x` will not + /// trigger this lint. pub UNUSED_ASSIGNMENTS, Warn, "detect assignments that will never be read" } declare_lint! { + /// The `dead_code` lint detects unused, unexported items. + /// + /// ### Example + /// + /// ```rust + /// fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Dead code may signal a mistake or unfinished code. To silence the + /// warning for individual items, prefix the name with an underscore such + /// as `_foo`. If it was intended to expose the item outside of the crate, + /// consider adding a visibility modifier like `pub`. Otherwise consider + /// removing the unused code. pub DEAD_CODE, Warn, "detect unused, unexported items" } declare_lint! { + /// The `unused_attributes` lint detects attributes that were not used by + /// the compiler. + /// + /// ### Example + /// + /// ```rust + /// #![macro_export] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused [attributes] may indicate the attribute is placed in the wrong + /// position. Consider removing it, or placing it in the correct position. + /// Also consider if you intended to use an _inner attribute_ (with a `!` + /// such as `#![allow(unused)]`) which applies to the item the attribute + /// is within, or an _outer attribute_ (without a `!` such as + /// `#[allow(unsued)]`) which applies to the item *following* the + /// attribute. + /// + /// [attributes]: https://doc.rust-lang.org/reference/attributes.html pub UNUSED_ATTRIBUTES, Warn, "detects attributes that were not used by the compiler" } declare_lint! { + /// The `unreachable_code` lint detects unreachable code paths. + /// + /// ### Example + /// + /// ```rust,no_run + /// panic!("we never go past here!"); + /// + /// let x = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unreachable code may signal a mistake or unfinished code. If the code + /// is no longer in use, consider removing it. pub UNREACHABLE_CODE, Warn, "detects unreachable code paths", @@ -124,48 +563,217 @@ declare_lint! { } declare_lint! { + /// The `unreachable_patterns` lint detects unreachable patterns. + /// + /// ### Example + /// + /// ```rust + /// let x = 5; + /// match x { + /// y => (), + /// 5 => (), + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This usually indicates a mistake in how the patterns are specified or + /// ordered. In this example, the `y` pattern will always match, so the + /// five is impossible to reach. Remember, match arms match in order, you + /// probably wanted to put the `5` case above the `y` case. pub UNREACHABLE_PATTERNS, Warn, "detects unreachable patterns" } declare_lint! { + /// The `overlapping_patterns` lint detects `match` arms that have + /// [range patterns] that overlap. + /// + /// [range patterns]: https://doc.rust-lang.org/nightly/reference/patterns.html#range-patterns + /// + /// ### Example + /// + /// ```rust + /// let x = 123u8; + /// match x { + /// 0..=100 => { println!("small"); } + /// 100..=255 => { println!("large"); } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is likely a mistake to have range patterns in a match expression + /// that overlap. Check that the beginning and end values are what you + /// expect, and keep in mind that with `..=` the left and right bounds are + /// inclusive. pub OVERLAPPING_PATTERNS, Warn, "detects overlapping patterns" } declare_lint! { + /// The `bindings_with_variant_name` lint detects pattern bindings with + /// the same name as one of the matched variants. + /// + /// ### Example + /// + /// ```rust + /// pub enum Enum { + /// Foo, + /// Bar, + /// } + /// + /// pub fn foo(x: Enum) { + /// match x { + /// Foo => {} + /// Bar => {} + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to specify an enum variant name as an + /// [identifier pattern]. In the example above, the `match` arms are + /// specifying a variable name to bind the value of `x` to. The second arm + /// is ignored because the first one matches *all* values. The likely + /// intent is that the arm was intended to match on the enum variant. + /// + /// Two possible solutions are: + /// + /// * Specify the enum variant using a [path pattern], such as + /// `Enum::Foo`. + /// * Bring the enum variants into local scope, such as adding `use + /// Enum::*;` to the beginning of the `foo` function in the example + /// above. + /// + /// [identifier pattern]: https://doc.rust-lang.org/reference/patterns.html#identifier-patterns + /// [path pattern]: https://doc.rust-lang.org/reference/patterns.html#path-patterns pub BINDINGS_WITH_VARIANT_NAME, Warn, "detects pattern bindings with the same name as one of the matched variants" } declare_lint! { + /// The `unused_macros` lint detects macros that were not used. + /// + /// ### Example + /// + /// ```rust + /// macro_rules! unused { + /// () => {}; + /// } + /// + /// fn main() { + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused macros may signal a mistake or unfinished code. To silence the + /// warning for the individual macro, prefix the name with an underscore + /// such as `_my_macro`. If you intended to export the macro to make it + /// available outside of the crate, use the [`macro_export` attribute]. + /// + /// [`macro_export` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope pub UNUSED_MACROS, Warn, "detects macros that were not used" } declare_lint! { + /// The `warnings` lint allows you to change the level of other + /// lints which produce warnings. + /// + /// ### Example + /// + /// ```rust + /// #![deny(warnings)] + /// fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The `warnings` lint is a bit special; by changing its level, you + /// change every other warning that would produce a warning to whatever + /// value you'd like. As such, you won't ever trigger this lint in your + /// code directly. pub WARNINGS, Warn, "mass-change the level for lints which produce warnings" } declare_lint! { + /// The `unused_features` lint detects unused or unknown features found in + /// crate-level [`feature` attributes]. + /// + /// [`feature` attributes]: https://doc.rust-lang.org/nightly/unstable-book/ + /// + /// Note: This lint is currently not functional, see [issue #44232] for + /// more details. + /// + /// [issue #44232]: https://github.com/rust-lang/rust/issues/44232 pub UNUSED_FEATURES, Warn, "unused features found in crate-level `#[feature]` directives" } declare_lint! { + /// The `stable_features` lint detects a [`feature` attribute] that + /// has since been made stable. + /// + /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/ + /// + /// ### Example + /// + /// ```rust + /// #![feature(test_accepted_feature)] + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When a feature is stabilized, it is no longer necessary to include a + /// `#![feature]` attribute for it. To fix, simply remove the + /// `#![feature]` attribute. pub STABLE_FEATURES, Warn, "stable features found in `#[feature]` directive" } declare_lint! { + /// The `unknown_crate_types` lint detects an unknown crate type found in + /// a [`crate_type` attribute]. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![crate_type="lol"] + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// An unknown value give to the `crate_type` attribute is almost + /// certainly a mistake. + /// + /// [`crate_type` attribute]: https://doc.rust-lang.org/reference/linkage.html pub UNKNOWN_CRATE_TYPES, Deny, "unknown crate type found in `#[crate_type]` directive", @@ -173,18 +781,104 @@ declare_lint! { } declare_lint! { + /// The `trivial_casts` lint detects trivial casts which could be replaced + /// with coercion, which may require [type ascription] or a temporary + /// variable. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(trivial_casts)] + /// let x: &u32 = &42; + /// let y = x as *const u32; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A trivial cast is a cast `e as T` where `e` has type `U` and `U` is a + /// subtype of `T`. This type of cast is usually unnecessary, as it can be + /// usually be inferred. + /// + /// This lint is "allow" by default because there are situations, such as + /// with FFI interfaces or complex type aliases, where it triggers + /// incorrectly, or in situations where it will be more difficult to + /// clearly express the intent. It may be possible that this will become a + /// warning in the future, possibly with [type ascription] providing a + /// convenient way to work around the current issues. See [RFC 401] for + /// historical context. + /// + /// [type ascription]: https://github.com/rust-lang/rust/issues/23416 + /// [RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md pub TRIVIAL_CASTS, Allow, "detects trivial casts which could be removed" } declare_lint! { + /// The `trivial_numeric_casts` lint detects trivial numeric casts of types + /// which could be removed. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(trivial_numeric_casts)] + /// let x = 42_i32 as i32; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A trivial numeric cast is a cast of a numeric type to the same numeric + /// type. This type of cast is usually unnecessary. + /// + /// This lint is "allow" by default because there are situations, such as + /// with FFI interfaces or complex type aliases, where it triggers + /// incorrectly, or in situations where it will be more difficult to + /// clearly express the intent. It may be possible that this will become a + /// warning in the future, possibly with [type ascription] providing a + /// convenient way to work around the current issues. See [RFC 401] for + /// historical context. + /// + /// [type ascription]: https://github.com/rust-lang/rust/issues/23416 + /// [RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md pub TRIVIAL_NUMERIC_CASTS, Allow, "detects trivial casts of numeric types which could be removed" } declare_lint! { + /// The `private_in_public` lint detects private items in public + /// interfaces not caught by the old implementation. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// struct SemiPriv; + /// + /// mod m1 { + /// struct Priv; + /// impl super::SemiPriv { + /// pub fn f(_: Priv) {} + /// } + /// } + /// # fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The visibility rules are intended to prevent exposing private items in + /// public interfaces. This is a [future-incompatible] lint to transition + /// this to a hard error in the future. See [issue #34537] for more + /// details. + /// + /// [issue #34537]: https://github.com/rust-lang/rust/issues/34537 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub PRIVATE_IN_PUBLIC, Warn, "detect private items in public interfaces not caught by the old implementation", @@ -195,12 +889,76 @@ declare_lint! { } declare_lint! { + /// The `exported_private_dependencies` lint detects private dependencies + /// that are exposed in a public interface. + /// + /// ### Example + /// + /// ```rust,ignore (needs-dependency) + /// pub fn foo() -> Option<some_private_dependency::Thing> { + /// None + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: type `bar::Thing` from private dependency 'bar' in public interface + /// --> src/lib.rs:3:1 + /// | + /// 3 | pub fn foo() -> Option<bar::Thing> { + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// | + /// = note: `#[warn(exported_private_dependencies)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// Dependencies can be marked as "private" to indicate that they are not + /// exposed in the public interface of a crate. This can be used by Cargo + /// to independently resolve those dependencies because it can assume it + /// does not need to unify them with other packages using that same + /// dependency. This lint is an indication of a violation of that + /// contract. + /// + /// To fix this, avoid exposing the dependency in your public interface. + /// Or, switch the dependency to a public dependency. + /// + /// Note that support for this is only available on the nightly channel. + /// See [RFC 1977] for more details, as well as the [Cargo documentation]. + /// + /// [RFC 1977]: https://github.com/rust-lang/rfcs/blob/master/text/1977-public-private-dependencies.md + /// [Cargo documentation]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#public-dependency pub EXPORTED_PRIVATE_DEPENDENCIES, Warn, "public interface leaks type from a private dependency" } declare_lint! { + /// The `pub_use_of_private_extern_crate` lint detects a specific + /// situation of re-exporting a private `extern crate`. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// extern crate core; + /// pub use core as reexported_core; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A public `use` declaration should not be used to publicly re-export a + /// private `extern crate`. `pub extern crate` should be used instead. + /// + /// This was historically allowed, but is not the intended behavior + /// according to the visibility rules. This is a [future-incompatible] + /// lint to transition this to a hard error in the future. See [issue + /// #34537] for more details. + /// + /// [issue #34537]: https://github.com/rust-lang/rust/issues/34537 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub PUB_USE_OF_PRIVATE_EXTERN_CRATE, Deny, "detect public re-exports of private extern crates", @@ -211,6 +969,26 @@ declare_lint! { } declare_lint! { + /// The `invalid_type_param_default` lint detects type parameter defaults + /// erroneously allowed in an invalid location. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// fn foo<T=i32>(t: T) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Default type parameters were only intended to be allowed in certain + /// situations, but historically the compiler allowed them everywhere. + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #36887] for more details. + /// + /// [issue #36887]: https://github.com/rust-lang/rust/issues/36887 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub INVALID_TYPE_PARAM_DEFAULT, Deny, "type parameter default erroneously allowed in invalid location", @@ -221,18 +999,174 @@ declare_lint! { } declare_lint! { + /// The `renamed_and_removed_lints` lint detects lints that have been + /// renamed or removed. + /// + /// ### Example + /// + /// ```rust + /// #![deny(raw_pointer_derive)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// To fix this, either remove the lint or use the new name. This can help + /// avoid confusion about lints that are no longer valid, and help + /// maintain consistency for renamed lints. pub RENAMED_AND_REMOVED_LINTS, Warn, "lints that have been renamed or removed" } declare_lint! { + /// The `unaligned_references` lint detects unaligned references to fields + /// of [packed] structs. + /// + /// [packed]: https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unaligned_references)] + /// + /// #[repr(packed)] + /// pub struct Foo { + /// field1: u64, + /// field2: u8, + /// } + /// + /// fn main() { + /// unsafe { + /// let foo = Foo { field1: 0, field2: 0 }; + /// let _ = &foo.field1; + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Creating a reference to an insufficiently aligned packed field is + /// [undefined behavior] and should be disallowed. + /// + /// This lint is "allow" by default because there is no stable + /// alternative, and it is not yet certain how widespread existing code + /// will trigger this lint. + /// + /// See [issue #27060] for more discussion. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// [issue #27060]: https://github.com/rust-lang/rust/issues/27060 pub UNALIGNED_REFERENCES, Allow, "detects unaligned references to fields of packed structs", } declare_lint! { + /// The `const_item_mutation` lint detects attempts to mutate a `const` + /// item. + /// + /// ### Example + /// + /// ```rust + /// const FOO: [i32; 1] = [0]; + /// + /// fn main() { + /// FOO[0] = 1; + /// // This will print "[0]". + /// println!("{:?}", FOO); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Trying to directly mutate a `const` item is almost always a mistake. + /// What is happening in the example above is that a temporary copy of the + /// `const` is mutated, but the original `const` is not. Each time you + /// refer to the `const` by name (such as `FOO` in the example above), a + /// separate copy of the value is inlined at that location. + /// + /// This lint checks for writing directly to a field (`FOO.field = + /// some_value`) or array entry (`FOO[0] = val`), or taking a mutable + /// reference to the const item (`&mut FOO`), including through an + /// autoderef (`FOO.some_mut_self_method()`). + /// + /// There are various alternatives depending on what you are trying to + /// accomplish: + /// + /// * First, always reconsider using mutable globals, as they can be + /// difficult to use correctly, and can make the code more difficult to + /// use or understand. + /// * If you are trying to perform a one-time initialization of a global: + /// * If the value can be computed at compile-time, consider using + /// const-compatible values (see [Constant Evaluation]). + /// * For more complex single-initialization cases, consider using a + /// third-party crate, such as [`lazy_static`] or [`once_cell`]. + /// * If you are using the [nightly channel], consider the new + /// [`lazy`] module in the standard library. + /// * If you truly need a mutable global, consider using a [`static`], + /// which has a variety of options: + /// * Simple data types can be directly defined and mutated with an + /// [`atomic`] type. + /// * More complex types can be placed in a synchronization primitive + /// like a [`Mutex`], which can be initialized with one of the options + /// listed above. + /// * A [mutable `static`] is a low-level primitive, requiring unsafe. + /// Typically This should be avoided in preference of something + /// higher-level like one of the above. + /// + /// [Constant Evaluation]: https://doc.rust-lang.org/reference/const_eval.html + /// [`static`]: https://doc.rust-lang.org/reference/items/static-items.html + /// [mutable `static`]: https://doc.rust-lang.org/reference/items/static-items.html#mutable-statics + /// [`lazy`]: https://doc.rust-lang.org/nightly/std/lazy/index.html + /// [`lazy_static`]: https://crates.io/crates/lazy_static + /// [`once_cell`]: https://crates.io/crates/once_cell + /// [`atomic`]: https://doc.rust-lang.org/std/sync/atomic/index.html + /// [`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html + pub CONST_ITEM_MUTATION, + Warn, + "detects attempts to mutate a `const` item", +} + +declare_lint! { + /// The `safe_packed_borrows` lint detects borrowing a field in the + /// interior of a packed structure with alignment other than 1. + /// + /// ### Example + /// + /// ```rust + /// #[repr(packed)] + /// pub struct Unaligned<T>(pub T); + /// + /// pub struct Foo { + /// start: u8, + /// data: Unaligned<u32>, + /// } + /// + /// fn main() { + /// let x = Foo { start: 0, data: Unaligned(1) }; + /// let y = &x.data.0; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This type of borrow is unsafe and can cause errors on some platforms + /// and violates some assumptions made by the compiler. This was + /// previously allowed unintentionally. This is a [future-incompatible] + /// lint to transition this to a hard error in the future. See [issue + /// #46043] for more details, including guidance on how to solve the + /// problem. + /// + /// [issue #46043]: https://github.com/rust-lang/rust/issues/46043 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub SAFE_PACKED_BORROWS, Warn, "safe borrows of fields of packed structs were erroneously allowed", @@ -243,6 +1177,49 @@ declare_lint! { } declare_lint! { + /// The `patterns_in_fns_without_body` lint detects `mut` identifier + /// patterns as a parameter in functions without a body. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// trait Trait { + /// fn foo(mut arg: u8); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// To fix this, remove `mut` from the parameter in the trait definition; + /// it can be used in the implementation. That is, the following is OK: + /// + /// ```rust + /// trait Trait { + /// fn foo(arg: u8); // Removed `mut` here + /// } + /// + /// impl Trait for i32 { + /// fn foo(mut arg: u8) { // `mut` here is OK + /// + /// } + /// } + /// ``` + /// + /// Trait definitions can define functions without a body to specify a + /// function that implementors must define. The parameter names in the + /// body-less functions are only allowed to be `_` or an [identifier] for + /// documentation purposes (only the type is relevant). Previous versions + /// of the compiler erroneously allowed [identifier patterns] with the + /// `mut` keyword, but this was not intended to be allowed. This is a + /// [future-incompatible] lint to transition this to a hard error in the + /// future. See [issue #35203] for more details. + /// + /// [identifier]: https://doc.rust-lang.org/reference/identifiers.html + /// [identifier patterns]: https://doc.rust-lang.org/reference/patterns.html#identifier-patterns + /// [issue #35203]: https://github.com/rust-lang/rust/issues/35203 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub PATTERNS_IN_FNS_WITHOUT_BODY, Deny, "patterns in functions without body were erroneously allowed", @@ -253,6 +1230,38 @@ declare_lint! { } declare_lint! { + /// The `late_bound_lifetime_arguments` lint detects generic lifetime + /// arguments in path segments with late bound lifetime parameters. + /// + /// ### Example + /// + /// ```rust + /// struct S; + /// + /// impl S { + /// fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} + /// } + /// + /// fn main() { + /// S.late::<'static>(&0, &0); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is not clear how to provide arguments for early-bound lifetime + /// parameters if they are intermixed with late-bound parameters in the + /// same list. For now, providing any explicit arguments will trigger this + /// lint if late-bound parameters are present, so in the future a solution + /// can be adopted without hitting backward compatibility issues. This is + /// a [future-incompatible] lint to transition this to a hard error in the + /// future. See [issue #42868] for more details, along with a description + /// of the difference between early and late-bound parameters. + /// + /// [issue #42868]: https://github.com/rust-lang/rust/issues/42868 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub LATE_BOUND_LIFETIME_ARGUMENTS, Warn, "detects generic lifetime arguments in path segments with late bound lifetime parameters", @@ -263,6 +1272,32 @@ declare_lint! { } declare_lint! { + /// The `order_dependent_trait_objects` lint detects a trait coherency + /// violation that would allow creating two trait impls for the same + /// dynamic trait object involving marker traits. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// pub trait Trait {} + /// + /// impl Trait for dyn Send + Sync { } + /// impl Trait for dyn Sync + Send { } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A previous bug caused the compiler to interpret traits with different + /// orders (such as `Send + Sync` and `Sync + Send`) as distinct types + /// when they were intended to be treated the same. This allowed code to + /// define separate trait implementations when there should be a coherence + /// error. This is a [future-incompatible] lint to transition this to a + /// hard error in the future. See [issue #56484] for more details. + /// + /// [issue #56484]: https://github.com/rust-lang/rust/issues/56484 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub ORDER_DEPENDENT_TRAIT_OBJECTS, Deny, "trait-object types were treated as different depending on marker-trait order", @@ -273,6 +1308,36 @@ declare_lint! { } declare_lint! { + /// The `coherence_leak_check` lint detects conflicting implementations of + /// a trait that are only distinguished by the old leak-check code. + /// + /// ### Example + /// + /// ```rust + /// trait SomeTrait { } + /// impl SomeTrait for for<'a> fn(&'a u8) { } + /// impl<'a> SomeTrait for fn(&'a u8) { } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In the past, the compiler would accept trait implementations for + /// identical functions that differed only in where the lifetime binder + /// appeared. Due to a change in the borrow checker implementation to fix + /// several bugs, this is no longer allowed. However, since this affects + /// existing code, this is a [future-incompatible] lint to transition this + /// to a hard error in the future. + /// + /// Code relying on this pattern should introduce "[newtypes]", + /// like `struct Foo(for<'a> fn(&'a u8))`. + /// + /// See [issue #56105] for more details. + /// + /// [issue #56105]: https://github.com/rust-lang/rust/issues/56105 + /// [newtypes]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction + /// [future-incompatible]: ../index.md#future-incompatible-lints pub COHERENCE_LEAK_CHECK, Warn, "distinct impls distinguished only by the leak-check code", @@ -283,6 +1348,29 @@ declare_lint! { } declare_lint! { + /// The `deprecated` lint detects use of deprecated items. + /// + /// ### Example + /// + /// ```rust + /// #[deprecated] + /// fn foo() {} + /// + /// fn bar() { + /// foo(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Items may be marked "deprecated" with the [`deprecated` attribute] to + /// indicate that they should no longer be used. Usually the attribute + /// should include a note on what to use instead, or check the + /// documentation. + /// + /// [`deprecated` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute pub DEPRECATED, Warn, "detects use of deprecated items", @@ -290,36 +1378,158 @@ declare_lint! { } declare_lint! { + /// The `unused_unsafe` lint detects unnecessary use of an `unsafe` block. + /// + /// ### Example + /// + /// ```rust + /// unsafe {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// If nothing within the block requires `unsafe`, then remove the + /// `unsafe` marker because it is not required and may cause confusion. pub UNUSED_UNSAFE, Warn, "unnecessary use of an `unsafe` block" } declare_lint! { + /// The `unused_mut` lint detects mut variables which don't need to be + /// mutable. + /// + /// ### Example + /// + /// ```rust + /// let mut x = 5; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The preferred style is to only mark variables as `mut` if it is + /// required. pub UNUSED_MUT, Warn, "detect mut variables which don't need to be mutable" } declare_lint! { + /// The `unconditional_recursion` lint detects functions that cannot + /// return without calling themselves. + /// + /// ### Example + /// + /// ```rust + /// fn foo() { + /// foo(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to have a recursive call that does not have + /// some condition to cause it to terminate. If you really intend to have + /// an infinite loop, using a `loop` expression is recommended. pub UNCONDITIONAL_RECURSION, Warn, "functions that cannot return without calling themselves" } declare_lint! { + /// The `single_use_lifetimes` lint detects lifetimes that are only used + /// once. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(single_use_lifetimes)] + /// + /// fn foo<'a>(x: &'a u32) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Specifying an explicit lifetime like `'a` in a function or `impl` + /// should only be used to link together two things. Otherwise, you should + /// just use `'_` to indicate that the lifetime is not linked to anything, + /// or elide the lifetime altogether if possible. + /// + /// This lint is "allow" by default because it was introduced at a time + /// when `'_` and elided lifetimes were first being introduced, and this + /// lint would be too noisy. Also, there are some known false positives + /// that it produces. See [RFC 2115] for historical context, and [issue + /// #44752] for more details. + /// + /// [RFC 2115]: https://github.com/rust-lang/rfcs/blob/master/text/2115-argument-lifetimes.md + /// [issue #44752]: https://github.com/rust-lang/rust/issues/44752 pub SINGLE_USE_LIFETIMES, Allow, "detects lifetime parameters that are only used once" } declare_lint! { + /// The `unused_lifetimes` lint detects lifetime parameters that are never + /// used. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[deny(unused_lifetimes)] + /// + /// pub fn foo<'a>() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused lifetime parameters may signal a mistake or unfinished code. + /// Consider removing the parameter. pub UNUSED_LIFETIMES, Allow, "detects lifetime parameters that are never used" } declare_lint! { + /// The `tyvar_behind_raw_pointer` lint detects raw pointer to an + /// inference variable. + /// + /// ### Example + /// + /// ```rust,edition2015 + /// // edition 2015 + /// let data = std::ptr::null(); + /// let _ = &data as *const *const (); + /// + /// if data.is_null() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This kind of inference was previously allowed, but with the future + /// arrival of [arbitrary self types], this can introduce ambiguity. To + /// resolve this, use an explicit type instead of relying on type + /// inference. + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the 2018 edition. See [issue #46906] for more details. This + /// is currently a hard-error on the 2018 edition, and is "warn" by + /// default in the 2015 edition. + /// + /// [arbitrary self types]: https://github.com/rust-lang/rust/issues/44874 + /// [issue #46906]: https://github.com/rust-lang/rust/issues/46906 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub TYVAR_BEHIND_RAW_POINTER, Warn, "raw pointer to an inference variable", @@ -330,6 +1540,34 @@ declare_lint! { } declare_lint! { + /// The `elided_lifetimes_in_paths` lint detects the use of hidden + /// lifetime parameters. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(elided_lifetimes_in_paths)] + /// struct Foo<'a> { + /// x: &'a u32 + /// } + /// + /// fn foo(x: &Foo) { + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Elided lifetime parameters can make it difficult to see at a glance + /// that borrowing is occurring. This lint ensures that lifetime + /// parameters are always explicitly stated, even if it is the `'_` + /// [placeholder lifetime]. + /// + /// This lint is "allow" by default because it has some known issues, and + /// may require a significant transition for old code. + /// + /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions pub ELIDED_LIFETIMES_IN_PATHS, Allow, "hidden lifetime parameters in types are deprecated", @@ -337,12 +1575,78 @@ declare_lint! { } declare_lint! { + /// The `bare_trait_objects` lint suggests using `dyn Trait` for trait + /// objects. + /// + /// ### Example + /// + /// ```rust + /// trait Trait { } + /// + /// fn takes_trait_object(_: Box<Trait>) { + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Without the `dyn` indicator, it can be ambiguous or confusing when + /// reading code as to whether or not you are looking at a trait object. + /// The `dyn` keyword makes it explicit, and adds a symmetry to contrast + /// with [`impl Trait`]. + /// + /// [`impl Trait`]: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters pub BARE_TRAIT_OBJECTS, Warn, "suggest using `dyn Trait` for trait objects" } declare_lint! { + /// The `absolute_paths_not_starting_with_crate` lint detects fully + /// qualified paths that start with a module name instead of `crate`, + /// `self`, or an extern crate name + /// + /// ### Example + /// + /// ```rust,edition2015,compile_fail + /// #![deny(absolute_paths_not_starting_with_crate)] + /// + /// mod foo { + /// pub fn bar() {} + /// } + /// + /// fn main() { + /// ::foo::bar(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust [editions] allow the language to evolve without breaking + /// backwards compatibility. This lint catches code that uses absolute + /// paths in the style of the 2015 edition. In the 2015 edition, absolute + /// paths (those starting with `::`) refer to either the crate root or an + /// external crate. In the 2018 edition it was changed so that they only + /// refer to external crates. The path prefix `crate::` should be used + /// instead to reference items from the crate root. + /// + /// If you switch the compiler from the 2015 to 2018 edition without + /// updating the code, then it will fail to compile if the old style paths + /// are used. You can manually change the paths to use the `crate::` + /// prefix to transition to the 2018 edition. + /// + /// This lint solves the problem automatically. It is "allow" by default + /// because the code is perfectly valid in the 2015 edition. The [`cargo + /// fix`] tool with the `--edition` flag will switch this lint to "warn" + /// and automatically apply the suggested fix from the compiler. This + /// provides a completely automated way to update old code to the 2018 + /// edition. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html pub ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, Allow, "fully qualified paths that start with a module name \ @@ -354,6 +1658,45 @@ declare_lint! { } declare_lint! { + /// The `illegal_floating_point_literal_pattern` lint detects + /// floating-point literals used in patterns. + /// + /// ### Example + /// + /// ```rust + /// let x = 42.0; + /// + /// match x { + /// 5.0 => {} + /// _ => {} + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous versions of the compiler accepted floating-point literals in + /// patterns, but it was later determined this was a mistake. The + /// semantics of comparing floating-point values may not be clear in a + /// pattern when contrasted with "structural equality". Typically you can + /// work around this by using a [match guard], such as: + /// + /// ```rust + /// # let x = 42.0; + /// + /// match x { + /// y if y == 5.0 => {} + /// _ => {} + /// } + /// ``` + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #41620] for more details. + /// + /// [issue #41620]: https://github.com/rust-lang/rust/issues/41620 + /// [match guard]: https://doc.rust-lang.org/reference/expressions/match-expr.html#match-guards + /// [future-incompatible]: ../index.md#future-incompatible-lints pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, Warn, "floating-point literals cannot be used in patterns", @@ -364,6 +1707,40 @@ declare_lint! { } declare_lint! { + /// The `unstable_name_collisions` lint detects that you have used a name + /// that the standard library plans to add in the future. + /// + /// ### Example + /// + /// ```rust + /// trait MyIterator : Iterator { + /// // is_sorted is an unstable method that already exists on the Iterator trait + /// fn is_sorted(self) -> bool where Self: Sized {true} + /// } + /// + /// impl<T: ?Sized> MyIterator for T where T: Iterator { } + /// + /// let x = vec![1,2,3]; + /// let _ = x.iter().is_sorted(); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When new methods are added to traits in the standard library, they are + /// usually added in an "unstable" form which is only available on the + /// [nightly channel] with a [`feature` attribute]. If there is any + /// pre-existing code which extends a trait to have a method with the same + /// name, then the names will collide. In the future, when the method is + /// stabilized, this will cause an error due to the ambiguity. This lint + /// is an early-warning to let you know that there may be a collision in + /// the future. This can be avoided by adding type annotations to + /// disambiguate which trait method you intend to call, such as + /// `MyIterator::is_sorted(my_iter)` or renaming or removing the method. + /// + /// [nightly channel]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html + /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/ pub UNSTABLE_NAME_COLLISIONS, Warn, "detects name collision with an existing but unstable method", @@ -376,48 +1753,155 @@ declare_lint! { } declare_lint! { + /// The `irrefutable_let_patterns` lint detects detects [irrefutable + /// patterns] in [if-let] and [while-let] statements. + /// + /// + /// + /// ### Example + /// + /// ```rust + /// if let _ = 123 { + /// println!("always runs!"); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// There usually isn't a reason to have an irrefutable pattern in an + /// if-let or while-let statement, because the pattern will always match + /// successfully. A [`let`] or [`loop`] statement will suffice. However, + /// when generating code with a macro, forbidding irrefutable patterns + /// would require awkward workarounds in situations where the macro + /// doesn't know if the pattern is refutable or not. This lint allows + /// macros to accept this form, while alerting for a possibly incorrect + /// use in normal code. + /// + /// See [RFC 2086] for more details. + /// + /// [irrefutable patterns]: https://doc.rust-lang.org/reference/patterns.html#refutability + /// [if-let]: https://doc.rust-lang.org/reference/expressions/if-expr.html#if-let-expressions + /// [while-let]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops + /// [`let`]: https://doc.rust-lang.org/reference/statements.html#let-statements + /// [`loop`]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#infinite-loops + /// [RFC 2086]: https://github.com/rust-lang/rfcs/blob/master/text/2086-allow-if-let-irrefutables.md pub IRREFUTABLE_LET_PATTERNS, Warn, "detects irrefutable patterns in if-let and while-let statements" } declare_lint! { + /// The `unused_labels` lint detects [labels] that are never used. + /// + /// [labels]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#loop-labels + /// + /// ### Example + /// + /// ```rust,no_run + /// 'unused_label: loop {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused labels may signal a mistake or unfinished code. To silence the + /// warning for the individual label, prefix it with an underscore such as + /// `'_my_label:`. pub UNUSED_LABELS, Warn, "detects labels that are never used" } declare_lint! { + /// The `broken_intra_doc_links` lint detects failures in resolving + /// intra-doc link targets. This is a `rustdoc` only lint, see the + /// documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#broken_intra_doc_links pub BROKEN_INTRA_DOC_LINKS, Warn, "failures in resolving intra-doc link targets" } declare_lint! { + /// The `invalid_codeblock_attributes` lint detects code block attributes + /// in documentation examples that have potentially mis-typed values. This + /// is a `rustdoc` only lint, see the documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_codeblock_attributes pub INVALID_CODEBLOCK_ATTRIBUTES, Warn, "codeblock attribute looks a lot like a known one" } declare_lint! { + /// The `missing_crate_level_docs` lint detects if documentation is + /// missing at the crate root. This is a `rustdoc` only lint, see the + /// documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#missing_crate_level_docs pub MISSING_CRATE_LEVEL_DOCS, Allow, "detects crates with no crate-level documentation" } declare_lint! { + /// The `missing_doc_code_examples` lint detects publicly-exported items + /// without code samples in their documentation. This is a `rustdoc` only + /// lint, see the documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#missing_doc_code_examples pub MISSING_DOC_CODE_EXAMPLES, Allow, "detects publicly-exported items without code samples in their documentation" } declare_lint! { + /// The `private_doc_tests` lint detects code samples in docs of private + /// items not documented by `rustdoc`. This is a `rustdoc` only lint, see + /// the documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#private_doc_tests pub PRIVATE_DOC_TESTS, Allow, "detects code samples in docs of private items not documented by rustdoc" } declare_lint! { + /// The `where_clauses_object_safety` lint detects for [object safety] of + /// [where clauses]. + /// + /// [object safety]: https://doc.rust-lang.org/reference/items/traits.html#object-safety + /// [where clauses]: https://doc.rust-lang.org/reference/items/generics.html#where-clauses + /// + /// ### Example + /// + /// ```rust,no_run + /// trait Trait {} + /// + /// trait X { fn foo(&self) where Self: Trait; } + /// + /// impl X for () { fn foo(&self) {} } + /// + /// impl Trait for dyn X {} + /// + /// // Segfault at opt-level 0, SIGILL otherwise. + /// pub fn main() { <dyn X as X>::foo(&()); } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler previously allowed these object-unsafe bounds, which was + /// incorrect. This is a [future-incompatible] lint to transition this to + /// a hard error in the future. See [issue #51443] for more details. + /// + /// [issue #51443]: https://github.com/rust-lang/rust/issues/51443 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub WHERE_CLAUSES_OBJECT_SAFETY, Warn, "checks the object safety of where clauses", @@ -428,6 +1912,63 @@ declare_lint! { } declare_lint! { + /// The `proc_macro_derive_resolution_fallback` lint detects proc macro + /// derives using inaccessible names from parent modules. + /// + /// ### Example + /// + /// ```rust,ignore (proc-macro) + /// // foo.rs + /// #![crate_type = "proc-macro"] + /// + /// extern crate proc_macro; + /// + /// use proc_macro::*; + /// + /// #[proc_macro_derive(Foo)] + /// pub fn foo1(a: TokenStream) -> TokenStream { + /// drop(a); + /// "mod __bar { static mut BAR: Option<Something> = None; }".parse().unwrap() + /// } + /// ``` + /// + /// ```rust,ignore (needs-dependency) + /// // bar.rs + /// #[macro_use] + /// extern crate foo; + /// + /// struct Something; + /// + /// #[derive(Foo)] + /// struct Another; + /// + /// fn main() {} + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: cannot find type `Something` in this scope + /// --> src/main.rs:8:10 + /// | + /// 8 | #[derive(Foo)] + /// | ^^^ names from parent modules are not accessible without an explicit import + /// | + /// = note: `#[warn(proc_macro_derive_resolution_fallback)]` on by default + /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + /// = note: for more information, see issue #50504 <https://github.com/rust-lang/rust/issues/50504> + /// ``` + /// + /// ### Explanation + /// + /// If a proc-macro generates a module, the compiler unintentionally + /// allowed items in that module to refer to items in the crate root + /// without importing them. This is a [future-incompatible] lint to + /// transition this to a hard error in the future. See [issue #50504] for + /// more details. + /// + /// [issue #50504]: https://github.com/rust-lang/rust/issues/50504 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, Warn, "detects proc macro derives using inaccessible names from parent modules", @@ -438,6 +1979,53 @@ declare_lint! { } declare_lint! { + /// The `macro_use_extern_crate` lint detects the use of the + /// [`macro_use` attribute]. + /// + /// ### Example + /// + /// ```rust,ignore (needs extern crate) + /// #![deny(macro_use_extern_crate)] + /// + /// #[macro_use] + /// extern crate serde_json; + /// + /// fn main() { + /// let _ = json!{{}}; + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: deprecated `#[macro_use]` attribute used to import macros should be replaced at use sites with a `use` item to import the macro instead + /// --> src/main.rs:3:1 + /// | + /// 3 | #[macro_use] + /// | ^^^^^^^^^^^^ + /// | + /// note: the lint level is defined here + /// --> src/main.rs:1:9 + /// | + /// 1 | #![deny(macro_use_extern_crate)] + /// | ^^^^^^^^^^^^^^^^^^^^^^ + /// ``` + /// + /// ### Explanation + /// + /// The [`macro_use` attribute] on an [`extern crate`] item causes + /// macros in that external crate to be brought into the prelude of the + /// crate, making the macros in scope everywhere. As part of the efforts + /// to simplify handling of dependencies in the [2018 edition], the use of + /// `extern crate` is being phased out. To bring macros from extern crates + /// into scope, it is recommended to use a [`use` import]. + /// + /// This lint is "allow" by default because this is a stylistic choice + /// that has not been settled, see [issue #52043] for more information. + /// + /// [`macro_use` attribute]: https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute + /// [`use` import]: https://doc.rust-lang.org/reference/items/use-declarations.html + /// [issue #52043]: https://github.com/rust-lang/rust/issues/52043 pub MACRO_USE_EXTERN_CRATE, Allow, "the `#[macro_use]` attribute is now deprecated in favor of using macros \ @@ -445,6 +2033,44 @@ declare_lint! { } declare_lint! { + /// The `macro_expanded_macro_exports_accessed_by_absolute_paths` lint + /// detects macro-expanded [`macro_export`] macros from the current crate + /// that cannot be referred to by absolute paths. + /// + /// [`macro_export`]: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope + /// + /// ### Example + /// + /// ```rust,compile_fail + /// macro_rules! define_exported { + /// () => { + /// #[macro_export] + /// macro_rules! exported { + /// () => {}; + /// } + /// }; + /// } + /// + /// define_exported!(); + /// + /// fn main() { + /// crate::exported!(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The intent is that all macros marked with the `#[macro_export]` + /// attribute are made available in the root of the crate. However, when a + /// `macro_rules!` definition is generated by another macro, the macro + /// expansion is unable to uphold this rule. This is a + /// [future-incompatible] lint to transition this to a hard error in the + /// future. See [issue #53495] for more details. + /// + /// [issue #53495]: https://github.com/rust-lang/rust/issues/53495 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, Deny, "macro-expanded `macro_export` macros from the current crate \ @@ -457,12 +2083,92 @@ declare_lint! { } declare_lint! { + /// The `explicit_outlives_requirements` lint detects unnecessary + /// lifetime bounds that can be inferred. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # #![allow(unused)] + /// #![deny(explicit_outlives_requirements)] + /// + /// struct SharedRef<'a, T> + /// where + /// T: 'a, + /// { + /// data: &'a T, + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// If a `struct` contains a reference, such as `&'a T`, the compiler + /// requires that `T` outlives the lifetime `'a`. This historically + /// required writing an explicit lifetime bound to indicate this + /// requirement. However, this can be overly explicit, causing clutter and + /// unnecessary complexity. The language was changed to automatically + /// infer the bound if it is not specified. Specifically, if the struct + /// contains a reference, directly or indirectly, to `T` with lifetime + /// `'x`, then it will infer that `T: 'x` is a requirement. + /// + /// This lint is "allow" by default because it can be noisy for existing + /// code that already had these requirements. This is a stylistic choice, + /// as it is still valid to explicitly state the bound. It also has some + /// false positives that can cause confusion. + /// + /// See [RFC 2093] for more details. + /// + /// [RFC 2093]: https://github.com/rust-lang/rfcs/blob/master/text/2093-infer-outlives.md pub EXPLICIT_OUTLIVES_REQUIREMENTS, Allow, "outlives requirements can be inferred" } declare_lint! { + /// The `indirect_structural_match` lint detects a `const` in a pattern + /// that manually implements [`PartialEq`] and [`Eq`]. + /// + /// [`PartialEq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html + /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(indirect_structural_match)] + /// + /// struct Plus(i32, i32); + /// const ONE_PLUS_TWO: &&Plus = &&Plus(1, 2); + /// + /// impl PartialEq for Plus { + /// fn eq(&self, other: &Self) -> bool { + /// self.0 + self.1 == other.0 + other.1 + /// } + /// } + /// + /// impl Eq for Plus {} + /// + /// fn main() { + /// if let ONE_PLUS_TWO = &&Plus(3, 0) { + /// println!("semantic!"); + /// } else { + /// println!("structural!"); + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler unintentionally accepted this form in the past. This is a + /// [future-incompatible] lint to transition this to a hard error in the + /// future. See [issue #62411] for a complete description of the problem, + /// and some possible solutions. + /// + /// [issue #62411]: https://github.com/rust-lang/rust/issues/62411 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub INDIRECT_STRUCTURAL_MATCH, // defaulting to allow until rust-lang/rust#62614 is fixed. Allow, @@ -474,6 +2180,17 @@ declare_lint! { } declare_lint! { + /// The `deprecated_in_future` lint is internal to rustc and should not be + /// used by user code. + /// + /// This lint is only enabled in the standard library. It works with the + /// use of `#[rustc_deprecated]` with a `since` field of a version in the + /// future. This allows something to be marked as deprecated in a future + /// version, and then this lint will ensure that the item is no longer + /// used in the standard library. See the [stability documentation] for + /// more details. + /// + /// [stability documentation]: https://rustc-dev-guide.rust-lang.org/stability.html#rustc_deprecated pub DEPRECATED_IN_FUTURE, Allow, "detects use of items that will be deprecated in a future version", @@ -481,6 +2198,54 @@ declare_lint! { } declare_lint! { + /// The `ambiguous_associated_items` lint detects ambiguity between + /// [associated items] and [enum variants]. + /// + /// [associated items]: https://doc.rust-lang.org/reference/items/associated-items.html + /// [enum variants]: https://doc.rust-lang.org/reference/items/enumerations.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// enum E { + /// V + /// } + /// + /// trait Tr { + /// type V; + /// fn foo() -> Self::V; + /// } + /// + /// impl Tr for E { + /// type V = u8; + /// // `Self::V` is ambiguous because it may refer to the associated type or + /// // the enum variant. + /// fn foo() -> Self::V { 0 } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous versions of Rust did not allow accessing enum variants + /// through [type aliases]. When this ability was added (see [RFC 2338]), this + /// introduced some situations where it can be ambiguous what a type + /// was referring to. + /// + /// To fix this ambiguity, you should use a [qualified path] to explicitly + /// state which type to use. For example, in the above example the + /// function can be written as `fn f() -> <Self as Tr>::V { 0 }` to + /// specifically refer to the associated type. + /// + /// This is a [future-incompatible] lint to transition this to a hard + /// error in the future. See [issue #57644] for more details. + /// + /// [issue #57644]: https://github.com/rust-lang/rust/issues/57644 + /// [type aliases]: https://doc.rust-lang.org/reference/items/type-aliases.html#type-aliases + /// [RFC 2338]: https://github.com/rust-lang/rfcs/blob/master/text/2338-type-alias-enum-variants.md + /// [qualified path]: https://doc.rust-lang.org/reference/paths.html#qualified-paths + /// [future-incompatible]: ../index.md#future-incompatible-lints pub AMBIGUOUS_ASSOCIATED_ITEMS, Deny, "ambiguous associated items", @@ -491,6 +2256,27 @@ declare_lint! { } declare_lint! { + /// The `mutable_borrow_reservation_conflict` lint detects the reservation + /// of a two-phased borrow that conflicts with other shared borrows. + /// + /// ### Example + /// + /// ```rust + /// let mut v = vec![0, 1, 2]; + /// let shared = &v; + /// v.push(shared.len()); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This is a [future-incompatible] lint to transition this to a hard error + /// in the future. See [issue #59159] for a complete description of the + /// problem, and some possible solutions. + /// + /// [issue #59159]: https://github.com/rust-lang/rust/issues/59159 + /// [future-incompatible]: ../index.md#future-incompatible-lints pub MUTABLE_BORROW_RESERVATION_CONFLICT, Warn, "reservation of a two-phased borrow conflicts with other shared borrows", @@ -501,6 +2287,38 @@ declare_lint! { } declare_lint! { + /// The `soft_unstable` lint detects unstable features that were + /// unintentionally allowed on stable. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[cfg(test)] + /// extern crate test; + /// + /// #[bench] + /// fn name(b: &mut test::Bencher) { + /// b.iter(|| 123) + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The [`bench` attribute] was accidentally allowed to be specified on + /// the [stable release channel]. Turning this to a hard error would have + /// broken some projects. This lint allows those projects to continue to + /// build correctly when [`--cap-lints`] is used, but otherwise signal an + /// error that `#[bench]` should not be used on the stable channel. This + /// is a [future-incompatible] lint to transition this to a hard error in + /// the future. See [issue #64266] for more details. + /// + /// [issue #64266]: https://github.com/rust-lang/rust/issues/64266 + /// [`bench` attribute]: https://doc.rust-lang.org/nightly/unstable-book/library-features/test.html + /// [stable release channel]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html + /// [`--cap-lints`]: https://doc.rust-lang.org/rustc/lints/levels.html#capping-lints + /// [future-incompatible]: ../index.md#future-incompatible-lints pub SOFT_UNSTABLE, Deny, "a feature gate that doesn't break dependent crates", @@ -511,18 +2329,135 @@ declare_lint! { } declare_lint! { + /// The `inline_no_sanitize` lint detects incompatible use of + /// [`#[inline(always)]`][inline] and [`#[no_sanitize(...)]`][no_sanitize]. + /// + /// [inline]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute + /// [no_sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html + /// + /// ### Example + /// + /// ```rust + /// #![feature(no_sanitize)] + /// + /// #[inline(always)] + /// #[no_sanitize(address)] + /// fn x() {} + /// + /// fn main() { + /// x() + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The use of the [`#[inline(always)]`][inline] attribute prevents the + /// the [`#[no_sanitize(...)]`][no_sanitize] attribute from working. + /// Consider temporarily removing `inline` attribute. pub INLINE_NO_SANITIZE, Warn, "detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`", } declare_lint! { + /// The `asm_sub_register` lint detects using only a subset of a register + /// for inline asm inputs. + /// + /// ### Example + /// + /// ```rust,ignore (fails on system llvm) + /// #![feature(asm)] + /// + /// fn main() { + /// #[cfg(target_arch="x86_64")] + /// unsafe { + /// asm!("mov {0}, {0}", in(reg) 0i16); + /// } + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: formatting may not be suitable for sub-register argument + /// --> src/main.rs:6:19 + /// | + /// 6 | asm!("mov {0}, {0}", in(reg) 0i16); + /// | ^^^ ^^^ ---- for this argument + /// | + /// = note: `#[warn(asm_sub_register)]` on by default + /// = help: use the `x` modifier to have the register formatted as `ax` + /// = help: or use the `r` modifier to keep the default formatting of `rax` + /// ``` + /// + /// ### Explanation + /// + /// Registers on some architectures can use different names to refer to a + /// subset of the register. By default, the compiler will use the name for + /// the full register size. To explicitly use a subset of the register, + /// you can override the default by using a modifier on the template + /// string operand to specify when subregister to use. This lint is issued + /// if you pass in a value with a smaller data type than the default + /// register size, to alert you of possibly using the incorrect width. To + /// fix this, add the suggested modifier to the template, or cast the + /// value to the correct size. + /// + /// See [register template modifiers] for more details. + /// + /// [register template modifiers]: https://doc.rust-lang.org/nightly/unstable-book/library-features/asm.html#register-template-modifiers pub ASM_SUB_REGISTER, Warn, "using only a subset of a register for inline asm inputs", } declare_lint! { + /// The `unsafe_op_in_unsafe_fn` lint detects unsafe operations in unsafe + /// functions without an explicit unsafe block. This lint only works on + /// the [**nightly channel**] with the + /// `#![feature(unsafe_block_in_unsafe_fn)]` feature. + /// + /// [**nightly channel**]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![feature(unsafe_block_in_unsafe_fn)] + /// #![deny(unsafe_op_in_unsafe_fn)] + /// + /// unsafe fn foo() {} + /// + /// unsafe fn bar() { + /// foo(); + /// } + /// + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Currently, an [`unsafe fn`] allows any [unsafe] operation within its + /// body. However, this can increase the surface area of code that needs + /// to be scrutinized for proper behavior. The [`unsafe` block] provides a + /// convenient way to make it clear exactly which parts of the code are + /// performing unsafe operations. In the future, it is desired to change + /// it so that unsafe operations cannot be performed in an `unsafe fn` + /// without an `unsafe` block. + /// + /// The fix to this is to wrap the unsafe code in an `unsafe` block. + /// + /// This lint is "allow" by default because it has not yet been + /// stabilized, and is not yet complete. See [RFC #2585] and [issue + /// #71668] for more details + /// + /// [`unsafe fn`]: https://doc.rust-lang.org/reference/unsafe-functions.html + /// [`unsafe` block]: https://doc.rust-lang.org/reference/expressions/block-expr.html#unsafe-blocks + /// [unsafe]: https://doc.rust-lang.org/reference/unsafety.html + /// [RFC #2585]: https://github.com/rust-lang/rfcs/blob/master/text/2585-unsafe-block-in-unsafe-fn.md + /// [issue #71668]: https://github.com/rust-lang/rust/issues/71668 pub UNSAFE_OP_IN_UNSAFE_FN, Allow, "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", @@ -530,6 +2465,48 @@ declare_lint! { } declare_lint! { + /// The `cenum_impl_drop_cast` lint detects an `as` cast of a field-less + /// `enum` that implements [`Drop`]. + /// + /// [`Drop`]: https://doc.rust-lang.org/std/ops/trait.Drop.html + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// enum E { + /// A, + /// } + /// + /// impl Drop for E { + /// fn drop(&mut self) { + /// println!("Drop"); + /// } + /// } + /// + /// fn main() { + /// let e = E::A; + /// let i = e as u32; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Casting a field-less `enum` that does not implement [`Copy`] to an + /// integer moves the value without calling `drop`. This can result in + /// surprising behavior if it was expected that `drop` should be called. + /// Calling `drop` automatically would be inconsistent with other move + /// operations. Since neither behavior is clear or consistent, it was + /// decided that a cast of this nature will no longer be allowed. + /// + /// This is a [future-incompatible] lint to transition this to a hard error + /// in the future. See [issue #73333] for more details. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + /// [issue #73333]: https://github.com/rust-lang/rust/issues/73333 + /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html pub CENUM_IMPL_DROP_CAST, Warn, "a C-like enum implementing Drop is cast", @@ -539,6 +2516,52 @@ declare_lint! { }; } +declare_lint! { + /// The `const_evaluatable_unchecked` lint detects a generic constant used + /// in a type. + /// + /// ### Example + /// + /// ```rust + /// const fn foo<T>() -> usize { + /// if std::mem::size_of::<*mut T>() < 8 { // size of *mut T does not depend on T + /// 4 + /// } else { + /// 8 + /// } + /// } + /// + /// fn test<T>() { + /// let _ = [0; foo::<T>()]; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In the 1.43 release, some uses of generic parameters in array repeat + /// expressions were accidentally allowed. This is a [future-incompatible] + /// lint to transition this to a hard error in the future. See [issue + /// #76200] for a more detailed description and possible fixes. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + /// [issue #76200]: https://github.com/rust-lang/rust/issues/76200 + pub CONST_EVALUATABLE_UNCHECKED, + Warn, + "detects a generic constant is used in a type without a emitting a warning", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #76200 <https://github.com/rust-lang/rust/issues/76200>", + edition: None, + }; +} + +declare_tool_lint! { + pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL, + Deny, + "detects `#[unstable]` on stable trait implementations for stable types" +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -572,6 +2595,7 @@ declare_lint_pass! { CONST_ERR, RENAMED_AND_REMOVED_LINTS, UNALIGNED_REFERENCES, + CONST_ITEM_MUTATION, SAFE_PACKED_BORROWS, PATTERNS_IN_FNS_WITHOUT_BODY, LATE_BOUND_LIFETIME_ARGUMENTS, @@ -612,10 +2636,29 @@ declare_lint_pass! { UNSAFE_OP_IN_UNSAFE_FN, INCOMPLETE_INCLUDE, CENUM_IMPL_DROP_CAST, + CONST_EVALUATABLE_UNCHECKED, + INEFFECTIVE_UNSTABLE_TRAIT_IMPL, ] } declare_lint! { + /// The `unused_doc_comments` lint detects doc comments that aren't used + /// by `rustdoc`. + /// + /// ### Example + /// + /// ```rust + /// /// docs for x + /// let x = 12; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `rustdoc` does not use doc comments in all positions, and so the doc + /// comment will be ignored. Try changing it to a normal comment with `//` + /// to avoid the warning. pub UNUSED_DOC_COMMENTS, Warn, "detects doc comments that aren't used by rustdoc" diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index d05f1a3f34b..8cc55f4ebe8 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -125,6 +125,9 @@ top_level_options!( // try to not rely on this too much. actually_rustdoc: bool [TRACKED], + // Control path trimming. + trimmed_def_paths: TrimmedDefPaths [TRACKED], + // Specifications of codegen units / ThinLTO which are forced as a // result of parsing command line options. These are not necessarily // what rustc was invoked with, but massaged a bit to agree with @@ -255,6 +258,7 @@ macro_rules! options { pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of(); pub const parse_optimization_fuel: &str = "crate=integer"; + pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0"; pub const parse_lto: &str = @@ -551,6 +555,36 @@ macro_rules! options { } } + fn parse_mir_spanview(slot: &mut Option<MirSpanview>, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + Some(MirSpanview::Statement) + } else { + None + }; + return true + } + } + + let v = match v { + None => { + *slot = Some(MirSpanview::Statement); + return true; + } + Some(v) => v, + }; + + *slot = Some(match v.trim_end_matches("s") { + "statement" | "stmt" => MirSpanview::Statement, + "terminator" | "term" => MirSpanview::Terminator, + "block" | "basicblock" => MirSpanview::Block, + _ => return false, + }); + true + } + fn parse_treat_err_as_bug(slot: &mut Option<usize>, v: Option<&str>) -> bool { match v { Some(s) => { *slot = s.parse().ok().filter(|&x| x != 0); slot.unwrap_or(0) != 0 } @@ -719,6 +753,9 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "extra arguments to append to the linker invocation (space separated)"), link_dead_code: Option<bool> = (None, parse_opt_bool, [UNTRACKED], "keep dead code at link time (useful for code coverage) (default: no)"), + link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED], + "control whether to link Rust provided C objects/libraries or rely + on C toolchain installed in the system"), linker: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED], "system linker to link outputs with"), linker_flavor: Option<LinkerFlavor> = (None, parse_linker_flavor, [UNTRACKED], @@ -813,6 +850,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "enable the experimental Chalk-based trait solving engine"), codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED], "the backend to use"), + combine_cgu: bool = (false, parse_bool, [TRACKED], + "combine CGUs into a single one"), crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED], "inject the given attribute in the crate"), debug_macros: bool = (false, parse_bool, [TRACKED], @@ -849,8 +888,18 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "exclude the pass number when dumping MIR (used in tests) (default: no)"), dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], "in addition to `.mir` files, create graphviz `.dot` files (default: no)"), + dump_mir_spanview: Option<MirSpanview> = (None, parse_mir_spanview, [UNTRACKED], + "in addition to `.mir` files, create `.html` files to view spans for \ + all `statement`s (including terminators), only `terminator` spans, or \ + computed `block` spans (one span encompassing a block's terminator and \ + all statements)."), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), + experimental_coverage: bool = (false, parse_bool, [TRACKED], + "enable and extend the `-Z instrument-coverage` function-level coverage \ + feature, adding additional experimental (likely inaccurate) counters and \ + code regions (used by `rustc` compiler developers to test new coverage \ + counter placements) (default: no)"), fewer_names: bool = (false, parse_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), @@ -860,6 +909,11 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "force all crates to be `rustc_private` unstable (default: no)"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], "set the optimization fuel quota for a crate"), + graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED], + "use dark-themed colors in graphviz output (default: no)"), + graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED], + "use the given `fontname` in graphviz output; can be overridden by setting \ + environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"), hir_stats: bool = (false, parse_bool, [UNTRACKED], "print some statistics about AST and HIR (default: no)"), human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], @@ -885,18 +939,15 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`, \ and is mutually exclusive with `-C profile-generate`/`-C profile-use`); \ - implies `-C link-dead-code` (unless explicitly disabled)` and \ - `-Z symbol-mangling-version=v0`; and disables/overrides some optimization \ - options (default: no)"), + implies `-C link-dead-code` (unless targeting MSVC, or explicitly disabled) \ + and `-Z symbol-mangling-version=v0`; disables/overrides some Rust \ + optimizations (default: no)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], "keep hygiene data after analysis (default: no)"), link_native_libraries: bool = (true, parse_bool, [UNTRACKED], "link native libraries in the linker invocation (default: yes)"), - link_self_contained: Option<bool> = (None, parse_opt_bool, [TRACKED], - "control whether to link Rust provided C objects/libraries or rely - on C toolchain installed in the system"), link_only: bool = (false, parse_bool, [TRACKED], "link the `.rlink` file generated by `-Z no-link` (default: no)"), llvm_time_trace: bool = (false, parse_bool, [UNTRACKED], @@ -967,6 +1018,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print the result of the monomorphization collection pass"), print_type_sizes: bool = (false, parse_bool, [UNTRACKED], "print layout information for each type encountered (default: no)"), + proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], + "show backtraces for panics during proc-macro execution (default: no)"), profile: bool = (false, parse_bool, [TRACKED], "insert profiling code (default: no)"), profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], @@ -1046,6 +1099,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "for every macro invocation, print its name and arguments (default: no)"), treat_err_as_bug: Option<usize> = (None, parse_treat_err_as_bug, [TRACKED], "treat error number `val` that occurs as bug"), + trim_diagnostic_paths: bool = (true, parse_bool, [UNTRACKED], + "in diagnostics, use heuristics to shorten paths referring to items"), ui_testing: bool = (false, parse_bool, [UNTRACKED], "emit compiler diagnostics in a form suitable for UI testing (default: no)"), unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs index e12364b7dac..83b737a73b1 100644 --- a/compiler/rustc_session/src/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -56,16 +56,16 @@ impl PathKind { impl SearchPath { pub fn from_cli_opt(path: &str, output: config::ErrorOutputType) -> Self { - let (kind, path) = if path.starts_with("native=") { - (PathKind::Native, &path["native=".len()..]) - } else if path.starts_with("crate=") { - (PathKind::Crate, &path["crate=".len()..]) - } else if path.starts_with("dependency=") { - (PathKind::Dependency, &path["dependency=".len()..]) - } else if path.starts_with("framework=") { - (PathKind::Framework, &path["framework=".len()..]) - } else if path.starts_with("all=") { - (PathKind::All, &path["all=".len()..]) + let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") { + (PathKind::Native, stripped) + } else if let Some(stripped) = path.strip_prefix("crate=") { + (PathKind::Crate, stripped) + } else if let Some(stripped) = path.strip_prefix("dependency=") { + (PathKind::Dependency, stripped) + } else if let Some(stripped) = path.strip_prefix("framework=") { + (PathKind::Framework, stripped) + } else if let Some(stripped) = path.strip_prefix("all=") { + (PathKind::All, stripped) } else { (PathKind::All, path) }; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index c006e593e47..ff67d3cb107 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -213,6 +213,9 @@ pub struct Session { known_attrs: Lock<MarkedAttrs>, used_attrs: Lock<MarkedAttrs>, + + /// `Span`s for `if` conditions that we have suggested turning into `if let`. + pub if_let_suggestions: Lock<FxHashSet<Span>>, } pub struct PerfStats { @@ -234,6 +237,14 @@ enum DiagnosticBuilderMethod { // Add more variants as needed to support one-time diagnostics. } +/// Trait implemented by error types. This should not be implemented manually. Instead, use +/// `#[derive(SessionDiagnostic)]` -- see [rustc_macros::SessionDiagnostic]. +pub trait SessionDiagnostic<'a> { + /// Write out as a diagnostic out of `sess`. + #[must_use] + fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a>; +} + /// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid /// emitting the same message more than once. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -389,6 +400,9 @@ impl Session { pub fn err(&self, msg: &str) { self.diagnostic().err(msg) } + pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) { + err.into_diagnostic(self).emit() + } pub fn err_count(&self) -> usize { self.diagnostic().err_count() } @@ -439,6 +453,24 @@ impl Session { pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.diagnostic().delay_span_bug(sp, msg) } + + /// Used for code paths of expensive computations that should only take place when + /// warnings or errors are emitted. If no messages are emitted ("good path"), then + /// it's likely a bug. + pub fn delay_good_path_bug(&self, msg: &str) { + if self.opts.debugging_opts.print_type_sizes + || self.opts.debugging_opts.query_dep_graph + || self.opts.debugging_opts.dump_mir.is_some() + || self.opts.debugging_opts.unpretty.is_some() + || self.opts.output_types.contains_key(&OutputType::Mir) + || std::env::var_os("RUSTC_LOG").is_some() + { + return; + } + + self.diagnostic().delay_good_path_bug(msg) + } + pub fn note_without_error(&self, msg: &str) { self.diagnostic().note_without_error(msg) } @@ -1021,6 +1053,40 @@ impl Session { || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY) } + pub fn link_dead_code(&self) -> bool { + match self.opts.cg.link_dead_code { + Some(explicitly_set) => explicitly_set, + None => { + self.opts.debugging_opts.instrument_coverage + && !self.target.target.options.is_like_msvc + // Issue #76038: (rustc `-Clink-dead-code` causes MSVC linker to produce invalid + // binaries when LLVM InstrProf counters are enabled). As described by this issue, + // the "link dead code" option produces incorrect binaries when compiled and linked + // under MSVC. The resulting Rust programs typically crash with a segmentation + // fault, or produce an empty "*.profraw" file (profiling counter results normally + // generated during program exit). + // + // If not targeting MSVC, `-Z instrument-coverage` implies `-C link-dead-code`, so + // unexecuted code is still counted as zero, rather than be optimized out. Note that + // instrumenting dead code can be explicitly disabled with: + // + // `-Z instrument-coverage -C link-dead-code=no`. + // + // FIXME(richkadel): Investigate if `instrument-coverage` implementation can inject + // [zero counters](https://llvm.org/docs/CoverageMappingFormat.html#counter) in the + // coverage map when "dead code" is removed, rather than forcing `link-dead-code`. + // This may not be possible, however, if (as it seems to appear) the "dead code" + // that would otherwise not be linked is only identified as "dead" by the native + // linker. If that's the case, I believe it is too late for the Rust compiler to + // leverage any information it might be able to get from the linker regarding what + // code is dead, to be able to add those counters. + // + // On the other hand, if any Rust compiler passes are optimizing out dead code blocks + // we should inject "zero" counters for those code regions. + } + } + } + pub fn mark_attr_known(&self, attr: &Attribute) { self.known_attrs.lock().mark(attr) } @@ -1168,6 +1234,7 @@ pub fn build_session( diagnostics_output: DiagnosticOutput, driver_lint_caps: FxHashMap<lint::LintId, lint::Level>, file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>, + target_override: Option<Target>, ) -> Session { // FIXME: This is not general enough to make the warning lint completely override // normal diagnostic warnings, since the warning lint can also be denied and changed @@ -1187,7 +1254,7 @@ pub fn build_session( DiagnosticOutput::Raw(write) => Some(write), }; - let target_cfg = config::build_target_config(&sopts, sopts.error_format); + let target_cfg = config::build_target_config(&sopts, target_override); let host_triple = TargetTriple::from_triple(config::host_triple()); let host = Target::search(&host_triple).unwrap_or_else(|e| { early_error(sopts.error_format, &format!("Error loading host specification: {}", e)) @@ -1354,6 +1421,7 @@ pub fn build_session( target_features: FxHashSet::default(), known_attrs: Lock::new(MarkedAttrs::new()), used_attrs: Lock::new(MarkedAttrs::new()), + if_let_suggestions: Default::default(), }; validate_commandline_args_with_session_available(&sess); @@ -1428,20 +1496,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) { ); } - // FIXME(richkadel): See `src/test/run-make-fulldeps/instrument-coverage/Makefile`. After - // compiling with `-Zinstrument-coverage`, the resulting binary generates a segfault during - // the program's exit process (likely while attempting to generate the coverage stats in - // the "*.profraw" file). An investigation to resolve the problem on Windows is ongoing, - // but until this is resolved, the option is disabled on Windows, and the test is skipped - // when targeting `MSVC`. - if sess.opts.debugging_opts.instrument_coverage && sess.target.target.options.is_like_msvc { - sess.warn( - "Rust source-based code coverage instrumentation (with `-Z instrument-coverage`) \ - is not yet supported on Windows when targeting MSVC. The resulting binaries will \ - still be instrumented for experimentation purposes, but may not execute correctly.", - ); - } - const ASAN_SUPPORTED_TARGETS: &[&str] = &[ "aarch64-fuchsia", "aarch64-unknown-linux-gnu", diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index c654dade2ab..96a6956a40c 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -4,16 +4,15 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(array_windows)] #![feature(crate_visibility_modifier)] #![feature(const_fn)] #![feature(const_panic)] #![feature(negative_impls)] #![feature(nll)] -#![feature(optin_builtin_traits)] #![feature(min_specialization)] #![feature(option_expect_none)] -#![feature(refcell_take)] #[macro_use] extern crate rustc_macros; @@ -400,6 +399,13 @@ impl Span { span.with_lo(span.hi) } + #[inline] + /// Returns true if hi == lo + pub fn is_empty(&self) -> bool { + let span = self.data(); + span.hi == span.lo + } + /// Returns `self` if `self` is not the dummy span, and `other` otherwise. pub fn substitute_dummy(self, other: Span) -> Span { if self.is_dummy() { other } else { self } @@ -537,6 +543,12 @@ impl Span { } /// Returns a `Span` that would enclose both `self` and `end`. + /// + /// ```text + /// ____ ___ + /// self lorem ipsum end + /// ^^^^^^^^^^^^^^^^^^^^ + /// ``` pub fn to(self, end: Span) -> Span { let span_data = self.data(); let end_data = end.data(); @@ -560,6 +572,12 @@ impl Span { } /// Returns a `Span` between the end of `self` to the beginning of `end`. + /// + /// ```text + /// ____ ___ + /// self lorem ipsum end + /// ^^^^^^^^^^^^^ + /// ``` pub fn between(self, end: Span) -> Span { let span = self.data(); let end = end.data(); @@ -570,7 +588,13 @@ impl Span { ) } - /// Returns a `Span` between the beginning of `self` to the beginning of `end`. + /// Returns a `Span` from the beginning of `self` until the beginning of `end`. + /// + /// ```text + /// ____ ___ + /// self lorem ipsum end + /// ^^^^^^^^^^^^^^^^^ + /// ``` pub fn until(self, end: Span) -> Span { let span = self.data(); let end = end.data(); @@ -1133,7 +1157,12 @@ impl<S: Encoder> Encodable<S> for SourceFile { let max_line_length = if lines.len() == 1 { 0 } else { - lines.windows(2).map(|w| w[1] - w[0]).map(|bp| bp.to_usize()).max().unwrap() + lines + .array_windows() + .map(|&[fst, snd]| snd - fst) + .map(|bp| bp.to_usize()) + .max() + .unwrap() }; let bytes_per_diff: u8 = match max_line_length { @@ -1148,7 +1177,7 @@ impl<S: Encoder> Encodable<S> for SourceFile { // Encode the first element. lines[0].encode(s)?; - let diff_iter = (&lines[..]).windows(2).map(|w| (w[1] - w[0])); + let diff_iter = lines[..].array_windows().map(|&[fst, snd]| snd - fst); match bytes_per_diff { 1 => { @@ -1529,58 +1558,71 @@ pub trait Pos { fn to_u32(&self) -> u32; } -/// A byte offset. Keep this small (currently 32-bits), as AST contains -/// a lot of them. -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] -pub struct BytePos(pub u32); - -/// A character offset. Because of multibyte UTF-8 characters, a byte offset -/// is not equivalent to a character offset. The `SourceMap` will convert `BytePos` -/// values to `CharPos` values as necessary. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct CharPos(pub usize); +macro_rules! impl_pos { + ( + $( + $(#[$attr:meta])* + $vis:vis struct $ident:ident($inner_vis:vis $inner_ty:ty); + )* + ) => { + $( + $(#[$attr])* + $vis struct $ident($inner_vis $inner_ty); + + impl Pos for $ident { + #[inline(always)] + fn from_usize(n: usize) -> $ident { + $ident(n as $inner_ty) + } -// FIXME: lots of boilerplate in these impls, but so far my attempts to fix -// have been unsuccessful. + #[inline(always)] + fn to_usize(&self) -> usize { + self.0 as usize + } -impl Pos for BytePos { - #[inline(always)] - fn from_usize(n: usize) -> BytePos { - BytePos(n as u32) - } + #[inline(always)] + fn from_u32(n: u32) -> $ident { + $ident(n as $inner_ty) + } - #[inline(always)] - fn to_usize(&self) -> usize { - self.0 as usize - } + #[inline(always)] + fn to_u32(&self) -> u32 { + self.0 as u32 + } + } - #[inline(always)] - fn from_u32(n: u32) -> BytePos { - BytePos(n) - } + impl Add for $ident { + type Output = $ident; - #[inline(always)] - fn to_u32(&self) -> u32 { - self.0 - } -} + #[inline(always)] + fn add(self, rhs: $ident) -> $ident { + $ident(self.0 + rhs.0) + } + } -impl Add for BytePos { - type Output = BytePos; + impl Sub for $ident { + type Output = $ident; - #[inline(always)] - fn add(self, rhs: BytePos) -> BytePos { - BytePos((self.to_usize() + rhs.to_usize()) as u32) - } + #[inline(always)] + fn sub(self, rhs: $ident) -> $ident { + $ident(self.0 - rhs.0) + } + } + )* + }; } -impl Sub for BytePos { - type Output = BytePos; +impl_pos! { + /// A byte offset. Keep this small (currently 32-bits), as AST contains + /// a lot of them. + #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] + pub struct BytePos(pub u32); - #[inline(always)] - fn sub(self, rhs: BytePos) -> BytePos { - BytePos((self.to_usize() - rhs.to_usize()) as u32) - } + /// A character offset. Because of multibyte UTF-8 characters, a byte offset + /// is not equivalent to a character offset. The `SourceMap` will convert `BytePos` + /// values to `CharPos` values as necessary. + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] + pub struct CharPos(pub usize); } impl<S: rustc_serialize::Encoder> Encodable<S> for BytePos { @@ -1595,46 +1637,6 @@ impl<D: rustc_serialize::Decoder> Decodable<D> for BytePos { } } -impl Pos for CharPos { - #[inline(always)] - fn from_usize(n: usize) -> CharPos { - CharPos(n) - } - - #[inline(always)] - fn to_usize(&self) -> usize { - self.0 - } - - #[inline(always)] - fn from_u32(n: u32) -> CharPos { - CharPos(n as usize) - } - - #[inline(always)] - fn to_u32(&self) -> u32 { - self.0 as u32 - } -} - -impl Add for CharPos { - type Output = CharPos; - - #[inline(always)] - fn add(self, rhs: CharPos) -> CharPos { - CharPos(self.to_usize() + rhs.to_usize()) - } -} - -impl Sub for CharPos { - type Output = CharPos; - - #[inline(always)] - fn sub(self, rhs: CharPos) -> CharPos { - CharPos(self.to_usize() - rhs.to_usize()) - } -} - // _____________________________________________________________________________ // Loc, SourceFileAndLine, SourceFileAndBytePos // diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 7c656db22ed..37596b8ef6f 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -487,6 +487,15 @@ impl SourceMap { } } + /// Returns a new `Span` covering the start and end `BytePos`s of the file containing the given + /// `pos`. This can be used to quickly determine if another `BytePos` or `Span` is from the same + /// file. + pub fn lookup_file_span(&self, pos: BytePos) -> Span { + let idx = self.lookup_source_file_idx(pos); + let SourceFile { start_pos, end_pos, .. } = *(*self.files.borrow().source_files)[idx]; + Span::with_root_ctxt(start_pos, end_pos) + } + /// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If /// there are gaps between LHS and RHS, the resulting union will cross these gaps. /// For this to work, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5092b945f72..2d5c6451d1a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -110,7 +110,7 @@ symbols! { // called `sym::proc_macro` because then it's easy to mistakenly think it // represents "proc_macro". // - // As well as the symbols listed, there are symbols for the the strings + // As well as the symbols listed, there are symbols for the strings // "0", "1", ..., "9", which are accessible via `sym::integer`. // // The proc macro will abort if symbols are not in alphabetical order (as @@ -348,6 +348,7 @@ symbols! { const_compare_raw_pointers, const_constructor, const_eval_limit, + const_evaluatable_checked, const_extern_fn, const_fn, const_fn_transmute, @@ -415,7 +416,9 @@ symbols! { deny, deprecated, deref, + deref_method, deref_mut, + deref_target, derive, diagnostic, direct, @@ -676,7 +679,6 @@ symbols! { minnumf32, minnumf64, mips_target_feature, - mmx_target_feature, module, module_path, more_struct_aliases, @@ -1069,6 +1071,7 @@ symbols! { target_feature, target_feature_11, target_has_atomic, + target_has_atomic_equal_alignment, target_has_atomic_load_store, target_os, target_pointer_width, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 24356844baf..6f9fc115cae 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -110,7 +110,7 @@ fn get_symbol_hash<'tcx>( // If this is a function, we hash the signature as well. // This is not *strictly* needed, but it may help in some // situations, see the `run-make/a-b-a-linker-guard` test. - if let ty::FnDef(..) = item_type.kind { + if let ty::FnDef(..) = item_type.kind() { item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher); } @@ -210,7 +210,7 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { } fn print_type(self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> { - match ty.kind { + match *ty.kind() { // Print all nominal types as paths (unlike `pretty_print_type`). ty::FnDef(def_id, substs) | ty::Opaque(def_id, substs) @@ -258,7 +258,7 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { ) -> Result<Self::Path, Self::Error> { // Similar to `pretty_path_qualified`, but for the other // types that are printed as paths (see `print_type` above). - match self_ty.kind { + match self_ty.kind() { ty::FnDef(..) | ty::Opaque(..) | ty::Projection(_) diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 296b40c4e39..75150a56c43 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -87,7 +87,7 @@ //! virtually impossible. Thus, symbol hash generation exclusively relies on //! DefPaths which are much more robust in the face of changes to the code base. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(never_type)] #![feature(nll)] #![feature(or_patterns)] diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index f01c3da8b5f..091d488138e 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -152,11 +152,8 @@ impl SymbolMangler<'tcx> { let _ = write!(self.out, "{}", ident.len()); // Write a separating `_` if necessary (leading digit or `_`). - match ident.chars().next() { - Some('_' | '0'..='9') => { - self.push("_"); - } - _ => {} + if let Some('_' | '0'..='9') = ident.chars().next() { + self.push("_"); } self.push(ident); @@ -323,7 +320,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> { // Basic types, never cached (single-character). - let basic_type = match ty.kind { + let basic_type = match ty.kind() { ty::Bool => "b", ty::Char => "c", ty::Str => "e", @@ -359,7 +356,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } let start = self.out.len(); - match ty.kind { + match *ty.kind() { // Basic types, handled above. ty::Bool | ty::Char | ty::Str | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Never => { unreachable!() @@ -505,7 +502,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } let start = self.out.len(); - match ct.ty.kind { + match ct.ty.kind() { ty::Uint(_) => {} _ => { bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty, ct); diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 5788e1e8385..fb747dfcbd3 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -7,7 +7,7 @@ //! more 'stuff' here in the future. It does not have a dependency on //! LLVM. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(const_fn)] #![feature(const_panic)] diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index ff559c2bfd6..527a322d56a 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -45,6 +45,8 @@ pub fn target(target_cpu: String) -> TargetResult { late_link_args: vec![(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])] .into_iter() .collect(), + max_atomic_width: Some(0), + atomic_cas: false, ..TargetOptions::default() }, }) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index d6e8b304380..f1e8330425e 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -654,6 +654,7 @@ supported_targets! { ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), + ("riscv32gc-unknown-linux-gnu", riscv32gc_unknown_linux_gnu), ("riscv64imac-unknown-none-elf", riscv64imac_unknown_none_elf), ("riscv64gc-unknown-none-elf", riscv64gc_unknown_none_elf), ("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu), diff --git a/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs new file mode 100644 index 00000000000..28710c60175 --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs @@ -0,0 +1,25 @@ +use crate::spec::{CodeModel, LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "riscv32-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + target_env: "gnu".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(), + arch: "riscv32".to_string(), + target_os: "linux".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + unsupported_abis: super::riscv_base::unsupported_abis(), + code_model: Some(CodeModel::Medium), + cpu: "generic-rv32".to_string(), + features: "+m,+a,+f,+d,+c".to_string(), + llvm_abiname: "ilp32d".to_string(), + max_atomic_width: Some(32), + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs index 62fc8f06183..a7957d84cbe 100644 --- a/compiler/rustc_target/src/spec/wasm32_base.rs +++ b/compiler/rustc_target/src/spec/wasm32_base.rs @@ -83,6 +83,7 @@ pub fn options() -> TargetOptions { dll_prefix: String::new(), dll_suffix: ".wasm".to_string(), linker_is_gnu: false, + eh_frame_header: false, max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 0bba7bdd473..351167105ec 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -30,7 +30,7 @@ //! ## No interop with C required //! //! By default the `crt-static` target feature is enabled, and when enabled -//! this means that the the bundled version of `libc.a` found in `liblibc.rlib` +//! this means that the bundled version of `libc.a` found in `liblibc.rlib` //! is used. This isn't intended really for interoperation with a C because it //! may be the case that Rust's bundled C library is incompatible with a //! foreign-compiled C library. In this use case, though, we use `rust-lld` and diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs index fd55a0fc6a1..fcb2af0005f 100644 --- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs @@ -22,7 +22,7 @@ pub fn opts() -> TargetOptions { "-lmingw32".to_string(), ]; late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone()); - late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs.clone()); + late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs); TargetOptions { executables: false, diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs index 02eefe56223..b9c5123e49a 100644 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ b/compiler/rustc_trait_selection/src/autoderef.rs @@ -27,6 +27,7 @@ pub struct Autoderef<'a, 'tcx> { // Meta infos: infcx: &'a InferCtxt<'a, 'tcx>, span: Span, + overloaded_span: Span, body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, @@ -98,10 +99,12 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { body_id: hir::HirId, span: Span, base_ty: Ty<'tcx>, + overloaded_span: Span, ) -> Autoderef<'a, 'tcx> { Autoderef { infcx, span, + overloaded_span, body_id, param_env, state: AutoderefSnapshot { @@ -190,6 +193,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { self.span } + pub fn overloaded_span(&self) -> Span { + self.overloaded_span + } + pub fn reached_recursion_limit(&self) -> bool { self.state.reached_recursion_limit } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index b5882df4729..ddeab340f38 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -10,10 +10,12 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] +#![feature(box_patterns)] #![feature(drain_filter)] #![feature(in_band_lifetimes)] +#![feature(never_type)] #![feature(crate_visibility_modifier)] #![feature(or_patterns)] #![recursion_limit = "512"] // For rustdoc diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 379c976df69..28697ec4e3b 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -708,11 +708,11 @@ where fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { // We're only interested in types involving regions - if !ty.flags.intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { return false; // keep visiting } - match ty.kind { + match ty.kind() { ty::Closure(_, ref substs) => { // Skip lifetime parameters of the enclosing item(s) @@ -866,7 +866,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { + match *ty.kind() { ty::Closure(def_id, substs) => { // I am a horrible monster and I pray for death. When // we encounter a closure here, it is always a closure @@ -1003,7 +1003,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { ty_op: |ty| { if ty.references_error() { return tcx.ty_error(); - } else if let ty::Opaque(def_id, substs) = ty.kind { + } 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 @@ -1261,7 +1261,8 @@ crate fn required_region_bounds( | ty::PredicateAtom::ClosureKind(..) | ty::PredicateAtom::RegionOutlives(..) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => None, + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => { // Search for a bound of the form `erased_self_ty // : 'a`, but be wary of something like `for<'a> diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index fa3d0241998..6b87bc4f34a 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -373,14 +373,12 @@ impl AutoTraitFinder<'tcx> { computed_preds.clone().chain(user_computed_preds.iter().cloned()), ) .map(|o| o.predicate); - new_env = - ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal(), None); + new_env = ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal()); } let final_user_env = ty::ParamEnv::new( tcx.mk_predicates(user_computed_preds.into_iter()), user_env.reveal(), - None, ); debug!( "evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \ @@ -598,7 +596,7 @@ impl AutoTraitFinder<'tcx> { } pub fn is_of_param(&self, ty: Ty<'_>) -> bool { - match ty.kind { + match ty.kind() { ty::Param(_) => true, ty::Projection(p) => self.is_of_param(p.self_ty()), _ => false, @@ -606,7 +604,7 @@ impl AutoTraitFinder<'tcx> { } fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { - match p.ty().skip_binder().kind { + match *p.ty().skip_binder().kind() { ty::Projection(proj) if proj == p.skip_binder().projection_ty => true, _ => false, } diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index 0097097707f..adc8ae59086 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -4,12 +4,11 @@ use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; use crate::traits::query::NoSolution; use crate::traits::{ - ChalkEnvironmentAndGoal, ChalkEnvironmentClause, FulfillmentError, FulfillmentErrorCode, - ObligationCause, PredicateObligation, SelectionError, TraitEngine, + ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, ObligationCause, + PredicateObligation, SelectionError, TraitEngine, }; use rustc_data_structures::fx::FxIndexSet; -use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty}; pub struct FulfillmentContext<'tcx> { obligations: FxIndexSet<PredicateObligation<'tcx>>, @@ -21,132 +20,6 @@ impl FulfillmentContext<'tcx> { } } -fn environment<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, -) -> &'tcx ty::List<ChalkEnvironmentClause<'tcx>> { - use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind}; - use rustc_middle::ty::subst::GenericArgKind; - - debug!("environment(def_id = {:?})", def_id); - - // The environment of an impl Trait type is its defining function's environment. - if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) { - return environment(tcx, parent); - } - - // Compute the bounds on `Self` and the type parameters. - let ty::InstantiatedPredicates { predicates, .. } = - tcx.predicates_of(def_id).instantiate_identity(tcx); - - let clauses = predicates.into_iter().map(ChalkEnvironmentClause::Predicate); - - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let node = tcx.hir().get(hir_id); - - enum NodeKind { - TraitImpl, - InherentImpl, - Fn, - Other, - }; - - let node_kind = match node { - Node::TraitItem(item) => match item.kind { - TraitItemKind::Fn(..) => NodeKind::Fn, - _ => NodeKind::Other, - }, - - Node::ImplItem(item) => match item.kind { - ImplItemKind::Fn(..) => NodeKind::Fn, - _ => NodeKind::Other, - }, - - Node::Item(item) => match item.kind { - ItemKind::Impl { of_trait: Some(_), .. } => NodeKind::TraitImpl, - ItemKind::Impl { of_trait: None, .. } => NodeKind::InherentImpl, - ItemKind::Fn(..) => NodeKind::Fn, - _ => NodeKind::Other, - }, - - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Fn(..) => NodeKind::Fn, - _ => NodeKind::Other, - }, - - // FIXME: closures? - _ => NodeKind::Other, - }; - - // FIXME(eddyb) isn't the unordered nature of this a hazard? - let mut inputs = FxIndexSet::default(); - - match node_kind { - // In a trait impl, we assume that the header trait ref and all its - // constituents are well-formed. - NodeKind::TraitImpl => { - let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl"); - - // FIXME(chalk): this has problems because of late-bound regions - //inputs.extend(trait_ref.substs.iter().flat_map(|arg| arg.walk())); - inputs.extend(trait_ref.substs.iter()); - } - - // In an inherent impl, we assume that the receiver type and all its - // constituents are well-formed. - NodeKind::InherentImpl => { - let self_ty = tcx.type_of(def_id); - inputs.extend(self_ty.walk()); - } - - // In an fn, we assume that the arguments and all their constituents are - // well-formed. - NodeKind::Fn => { - let fn_sig = tcx.fn_sig(def_id); - let fn_sig = tcx.liberate_late_bound_regions(def_id, &fn_sig); - - inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk())); - } - - NodeKind::Other => (), - } - let input_clauses = inputs.into_iter().filter_map(|arg| { - match arg.unpack() { - GenericArgKind::Type(ty) => Some(ChalkEnvironmentClause::TypeFromEnv(ty)), - - // FIXME(eddyb) no WF conditions from lifetimes? - GenericArgKind::Lifetime(_) => None, - - // FIXME(eddyb) support const generics in Chalk - GenericArgKind::Const(_) => None, - } - }); - - tcx.mk_chalk_environment_clause_list(clauses.chain(input_clauses)) -} - -/// We need to wrap a `ty::Predicate` in an elaborated environment *before* we -/// canonicalize. This is due to the fact that we insert extra clauses into the -/// environment for all input types (`FromEnv`). -fn in_environment( - infcx: &InferCtxt<'_, 'tcx>, - obligation: &PredicateObligation<'tcx>, -) -> ChalkEnvironmentAndGoal<'tcx> { - assert!(!infcx.is_in_snapshot()); - let obligation = infcx.resolve_vars_if_possible(obligation); - - let environment = match obligation.param_env.def_id { - Some(def_id) => environment(infcx.tcx, def_id), - None if obligation.param_env.caller_bounds().is_empty() => ty::List::empty(), - // FIXME(chalk): this is hit in ui/where-clauses/where-clause-constraints-are-local-for-trait-impl - // and ui/generics/generic-static-methods - //_ => bug!("non-empty `ParamEnv` with no def-id"), - _ => ty::List::empty(), - }; - - ChalkEnvironmentAndGoal { environment, goal: obligation.predicate } -} - impl TraitEngine<'tcx> for FulfillmentContext<'tcx> { fn normalize_projection_type( &mut self, @@ -195,6 +68,8 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> { &mut self, infcx: &InferCtxt<'_, 'tcx>, ) -> Result<(), Vec<FulfillmentError<'tcx>>> { + assert!(!infcx.is_in_snapshot()); + let mut errors = Vec::new(); let mut next_round = FxIndexSet::default(); let mut making_progress; @@ -205,10 +80,11 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> { // We iterate over all obligations, and record if we are able // to unambiguously prove at least one obligation. for obligation in self.obligations.drain(..) { - let goal_in_environment = in_environment(infcx, &obligation); + let obligation = infcx.resolve_vars_if_possible(&obligation); + let environment = obligation.param_env.caller_bounds(); + let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate }; let mut orig_values = OriginalQueryValues::default(); - let canonical_goal = - infcx.canonicalize_query(&goal_in_environment, &mut orig_values); + let canonical_goal = infcx.canonicalize_query(&goal, &mut orig_values); match infcx.tcx.evaluate_goal(canonical_goal) { Ok(response) => { diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index b06cf4411d0..c53c65c00b7 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -182,7 +182,7 @@ fn overlap_within_probe( } if !skip_leak_check.is_yes() { - if let Err(_) = infcx.leak_check(true, snapshot) { + if infcx.leak_check(true, snapshot).is_err() { debug!("overlap: leak check failed"); return None; } @@ -412,7 +412,7 @@ fn orphan_check_trait_ref<'tcx>( if non_local_tys.is_empty() { debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty); return Ok(()); - } else if let ty::Param(_) = input_ty.kind { + } else if let ty::Param(_) = input_ty.kind() { debug!("orphan_check_trait_ref: uncovered ty: `{:?}`", input_ty); let local_type = trait_ref .substs @@ -467,7 +467,7 @@ fn fundamental_ty_inner_tys( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option<impl Iterator<Item = Ty<'tcx>>> { - let (first_ty, rest_tys) = match ty.kind { + let (first_ty, rest_tys) = match *ty.kind() { ty::Ref(_, ty, _) => (ty, ty::subst::InternalSubsts::empty().types()), ty::Adt(def, substs) if def.is_fundamental() => { let mut types = substs.types(); @@ -504,7 +504,7 @@ fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool { fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool { debug!("ty_is_local_constructor({:?})", ty); - match ty.kind { + match *ty.kind() { ty::Bool | ty::Char | ty::Int(..) diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs new file mode 100644 index 00000000000..0cfcaca9060 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -0,0 +1,470 @@ +//! Checking that constant values used in types can be successfully evaluated. +//! +//! For concrete constants, this is fairly simple as we can just try and evaluate it. +//! +//! When dealing with polymorphic constants, for example `std::mem::size_of::<T>() - 1`, +//! this is not as easy. +//! +//! In this case we try to build an abstract representation of this constant using +//! `mir_abstract_const` which can then be checked for structural equality with other +//! generic constants mentioned in the `caller_bounds` of the current environment. +use rustc_errors::ErrorReported; +use rustc_hir::def::DefKind; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_infer::infer::InferCtxt; +use rustc_middle::mir::abstract_const::{Node, NodeId}; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::mir::{self, Rvalue, StatementKind, TerminatorKind}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; +use rustc_session::lint; +use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::Span; + +pub fn is_const_evaluatable<'cx, 'tcx>( + infcx: &InferCtxt<'cx, 'tcx>, + def: ty::WithOptConstParam<DefId>, + substs: SubstsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, +) -> Result<(), ErrorHandled> { + debug!("is_const_evaluatable({:?}, {:?})", def, substs); + if infcx.tcx.features().const_evaluatable_checked { + if let Some(ct) = AbstractConst::new(infcx.tcx, def, substs)? { + for pred in param_env.caller_bounds() { + match pred.skip_binders() { + ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => { + debug!("is_const_evaluatable: caller_bound={:?}, {:?}", b_def, b_substs); + if b_def == def && b_substs == substs { + debug!("is_const_evaluatable: caller_bound ~~> ok"); + return Ok(()); + } else if AbstractConst::new(infcx.tcx, b_def, b_substs)? + .map_or(false, |b_ct| try_unify(infcx.tcx, ct, b_ct)) + { + debug!("is_const_evaluatable: abstract_const ~~> ok"); + return Ok(()); + } + } + _ => {} // don't care + } + } + } + } + + let future_compat_lint = || { + if let Some(local_def_id) = def.did.as_local() { + infcx.tcx.struct_span_lint_hir( + lint::builtin::CONST_EVALUATABLE_UNCHECKED, + infcx.tcx.hir().local_def_id_to_hir_id(local_def_id), + span, + |err| { + err.build("cannot use constants which depend on generic parameters in types") + .emit(); + }, + ); + } + }; + + // FIXME: We should only try to evaluate a given constant here if it is fully concrete + // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`. + // + // We previously did not check this, so we only emit a future compat warning if + // const evaluation succeeds and the given constant is still polymorphic for now + // and hopefully soon change this to an error. + // + // See #74595 for more details about this. + let concrete = infcx.const_eval_resolve(param_env, def, substs, None, Some(span)); + + if concrete.is_ok() && substs.has_param_types_or_consts() { + match infcx.tcx.def_kind(def.did) { + DefKind::AnonConst => { + let mir_body = if let Some(def) = def.as_const_arg() { + infcx.tcx.optimized_mir_of_const_arg(def) + } else { + infcx.tcx.optimized_mir(def.did) + }; + + if mir_body.is_polymorphic { + future_compat_lint(); + } + } + _ => future_compat_lint(), + } + } + + debug!(?concrete, "is_const_evaluatable"); + concrete.map(drop) +} + +/// A tree representing an anonymous constant. +/// +/// This is only able to represent a subset of `MIR`, +/// and should not leak any information about desugarings. +#[derive(Clone, Copy)] +pub struct AbstractConst<'tcx> { + // FIXME: Consider adding something like `IndexSlice` + // and use this here. + inner: &'tcx [Node<'tcx>], + substs: SubstsRef<'tcx>, +} + +impl AbstractConst<'tcx> { + pub fn new( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam<DefId>, + substs: SubstsRef<'tcx>, + ) -> Result<Option<AbstractConst<'tcx>>, ErrorReported> { + let inner = match (def.did.as_local(), def.const_param_did) { + (Some(did), Some(param_did)) => { + tcx.mir_abstract_const_of_const_arg((did, param_did))? + } + _ => tcx.mir_abstract_const(def.did)?, + }; + + Ok(inner.map(|inner| AbstractConst { inner, substs })) + } + + #[inline] + pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> { + AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs } + } + + #[inline] + pub fn root(self) -> Node<'tcx> { + self.inner.last().copied().unwrap() + } +} + +struct AbstractConstBuilder<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + /// The current WIP node tree. + nodes: IndexVec<NodeId, Node<'tcx>>, + locals: IndexVec<mir::Local, NodeId>, + /// We only allow field accesses if they access + /// the result of a checked operation. + checked_op_locals: BitSet<mir::Local>, +} + +impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { + fn error(&mut self, span: Option<Span>, msg: &str) -> Result<!, ErrorReported> { + self.tcx + .sess + .struct_span_err(self.body.span, "overly complex generic constant") + .span_label(span.unwrap_or(self.body.span), msg) + .help("consider moving this anonymous constant into a `const` function") + .emit(); + + Err(ErrorReported) + } + + fn new( + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorReported> { + let mut builder = AbstractConstBuilder { + tcx, + body, + nodes: IndexVec::new(), + locals: IndexVec::from_elem(NodeId::MAX, &body.local_decls), + checked_op_locals: BitSet::new_empty(body.local_decls.len()), + }; + + // We don't have to look at concrete constants, as we + // can just evaluate them. + if !body.is_polymorphic { + return Ok(None); + } + + // We only allow consts without control flow, so + // we check for cycles here which simplifies the + // rest of this implementation. + if body.is_cfg_cyclic() { + builder.error(None, "cyclic anonymous constants are forbidden")?; + } + + Ok(Some(builder)) + } + + fn place_to_local( + &mut self, + span: Span, + p: &mir::Place<'tcx>, + ) -> Result<mir::Local, ErrorReported> { + const ZERO_FIELD: mir::Field = mir::Field::from_usize(0); + // Do not allow any projections. + // + // One exception are field accesses on the result of checked operations, + // which are required to support things like `1 + 2`. + if let Some(p) = p.as_local() { + debug_assert!(!self.checked_op_locals.contains(p)); + Ok(p) + } else if let &[mir::ProjectionElem::Field(ZERO_FIELD, _)] = p.projection.as_ref() { + // Only allow field accesses if the given local + // contains the result of a checked operation. + if self.checked_op_locals.contains(p.local) { + Ok(p.local) + } else { + self.error(Some(span), "unsupported projection")?; + } + } else { + self.error(Some(span), "unsupported projection")?; + } + } + + fn operand_to_node( + &mut self, + span: Span, + op: &mir::Operand<'tcx>, + ) -> Result<NodeId, ErrorReported> { + debug!("operand_to_node: op={:?}", op); + match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => { + let local = self.place_to_local(span, p)?; + Ok(self.locals[local]) + } + mir::Operand::Constant(ct) => Ok(self.nodes.push(Node::Leaf(ct.literal))), + } + } + + /// We do not allow all binary operations in abstract consts, so filter disallowed ones. + fn check_binop(op: mir::BinOp) -> bool { + use mir::BinOp::*; + match op { + Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le + | Ne | Ge | Gt => true, + Offset => false, + } + } + + /// While we currently allow all unary operations, we still want to explicitly guard against + /// future changes here. + fn check_unop(op: mir::UnOp) -> bool { + use mir::UnOp::*; + match op { + Not | Neg => true, + } + } + + fn build_statement(&mut self, stmt: &mir::Statement<'tcx>) -> Result<(), ErrorReported> { + debug!("AbstractConstBuilder: stmt={:?}", stmt); + match stmt.kind { + StatementKind::Assign(box (ref place, ref rvalue)) => { + let local = self.place_to_local(stmt.source_info.span, place)?; + match *rvalue { + Rvalue::Use(ref operand) => { + self.locals[local] = + self.operand_to_node(stmt.source_info.span, operand)?; + Ok(()) + } + Rvalue::BinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => { + let lhs = self.operand_to_node(stmt.source_info.span, lhs)?; + let rhs = self.operand_to_node(stmt.source_info.span, rhs)?; + self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs)); + if op.is_checkable() { + bug!("unexpected unchecked checkable binary operation"); + } else { + Ok(()) + } + } + Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => { + let lhs = self.operand_to_node(stmt.source_info.span, lhs)?; + let rhs = self.operand_to_node(stmt.source_info.span, rhs)?; + self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs)); + self.checked_op_locals.insert(local); + Ok(()) + } + Rvalue::UnaryOp(op, ref operand) if Self::check_unop(op) => { + let operand = self.operand_to_node(stmt.source_info.span, operand)?; + self.locals[local] = self.nodes.push(Node::UnaryOp(op, operand)); + Ok(()) + } + _ => self.error(Some(stmt.source_info.span), "unsupported rvalue")?, + } + } + // These are not actually relevant for us here, so we can ignore them. + StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => Ok(()), + _ => self.error(Some(stmt.source_info.span), "unsupported statement")?, + } + } + + /// Possible return values: + /// + /// - `None`: unsupported terminator, stop building + /// - `Some(None)`: supported terminator, finish building + /// - `Some(Some(block))`: support terminator, build `block` next + fn build_terminator( + &mut self, + terminator: &mir::Terminator<'tcx>, + ) -> Result<Option<mir::BasicBlock>, ErrorReported> { + debug!("AbstractConstBuilder: terminator={:?}", terminator); + match terminator.kind { + TerminatorKind::Goto { target } => Ok(Some(target)), + TerminatorKind::Return => Ok(None), + TerminatorKind::Call { + ref func, + ref args, + destination: Some((ref place, target)), + // We do not care about `cleanup` here. Any branch which + // uses `cleanup` will fail const-eval and they therefore + // do not matter when checking for const evaluatability. + // + // Do note that even if `panic::catch_unwind` is made const, + // we still do not have to care about this, as we do not look + // into functions. + cleanup: _, + // Do not allow overloaded operators for now, + // we probably do want to allow this in the future. + // + // This is currently fairly irrelevant as it requires `const Trait`s. + from_hir_call: true, + fn_span, + } => { + let local = self.place_to_local(fn_span, place)?; + let func = self.operand_to_node(fn_span, func)?; + let args = self.tcx.arena.alloc_from_iter( + args.iter() + .map(|arg| self.operand_to_node(terminator.source_info.span, arg)) + .collect::<Result<Vec<NodeId>, _>>()?, + ); + self.locals[local] = self.nodes.push(Node::FunctionCall(func, args)); + Ok(Some(target)) + } + // We only allow asserts for checked operations. + // + // These asserts seem to all have the form `!_local.0` so + // we only allow exactly that. + TerminatorKind::Assert { ref cond, expected: false, target, .. } => { + let p = match cond { + mir::Operand::Copy(p) | mir::Operand::Move(p) => p, + mir::Operand::Constant(_) => bug!("unexpected assert"), + }; + + const ONE_FIELD: mir::Field = mir::Field::from_usize(1); + debug!("proj: {:?}", p.projection); + if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() { + // Only allow asserts checking the result of a checked operation. + if self.checked_op_locals.contains(p.local) { + return Ok(Some(target)); + } + } + + self.error(Some(terminator.source_info.span), "unsupported assertion")?; + } + _ => self.error(Some(terminator.source_info.span), "unsupported terminator")?, + } + } + + /// Builds the abstract const by walking the mir from start to finish + /// and bailing out when encountering an unsupported operation. + fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorReported> { + let mut block = &self.body.basic_blocks()[mir::START_BLOCK]; + // We checked for a cyclic cfg above, so this should terminate. + loop { + debug!("AbstractConstBuilder: block={:?}", block); + for stmt in block.statements.iter() { + self.build_statement(stmt)?; + } + + if let Some(next) = self.build_terminator(block.terminator())? { + block = &self.body.basic_blocks()[next]; + } else { + return Ok(self.tcx.arena.alloc_from_iter(self.nodes)); + } + } + } +} + +/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. +pub(super) fn mir_abstract_const<'tcx>( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam<LocalDefId>, +) -> Result<Option<&'tcx [mir::abstract_const::Node<'tcx>]>, ErrorReported> { + if tcx.features().const_evaluatable_checked { + match tcx.def_kind(def.did) { + // FIXME(const_evaluatable_checked): We currently only do this for anonymous constants, + // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether + // 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 => (), + _ => return Ok(None), + } + let body = tcx.mir_const(def).borrow(); + AbstractConstBuilder::new(tcx, &body)?.map(AbstractConstBuilder::build).transpose() + } else { + Ok(None) + } +} + +pub(super) fn try_unify_abstract_consts<'tcx>( + tcx: TyCtxt<'tcx>, + ((a, a_substs), (b, b_substs)): ( + (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>), + (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>), + ), +) -> bool { + (|| { + if let Some(a) = AbstractConst::new(tcx, a, a_substs)? { + if let Some(b) = AbstractConst::new(tcx, b, b_substs)? { + return Ok(try_unify(tcx, a, b)); + } + } + + Ok(false) + })() + .unwrap_or_else(|ErrorReported| true) + // FIXME(const_evaluatable_checked): We should instead have this + // method return the resulting `ty::Const` and return `ConstKind::Error` + // on `ErrorReported`. +} + +/// Tries to unify two abstract constants using structural equality. +pub(super) fn try_unify<'tcx>( + tcx: TyCtxt<'tcx>, + a: AbstractConst<'tcx>, + b: AbstractConst<'tcx>, +) -> bool { + match (a.root(), b.root()) { + (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); + match (a_ct.val, b_ct.val) { + // We can just unify errors with everything to reduce the amount of + // emitted errors here. + (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, + (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { + a_param == b_param + } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, + // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]` + // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This + // means that we only allow inference variables if they are equal. + (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, + // FIXME(const_evaluatable_checked): We may want to either actually try + // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // this, for now we just return false here. + _ => false, + } + } + (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { + try_unify(tcx, a.subtree(al), b.subtree(bl)) + && try_unify(tcx, a.subtree(ar), b.subtree(br)) + } + (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { + try_unify(tcx, a.subtree(av), b.subtree(bv)) + } + (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) + if a_args.len() == b_args.len() => + { + try_unify(tcx, a.subtree(a_f), b.subtree(b_f)) + && a_args + .iter() + .zip(b_args) + .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn))) + } + _ => 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 28542d4b12e..bda4351b2f2 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -382,7 +382,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! err.span_label(span, s.as_str()); - if !matches!(trait_ref.skip_binder().self_ty().kind, ty::Param(_)) { + if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point // at the type param with a label to suggest constraining it. @@ -446,12 +446,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.tcx.lang_items().fn_once_trait(), ] .contains(&Some(trait_ref.def_id())); - let is_target_feature_fn = - if let ty::FnDef(def_id, _) = trait_ref.skip_binder().self_ty().kind { - !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() - } else { - false - }; + let is_target_feature_fn = if let ty::FnDef(def_id, _) = + *trait_ref.skip_binder().self_ty().kind() + { + !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() + } else { + false + }; if is_fn_trait && is_target_feature_fn { err.note( "`#[target_feature]` functions do not implement the `Fn` traits", @@ -662,6 +663,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation ) } + + ty::PredicateAtom::TypeWellFormedFromEnv(..) => span_bug!( + span, + "TypeWellFormedFromEnv predicate should only exist in the environment" + ), } } @@ -678,7 +684,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { None => return, }; - let found_did = match found_trait_ty.kind { + let found_did = match *found_trait_ty.kind() { ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did), ty::Adt(def, _) => Some(def.did), _ => None, @@ -696,13 +702,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); - let found = match found_trait_ref.skip_binder().substs.type_at(1).kind { + let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() { ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], _ => vec![ArgKind::empty()], }; let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1); - let expected = match expected_ty.kind { + let expected = match expected_ty.kind() { ty::Tuple(ref tys) => tys .iter() .map(|t| ArgKind::from_expected_ty(t.expect_ty(), Some(span))) @@ -1252,7 +1258,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { /// returns the fuzzy category of a given type, or None /// if the type can be equated to any type. fn type_category(t: Ty<'_>) -> Option<u32> { - match t.kind { + match t.kind() { ty::Bool => Some(0), ty::Char => Some(1), ty::Str => Some(2), @@ -1281,7 +1287,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { } match (type_category(a), type_category(b)) { - (Some(cat_a), Some(cat_b)) => match (&a.kind, &b.kind) { + (Some(cat_a), Some(cat_b)) => match (a.kind(), b.kind()) { (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b, _ => cat_a == cat_b, }, @@ -1342,8 +1348,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { .normalize(candidate) .ok(); match normalized { - Some(normalized) => format!("\n {:?}", normalized.value), - None => format!("\n {:?}", candidate), + Some(normalized) => format!("\n {}", normalized.value), + None => format!("\n {}", candidate), } }) }; @@ -1476,7 +1482,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ty::PredicateAtom::Trait(data, _) => { let trait_ref = ty::Binder::bind(data.trait_ref); let self_ty = trait_ref.skip_binder().self_ty(); - debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind, trait_ref); + debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind(), trait_ref); if predicate.references_error() { return; @@ -1506,12 +1512,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { // avoid inundating the user with unnecessary errors, but we now // check upstream for type errors and don't add the obligations to // begin with in those cases. - if self - .tcx - .lang_items() - .sized_trait() - .map_or(false, |sized_id| sized_id == trait_ref.def_id()) - { + if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { self.need_type_info_err(body_id, span, self_ty, ErrorCode::E0282).emit(); return; } @@ -1664,7 +1665,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Param(ty::ParamTy { name, .. }) = ty.kind { + if let ty::Param(ty::ParamTy { name, .. }) = *ty.kind() { let infcx = self.infcx; self.var_map.entry(ty).or_insert_with(|| { infcx.next_ty_var(TypeVariableOrigin { @@ -1938,8 +1939,8 @@ impl ArgKind { /// Creates an `ArgKind` from the expected type of an /// argument. It has no name (`_`) and an optional source span. pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind { - match t.kind { - ty::Tuple(ref tys) => ArgKind::Tuple( + match t.kind() { + ty::Tuple(tys) => ArgKind::Tuple( span, tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(), ), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index d2b9f84af33..0f5aad5af12 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -194,7 +194,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { flags.push((sym::_Self, Some("{integral}".to_owned()))); } - if let ty::Array(aty, len) = self_ty.kind { + if let ty::Array(aty, len) = self_ty.kind() { flags.push((sym::_Self, Some("[]".to_owned()))); flags.push((sym::_Self, Some(format!("[{}]", aty)))); if let Some(def) = aty.ty_adt_def() { @@ -218,7 +218,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } } - if let ty::Dynamic(traits, _) = self_ty.kind { + if let ty::Dynamic(traits, _) = self_ty.kind() { for t in traits.skip_binder() { if let ty::ExistentialPredicate::Trait(trait_ref) = t { flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) 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 138293c9533..90a8d9634ae 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -207,7 +207,7 @@ fn suggest_restriction( } // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`... if let Some((bound_str, fn_sig)) = - fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind { + fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind() { // Shenanigans to get the `Trait` from the `impl Trait`. ty::Param(param) => { // `fn foo(t: impl Trait)` @@ -323,7 +323,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { body_id: hir::HirId, ) { let self_ty = trait_ref.skip_binder().self_ty(); - let (param_ty, projection) = match &self_ty.kind { + let (param_ty, projection) = match self_ty.kind() { ty::Param(_) => (true, None), ty::Projection(projection) => (false, Some(projection)), _ => return, @@ -482,8 +482,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { None => return, }; - if let ty::Ref(region, base_ty, mutbl) = real_ty.kind { - let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty); + if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { + let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span); if let Some(steps) = autoderef.find_map(|(ty, steps)| { // Re-add the `&` let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); @@ -563,7 +563,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { Some(ty) => ty, }; - let (def_id, output_ty, callable) = match self_ty.kind { + let (def_id, output_ty, callable) = match *self_ty.kind() { ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"), ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"), _ => return, @@ -751,7 +751,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }; for refs_remaining in 0..refs_number { - if let ty::Ref(_, inner_ty, _) = suggested_ty.kind { + if let ty::Ref(_, inner_ty, _) = suggested_ty.kind() { suggested_ty = inner_ty; let new_obligation = self.mk_trait_obligation_with_new_self_ty( @@ -814,7 +814,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind { + if let ty::Ref(region, t_type, mutability) = *trait_ref.skip_binder().self_ty().kind() { if region.is_late_bound() || t_type.has_escaping_bound_vars() { // Avoid debug assertion in `mk_obligation_for_def_id`. // @@ -871,7 +871,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_ref: &ty::Binder<ty::TraitRef<'tcx>>, ) { let is_empty_tuple = - |ty: ty::Binder<Ty<'_>>| ty.skip_binder().kind == ty::Tuple(ty::List::empty()); + |ty: ty::Binder<Ty<'_>>| *ty.skip_binder().kind() == ty::Tuple(ty::List::empty()); let hir = self.tcx.hir(); let parent_node = hir.get_parent_node(obligation.cause.body_id); @@ -941,7 +941,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let body = hir.body(*body_id); let trait_ref = self.resolve_vars_if_possible(trait_ref); let ty = trait_ref.skip_binder().self_ty(); - let is_object_safe = match ty.kind { + let is_object_safe = match ty.kind() { ty::Dynamic(predicates, _) => { // If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`. predicates @@ -982,11 +982,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ty| { let ty = self.resolve_vars_if_possible(&ty); same &= - !matches!(ty.kind, ty::Error(_)) + !matches!(ty.kind(), ty::Error(_)) && last_ty.map_or(true, |last_ty| { // FIXME: ideally we would use `can_coerce` here instead, but `typeck` comes // *after* in the dependency graph. - match (&ty.kind, &last_ty.kind) { + match (ty.kind(), last_ty.kind()) { (Infer(InferTy::IntVar(_)), Infer(InferTy::IntVar(_))) | (Infer(InferTy::FloatVar(_)), Infer(InferTy::FloatVar(_))) | (Infer(InferTy::FreshIntTy(_)), Infer(InferTy::FreshIntTy(_))) @@ -997,12 +997,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => ty == last_ty, } }); - (Some(ty), same, only_never_return && matches!(ty.kind, ty::Never)) + (Some(ty), same, only_never_return && matches!(ty.kind(), ty::Never)) }, ); let all_returns_conform_to_trait = if let Some(ty_ret_ty) = typeck_results.node_type_opt(ret_ty.hir_id) { - match ty_ret_ty.kind { + match ty_ret_ty.kind() { ty::Dynamic(predicates, _) => { let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); let param_env = ty::ParamEnv::empty(); @@ -1151,7 +1151,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_ref: ty::TraitRef<'tcx>, ) -> String { let inputs = trait_ref.substs.type_at(1); - let sig = if let ty::Tuple(inputs) = inputs.kind { + let sig = if let ty::Tuple(inputs) = inputs.kind() { tcx.mk_fn_sig( inputs.iter().map(|k| k.expect_ty()), tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), @@ -1317,10 +1317,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { debug!( "maybe_note_obligation_cause_for_async_await: \ parent_trait_ref={:?} self_ty.kind={:?}", - derived_obligation.parent_trait_ref, ty.kind + derived_obligation.parent_trait_ref, + ty.kind() ); - match ty.kind { + match *ty.kind() { ty::Generator(did, ..) => { generator = generator.or(Some(did)); outer_generator = Some(did); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a5c6dc042ab..7e5be8276f7 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::ToPredicate; use rustc_middle::ty::{self, Binder, Const, Ty, TypeFoldable}; use std::marker::PhantomData; +use super::const_evaluatable; use super::project; use super::select::SelectionContext; use super::wf; @@ -304,8 +305,34 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { return ProcessResult::Unchanged; } - // This part of the code is much colder. + self.progress_changed_obligations(pending_obligation) + } + fn process_backedge<'c, I>( + &mut self, + cycle: I, + _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, + ) where + I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>, + { + if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { + debug!("process_child_obligations: coinductive match"); + } else { + let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); + self.selcx.infcx().report_overflow_error_cycle(&cycle); + } + } +} + +impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { + // The code calling this method is extremely hot and only rarely + // actually uses this, so move this part of the code + // out of that loop. + #[inline(never)] + fn progress_changed_obligations( + &mut self, + pending_obligation: &mut PendingPredicateObligation<'tcx>, + ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { pending_obligation.stalled_on.truncate(0); let obligation = &mut pending_obligation.obligation; @@ -354,6 +381,9 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { obligation.with(pred.to_predicate(self.selcx.tcx())), ])) } + ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + bug!("TypeWellFormedFromEnv is only used for Chalk") + } }, &ty::PredicateKind::Atom(atom) => match atom { ty::PredicateAtom::Trait(ref data, _) => { @@ -458,20 +488,39 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { - match self.selcx.infcx().const_eval_resolve( - obligation.param_env, + match const_evaluatable::is_const_evaluatable( + self.selcx.infcx(), def_id, substs, - None, - Some(obligation.cause.span), + obligation.param_env, + obligation.cause.span, ) { - Ok(_) => ProcessResult::Changed(vec![]), - Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))), + Ok(()) => ProcessResult::Changed(vec![]), + Err(e) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))), } } ty::PredicateAtom::ConstEquate(c1, c2) => { debug!("equating consts: c1={:?} c2={:?}", c1, c2); + if self.selcx.tcx().features().const_evaluatable_checked { + // FIXME: we probably should only try to unify abstract constants + // if the constants depend on generic parameters. + // + // Let's just see where this breaks :shrug: + if let ( + ty::ConstKind::Unevaluated(a_def, a_substs, None), + ty::ConstKind::Unevaluated(b_def, b_substs, None), + ) = (c1.val, c2.val) + { + if self + .selcx + .tcx() + .try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs))) + { + return ProcessResult::Changed(vec![]); + } + } + } let stalled_on = &mut pending_obligation.stalled_on; @@ -535,27 +584,13 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } } } + ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + bug!("TypeWellFormedFromEnv is only used for Chalk") + } }, } } - fn process_backedge<'c, I>( - &mut self, - cycle: I, - _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, - ) where - I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>, - { - if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { - debug!("process_child_obligations: coinductive match"); - } else { - let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); - self.selcx.infcx().report_overflow_error_cycle(&cycle); - } - } -} - -impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { fn process_trait_obligation( &mut self, obligation: &PredicateObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 61567aeb57c..e23f5a583b2 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -23,7 +23,7 @@ pub fn can_type_implement_copy( ) -> Result<(), CopyImplementationError<'tcx>> { // FIXME: (@jroesch) float this code up tcx.infer_ctxt().enter(|infcx| { - let (adt, substs) = match self_type.kind { + let (adt, substs) = match self_type.kind() { // These types used to have a builtin impl. // Now libcore provides that impl. ty::Uint(_) diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index fe406e88c52..79495ba7f9b 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -7,6 +7,7 @@ pub mod auto_trait; mod chalk_fulfill; pub mod codegen; mod coherence; +mod const_evaluatable; mod engine; pub mod error_reporting; mod fulfill; @@ -301,11 +302,8 @@ pub fn normalize_param_env_or_error<'tcx>( debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); - let elaborated_env = ty::ParamEnv::new( - tcx.intern_predicates(&predicates), - unnormalized_env.reveal(), - unnormalized_env.def_id, - ); + let elaborated_env = + ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal()); // HACK: we are trying to normalize the param-env inside *itself*. The problem is that // normalization expects its param-env to be already normalized, which means we have @@ -359,7 +357,7 @@ pub fn normalize_param_env_or_error<'tcx>( let outlives_env: Vec<_> = non_outlives_predicates.iter().chain(&outlives_predicates).cloned().collect(); let outlives_env = - ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal(), None); + ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal()); let outlives_predicates = match do_normalize_predicates( tcx, region_context, @@ -379,11 +377,7 @@ pub fn normalize_param_env_or_error<'tcx>( let mut predicates = non_outlives_predicates; predicates.extend(outlives_predicates); debug!("normalize_param_env_or_error: final predicates={:?}", predicates); - ty::ParamEnv::new( - tcx.intern_predicates(&predicates), - unnormalized_env.reveal(), - unnormalized_env.def_id, - ) + ty::ParamEnv::new(tcx.intern_predicates(&predicates), unnormalized_env.reveal()) } pub fn fully_normalize<'a, 'tcx, T>( @@ -558,6 +552,21 @@ pub fn provide(providers: &mut ty::query::Providers) { vtable_methods, type_implements_trait, subst_and_check_impossible_predicates, + mir_abstract_const: |tcx, def_id| { + let def_id = def_id.expect_local(); + if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { + tcx.mir_abstract_const_of_const_arg(def) + } else { + const_evaluatable::mir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id)) + } + }, + mir_abstract_const_of_const_arg: |tcx, (did, param_did)| { + const_evaluatable::mir_abstract_const( + tcx, + ty::WithOptConstParam { did, const_param_did: Some(param_did) }, + ) + }, + try_unify_abstract_consts: const_evaluatable::try_unify_abstract_consts, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index c003e4f8068..2f2ac9f094d 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -276,7 +276,8 @@ fn predicates_reference_self( | ty::PredicateAtom::ClosureKind(..) | ty::PredicateAtom::Subtype(..) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => None, + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, } }) .collect() @@ -310,7 +311,8 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | ty::PredicateAtom::ClosureKind(..) | ty::PredicateAtom::TypeOutlives(..) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => false, + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => false, } }) } @@ -654,11 +656,7 @@ fn receiver_is_dispatchable<'tcx>( .chain(iter::once(trait_predicate)) .collect(); - ty::ParamEnv::new( - tcx.intern_predicates(&caller_bounds), - param_env.reveal(), - param_env.def_id, - ) + ty::ParamEnv::new(tcx.intern_predicates(&caller_bounds), param_env.reveal()) }; // Receiver: DispatchFromDyn<Receiver[Self => U]> @@ -732,7 +730,7 @@ fn contains_illegal_self_type_reference<'tcx>( impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - match t.kind { + match t.kind() { ty::Param(_) => t == self.self_ty, ty::Projection(ref data) => { // This is a projected type `<Foo as SomeTrait>::X`. diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index c788e4f5c90..d37f819f376 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -345,7 +345,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { // should occur eventually). let ty = ty.super_fold_with(self); - match ty.kind { + match *ty.kind() { ty::Opaque(def_id, substs) => { // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal() { @@ -908,7 +908,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( let tcx = selcx.tcx(); // Check whether the self-type is itself a projection. // If so, extract what we know from the trait and try to come up with a good answer. - let bounds = match obligation_trait_ref.self_ty().kind { + let bounds = match *obligation_trait_ref.self_ty().kind() { ty::Projection(ref data) => { tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) } @@ -1067,7 +1067,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // Any type with multiple potential discriminant types is therefore not eligible. let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); - match self_ty.kind { + match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -1219,8 +1219,8 @@ fn confirm_object_candidate<'cx, 'tcx>( let self_ty = obligation_trait_ref.self_ty(); let object_ty = selcx.infcx().shallow_resolve(self_ty); debug!("confirm_object_candidate(object_ty={:?})", object_ty); - let data = match object_ty.kind { - ty::Dynamic(ref data, ..) => data, + let data = match object_ty.kind() { + ty::Dynamic(data, ..) => data, _ => span_bug!( obligation.cause.span, "confirm_object_candidate called with non-object: {:?}", diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index d07c95270e0..424b3bd67ff 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -82,7 +82,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { /// Note also that `needs_drop` requires a "global" type (i.e., one /// with erased regions), but this function does not. pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind { + match ty.kind() { // None of these types have a destructor and hence they do not // require anything in particular to outlive the dtor's // execution. diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 93652329305..3dcebbcc244 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -7,6 +7,7 @@ use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; use crate::traits::error_reporting::InferCtxtExt; use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; +use rustc_data_structures::mini_map::MiniMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::Normalized; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; @@ -57,6 +58,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { param_env: self.param_env, obligations: vec![], error: false, + cache: MiniMap::new(), anon_depth: 0, }; @@ -85,6 +87,7 @@ struct QueryNormalizer<'cx, 'tcx> { cause: &'cx ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, obligations: Vec<PredicateObligation<'tcx>>, + cache: MiniMap<Ty<'tcx>, Ty<'tcx>>, error: bool, anon_depth: usize, } @@ -99,8 +102,12 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { return ty; } + if let Some(ty) = self.cache.get(&ty) { + return ty; + } + let ty = ty.super_fold_with(self); - match ty.kind { + let res = (|| match *ty.kind() { ty::Opaque(def_id, substs) => { // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal() { @@ -197,7 +204,9 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { } _ => ty, - } + })(); + self.cache.insert(ty, res); + res } fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'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 1d5441b8eff..a4943231dfd 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -152,7 +152,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Before we go into the whole placeholder thing, just // quickly check if the self-type is a projection at all. - match obligation.predicate.skip_binder().trait_ref.self_ty().kind { + match obligation.predicate.skip_binder().trait_ref.self_ty().kind() { ty::Projection(_) | ty::Opaque(..) => {} ty::Infer(ty::TyVar(_)) => { span_bug!( @@ -221,7 +221,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // touch bound regions, they just capture the in-scope // type/region parameters. let self_ty = obligation.self_ty().skip_binder(); - match self_ty.kind { + match self_ty.kind() { ty::Generator(..) => { debug!( "assemble_generator_candidates: self_ty={:?} obligation={:?}", @@ -261,7 +261,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Okay to skip binder because the substs on closure types never // touch bound regions, they just capture the in-scope // type/region parameters - match obligation.self_ty().skip_binder().kind { + match *obligation.self_ty().skip_binder().kind() { ty::Closure(_, closure_substs) => { debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation); match self.infcx.closure_kind(closure_substs) { @@ -300,7 +300,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Okay to skip binder because what we are inspecting doesn't involve bound regions. let self_ty = obligation.self_ty().skip_binder(); - match self_ty.kind { + match *self_ty.kind() { ty::Infer(ty::TyVar(_)) => { debug!("assemble_fn_pointer_candidates: ambiguous self-type"); candidates.ambiguous = true; // Could wind up being a fn() type. @@ -382,7 +382,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let def_id = obligation.predicate.def_id(); if self.tcx().trait_is_auto(def_id) { - match self_ty.kind { + match self_ty.kind() { ty::Dynamic(..) => { // For object types, we don't know what the closed // over types are. This means we conservatively @@ -453,7 +453,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // self-ty here doesn't escape this probe, so just erase // any LBR. let self_ty = self.tcx().erase_late_bound_regions(&obligation.self_ty()); - let poly_trait_ref = match self_ty.kind { + let poly_trait_ref = match self_ty.kind() { ty::Dynamic(ref data, ..) => { if data.auto_traits().any(|did| did == obligation.predicate.def_id()) { debug!( @@ -539,7 +539,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target); - let may_apply = match (&source.kind, &target.kind) { + let may_apply = match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { // Upcasts permit two things: diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 3d6eb845136..9906c1f325f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -327,8 +327,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // from the object. Have to try to make a broken test case that // results. let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - let poly_trait_ref = match self_ty.kind { - ty::Dynamic(ref data, ..) => data + let poly_trait_ref = match self_ty.kind() { + ty::Dynamic(data, ..) => data .principal() .unwrap_or_else(|| { span_bug!(obligation.cause.span, "object candidate with no principal") @@ -449,7 +449,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // touch bound regions, they just capture the in-scope // type/region parameters. let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - let (generator_def_id, substs) = match self_ty.kind { + let (generator_def_id, substs) = match *self_ty.kind() { ty::Generator(id, substs, _) => (id, substs), _ => bug!("closure candidate for non-closure {:?}", obligation), }; @@ -498,7 +498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // touch bound regions, they just capture the in-scope // type/region parameters. let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - let (closure_def_id, substs) = match self_ty.kind { + let (closure_def_id, substs) = match *self_ty.kind() { ty::Closure(id, substs) => (id, substs), _ => bug!("closure candidate for non-closure {:?}", obligation), }; @@ -594,7 +594,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})", source, target); let mut nested = vec![]; - match (&source.kind, &target.kind) { + match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { // See `assemble_candidates_for_unsizing` for more info. @@ -693,7 +693,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `Struct<T>` -> `Struct<U>` (&ty::Adt(def, substs_a), &ty::Adt(_, substs_b)) => { let maybe_unsizing_param_idx = |arg: GenericArg<'tcx>| match arg.unpack() { - GenericArgKind::Type(ty) => match ty.kind { + GenericArgKind::Type(ty) => match ty.kind() { ty::Param(p) => Some(p.index), _ => None, }, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 82f476b463d..5e2f7d81d00 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -6,6 +6,7 @@ use self::EvaluationResult::*; use self::SelectionCandidate::*; use super::coherence::{self, Conflict}; +use super::const_evaluatable; use super::project; use super::project::normalize_with_depth_to; use super::util; @@ -32,6 +33,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::fast_reject; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef}; use rustc_middle::ty::{ @@ -448,150 +450,167 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { None => self.check_recursion_limit(&obligation, &obligation)?, } - match obligation.predicate.skip_binders() { - ty::PredicateAtom::Trait(t, _) => { - let t = ty::Binder::bind(t); - debug_assert!(!t.has_escaping_bound_vars()); - let obligation = obligation.with(t); - self.evaluate_trait_predicate_recursively(previous_stack, obligation) - } + ensure_sufficient_stack(|| { + match obligation.predicate.skip_binders() { + ty::PredicateAtom::Trait(t, _) => { + let t = ty::Binder::bind(t); + debug_assert!(!t.has_escaping_bound_vars()); + let obligation = obligation.with(t); + self.evaluate_trait_predicate_recursively(previous_stack, obligation) + } + + ty::PredicateAtom::Subtype(p) => { + let p = ty::Binder::bind(p); + // Does this code ever run? + match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { + Some(Ok(InferOk { mut obligations, .. })) => { + self.add_depth(obligations.iter_mut(), obligation.recursion_depth); + self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ) + } + Some(Err(_)) => Ok(EvaluatedToErr), + None => Ok(EvaluatedToAmbig), + } + } - ty::PredicateAtom::Subtype(p) => { - let p = ty::Binder::bind(p); - // Does this code ever run? - match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { - Some(Ok(InferOk { mut obligations, .. })) => { + ty::PredicateAtom::WellFormed(arg) => match wf::obligations( + self.infcx, + obligation.param_env, + obligation.cause.body_id, + arg, + obligation.cause.span, + ) { + Some(mut obligations) => { self.add_depth(obligations.iter_mut(), obligation.recursion_depth); self.evaluate_predicates_recursively( previous_stack, obligations.into_iter(), ) } - Some(Err(_)) => Ok(EvaluatedToErr), None => Ok(EvaluatedToAmbig), - } - } + }, - ty::PredicateAtom::WellFormed(arg) => match wf::obligations( - self.infcx, - obligation.param_env, - obligation.cause.body_id, - arg, - obligation.cause.span, - ) { - Some(mut obligations) => { - self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively(previous_stack, obligations.into_iter()) + ty::PredicateAtom::TypeOutlives(..) | ty::PredicateAtom::RegionOutlives(..) => { + // We do not consider region relationships when evaluating trait matches. + Ok(EvaluatedToOkModuloRegions) } - None => Ok(EvaluatedToAmbig), - }, - - ty::PredicateAtom::TypeOutlives(..) | ty::PredicateAtom::RegionOutlives(..) => { - // We do not consider region relationships when evaluating trait matches. - Ok(EvaluatedToOkModuloRegions) - } - ty::PredicateAtom::ObjectSafe(trait_def_id) => { - if self.tcx().is_object_safe(trait_def_id) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) + ty::PredicateAtom::ObjectSafe(trait_def_id) => { + if self.tcx().is_object_safe(trait_def_id) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToErr) + } } - } - ty::PredicateAtom::Projection(data) => { - let data = ty::Binder::bind(data); - let project_obligation = obligation.with(data); - match project::poly_project_and_unify_type(self, &project_obligation) { - Ok(Ok(Some(mut subobligations))) => { - self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); - let result = self.evaluate_predicates_recursively( - previous_stack, - subobligations.into_iter(), - ); - if let Some(key) = - ProjectionCacheKey::from_poly_projection_predicate(self, data) - { - self.infcx.inner.borrow_mut().projection_cache().complete(key); + ty::PredicateAtom::Projection(data) => { + let data = ty::Binder::bind(data); + let project_obligation = obligation.with(data); + match project::poly_project_and_unify_type(self, &project_obligation) { + Ok(Ok(Some(mut subobligations))) => { + self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); + let result = self.evaluate_predicates_recursively( + previous_stack, + subobligations.into_iter(), + ); + if let Some(key) = + ProjectionCacheKey::from_poly_projection_predicate(self, data) + { + self.infcx.inner.borrow_mut().projection_cache().complete(key); + } + result } - result + Ok(Ok(None)) => Ok(EvaluatedToAmbig), + // EvaluatedToRecur might also be acceptable here, but use + // Unknown for now because it means that we won't dismiss a + // selection candidate solely because it has a projection + // cycle. This is closest to the previous behavior of + // immediately erroring. + Ok(Err(project::InProgress)) => Ok(EvaluatedToUnknown), + Err(_) => Ok(EvaluatedToErr), } - Ok(Ok(None)) => Ok(EvaluatedToAmbig), - // EvaluatedToRecur might also be acceptable here, but use - // Unknown for now because it means that we won't dismiss a - // selection candidate solely because it has a projection - // cycle. This is closest to the previous behavior of - // immediately erroring. - Ok(Err(project::InProgress)) => Ok(EvaluatedToUnknown), - Err(_) => Ok(EvaluatedToErr), } - } - ty::PredicateAtom::ClosureKind(_, closure_substs, kind) => { - match self.infcx.closure_kind(closure_substs) { - Some(closure_kind) => { - if closure_kind.extends(kind) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) + ty::PredicateAtom::ClosureKind(_, closure_substs, kind) => { + match self.infcx.closure_kind(closure_substs) { + Some(closure_kind) => { + if closure_kind.extends(kind) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToErr) + } } + None => Ok(EvaluatedToAmbig), } - None => Ok(EvaluatedToAmbig), } - } - ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { - match self.tcx().const_eval_resolve( - obligation.param_env, - def_id, - substs, - None, - None, - ) { - Ok(_) => Ok(EvaluatedToOk), - Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig), - Err(_) => Ok(EvaluatedToErr), + ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { + match const_evaluatable::is_const_evaluatable( + self.infcx, + def_id, + substs, + obligation.param_env, + obligation.cause.span, + ) { + Ok(()) => Ok(EvaluatedToOk), + Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig), + Err(_) => Ok(EvaluatedToErr), + } } - } - ty::PredicateAtom::ConstEquate(c1, c2) => { - debug!("evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", c1, c2); - - let evaluate = |c: &'tcx ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { - self.infcx - .const_eval_resolve( - obligation.param_env, - def, - substs, - promoted, - Some(obligation.cause.span), - ) - .map(|val| ty::Const::from_value(self.tcx(), val, c.ty)) - } else { - Ok(c) - } - }; + ty::PredicateAtom::ConstEquate(c1, c2) => { + debug!( + "evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", + c1, c2 + ); - match (evaluate(c1), evaluate(c2)) { - (Ok(c1), Ok(c2)) => { - match self.infcx().at(&obligation.cause, obligation.param_env).eq(c1, c2) { - Ok(_) => Ok(EvaluatedToOk), - Err(_) => Ok(EvaluatedToErr), + let evaluate = |c: &'tcx ty::Const<'tcx>| { + if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { + self.infcx + .const_eval_resolve( + obligation.param_env, + def, + substs, + promoted, + Some(obligation.cause.span), + ) + .map(|val| ty::Const::from_value(self.tcx(), val, c.ty)) + } else { + Ok(c) + } + }; + + match (evaluate(c1), evaluate(c2)) { + (Ok(c1), Ok(c2)) => { + match self + .infcx() + .at(&obligation.cause, obligation.param_env) + .eq(c1, c2) + { + Ok(_) => Ok(EvaluatedToOk), + Err(_) => Ok(EvaluatedToErr), + } + } + (Err(ErrorHandled::Reported(ErrorReported)), _) + | (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr), + (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => { + span_bug!( + obligation.cause.span(self.tcx()), + "ConstEquate: const_eval_resolve returned an unexpected error" + ) + } + (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { + Ok(EvaluatedToAmbig) } } - (Err(ErrorHandled::Reported(ErrorReported)), _) - | (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr), - (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!( - obligation.cause.span(self.tcx()), - "ConstEquate: const_eval_resolve returned an unexpected error" - ), - (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { - Ok(EvaluatedToAmbig) - } + } + ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + bug!("TypeWellFormedFromEnv is only used for chalk") } } - } + }) } fn evaluate_trait_predicate_recursively<'o>( @@ -778,14 +797,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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 = 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 - }, - }; + 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!("evaluate_stack: pushing cause = {:?}", cause); self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); } @@ -1030,12 +1051,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if !candidate_set.ambiguous && no_candidates_apply { let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; let self_ty = trait_ref.self_ty(); - let trait_desc = trait_ref.print_only_trait_path().to_string(); - let self_desc = if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }; + let (trait_desc, self_desc) = with_no_trimmed_paths(|| { + let trait_desc = trait_ref.print_only_trait_path().to_string(); + let self_desc = if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }; + (trait_desc, self_desc) + }); let cause = if let Conflict::Upstream = conflict { IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } } else { @@ -1301,7 +1325,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); let tcx = self.infcx.tcx; - let predicates = match placeholder_trait_predicate.trait_ref.self_ty().kind { + let predicates = match *placeholder_trait_predicate.trait_ref.self_ty().kind() { ty::Projection(ref data) => { tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) } @@ -1560,7 +1584,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // NOTE: binder moved to (*) let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - match self_ty.kind { + match self_ty.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) @@ -1615,7 +1639,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { use self::BuiltinImplConditions::{Ambiguous, None, Where}; - match self_ty.kind { + match self_ty.kind() { ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::FnDef(..) @@ -1689,7 +1713,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32] /// ``` fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec<Ty<'tcx>> { - match t.kind { + match *t.kind() { ty::Uint(_) | ty::Int(_) | ty::Bool diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 56b8354d68c..c8bcab6efd7 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -3,6 +3,7 @@ use super::OverlapError; use crate::traits; use rustc_hir::def_id::DefId; use rustc_middle::ty::fast_reject::{self, SimplifiedType}; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; pub use rustc_middle::traits::specialization_graph::*; @@ -102,7 +103,8 @@ impl ChildrenExt for Children { let trait_ref = overlap.impl_header.trait_ref.unwrap(); let self_ty = trait_ref.self_ty(); - OverlapError { + // FIXME: should postpone string formatting until we decide to actually emit. + with_no_trimmed_paths(|| OverlapError { with_impl: possible_sibling, trait_desc: trait_ref.print_only_trait_path().to_string(), // Only report the `Self` type if it has at least @@ -115,7 +117,7 @@ impl ChildrenExt for Children { }, intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, involves_placeholder: overlap.involves_placeholder, - } + }) }; let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>, diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 78186a5e8a5..4f7fa2c3988 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -137,7 +137,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { debug!("Search visiting ty: {:?}", ty); - let (adt_def, substs) = match ty.kind { + let (adt_def, substs) = match *ty.kind() { ty::Adt(adt_def, substs) => (adt_def, substs), ty::Param(_) => { self.found = Some(NonStructuralMatchTy::Param); diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 0ac3c6ffe62..909cd2aa155 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -7,8 +7,9 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_span::Span; -use std::rc::Rc; +use std::iter; +use std::rc::Rc; /// Returns the set of obligations needed to make `arg` well-formed. /// If `arg` contains unresolved inference variables, this may include /// further WF obligations. However, if `arg` IS an unresolved @@ -25,7 +26,7 @@ pub fn obligations<'a, 'tcx>( // Handle the "livelock" case (see comment above) by bailing out if necessary. let arg = match arg.unpack() { GenericArgKind::Type(ty) => { - match ty.kind { + match ty.kind() { ty::Infer(ty::TyVar(_)) => { let resolved_ty = infcx.shallow_resolve(ty); if resolved_ty == ty { @@ -127,6 +128,9 @@ pub fn predicate_obligations<'a, 'tcx>( wf.compute(c1.into()); wf.compute(c2.into()); } + ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + bug!("TypeWellFormedFromEnv is only used for Chalk") + } } wf.normalize() @@ -200,7 +204,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( // projection coming from another associated type. See // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs` and // `traits-assoc-type-in-supertrait-bad.rs`. - if let ty::Projection(projection_ty) = proj.ty.kind { + if let ty::Projection(projection_ty) = proj.ty.kind() { let trait_assoc_item = tcx.associated_item(projection_ty.item_def_id); if let Some(impl_item_span) = items.iter().find(|item| item.ident == trait_assoc_item.ident).map(fix_span) @@ -213,7 +217,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( // An associated item obligation born out of the `trait` failed to be met. An example // can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`. debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred); - if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = pred.self_ty().kind { + if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = *pred.self_ty().kind() { if let Some(impl_item_span) = trait_assoc_items .find(|i| i.def_id == item_def_id) .and_then(|trait_assoc_item| { @@ -412,7 +416,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } }; - match ty.kind { + match *ty.kind() { ty::Bool | ty::Char | ty::Int(..) @@ -590,7 +594,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // prevention, which happens before this can be reached. ty::Infer(_) => { let ty = self.infcx.shallow_resolve(ty); - if let ty::Infer(ty::TyVar(_)) = ty.kind { + if let ty::Infer(ty::TyVar(_)) = ty.kind() { // Not yet resolved, but we've made progress. let cause = self.cause(traits::MiscObligation); self.out.push(traits::Obligation::new( @@ -613,13 +617,24 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { def_id: DefId, substs: SubstsRef<'tcx>, ) -> Vec<traits::PredicateObligation<'tcx>> { - let predicates = self.infcx.tcx.predicates_of(def_id).instantiate(self.infcx.tcx, substs); + let predicates = self.infcx.tcx.predicates_of(def_id); + let mut origins = vec![def_id; predicates.predicates.len()]; + let mut head = predicates; + while let Some(parent) = head.parent { + head = self.infcx.tcx.predicates_of(parent); + origins.extend(iter::repeat(parent).take(head.predicates.len())); + } + + let predicates = predicates.instantiate(self.infcx.tcx, substs); + debug_assert_eq!(predicates.predicates.len(), origins.len()); + predicates .predicates .into_iter() .zip(predicates.spans.into_iter()) - .map(|(pred, span)| { - let cause = self.cause(traits::BindingObligation(def_id, span)); + .zip(origins.into_iter().rev()) + .map(|((pred, span), origin_def_id)| { + let cause = self.cause(traits::BindingObligation(origin_def_id, span)); traits::Obligation::new(cause, self.param_env, pred) }) .filter(|pred| !pred.has_escaping_bound_vars()) diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml index 2d63fc51220..369d003eb22 100644 --- a/compiler/rustc_traits/Cargo.toml +++ b/compiler/rustc_traits/Cargo.toml @@ -12,8 +12,9 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -chalk-ir = "0.14.0" -chalk-solve = "0.14.0" +chalk-ir = "0.28.0" +chalk-solve = "0.28.0" +chalk-engine = "0.28.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_infer = { path = "../rustc_infer" } rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index 4c8be8eb610..828ee6dea62 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -8,7 +8,7 @@ use rustc_middle::traits::ChalkRustInterner as RustInterner; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, AssocItemContainer, AssocKind, TyCtxt}; +use rustc_middle::ty::{self, AssocItemContainer, AssocKind, TyCtxt, TypeFoldable}; use rustc_hir::def_id::DefId; @@ -17,11 +17,13 @@ use rustc_span::symbol::sym; use std::fmt; use std::sync::Arc; -use crate::chalk::lowering::LowerInto; +use crate::chalk::lowering::{self, LowerInto}; +use rustc_ast::ast; pub struct RustIrDatabase<'tcx> { - pub tcx: TyCtxt<'tcx>, - pub interner: RustInterner<'tcx>, + pub(crate) interner: RustInterner<'tcx>, + pub(crate) restatic_placeholder: ty::Region<'tcx>, + pub(crate) reempty_placeholder: ty::Region<'tcx>, } impl fmt::Debug for RustIrDatabase<'_> { @@ -30,6 +32,26 @@ impl fmt::Debug for RustIrDatabase<'_> { } } +impl<'tcx> RustIrDatabase<'tcx> { + fn where_clauses_for( + &self, + def_id: DefId, + bound_vars: SubstsRef<'tcx>, + ) -> Vec<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> { + let predicates = self.interner.tcx.predicates_of(def_id).predicates; + let mut regions_substitutor = lowering::RegionsSubstitutor::new( + self.interner.tcx, + self.restatic_placeholder, + self.reempty_placeholder, + ); + predicates + .iter() + .map(|(wc, _)| wc.subst(self.interner.tcx, bound_vars)) + .map(|wc| wc.fold_with(&mut regions_substitutor)) + .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect() + } +} + impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'tcx> { fn interner(&self) -> &RustInterner<'tcx> { &self.interner @@ -40,7 +62,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t assoc_type_id: chalk_ir::AssocTypeId<RustInterner<'tcx>>, ) -> Arc<chalk_solve::rust_ir::AssociatedTyDatum<RustInterner<'tcx>>> { let def_id = assoc_type_id.0; - let assoc_item = self.tcx.associated_item(def_id); + let assoc_item = self.interner.tcx.associated_item(def_id); let trait_def_id = match assoc_item.container { AssocItemContainer::TraitContainer(def_id) => def_id, _ => unimplemented!("Not possible??"), @@ -49,16 +71,12 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t AssocKind::Type => {} _ => unimplemented!("Not possible??"), } - let bound_vars = bound_vars_for_item(self.tcx, def_id); + let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(&self.interner, bound_vars); // FIXME(chalk): this really isn't right I don't think. The functions // for GATs are a bit hard to figure out. Are these supposed to be where // clauses or bounds? - let predicates = self.tcx.predicates_defined_on(def_id).predicates; - let where_clauses: Vec<_> = predicates - .iter() - .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) - .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect(); + let where_clauses = self.where_clauses_for(def_id, bound_vars); Arc::new(chalk_solve::rust_ir::AssociatedTyDatum { trait_id: chalk_ir::TraitId(trait_def_id), @@ -76,16 +94,15 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t trait_id: chalk_ir::TraitId<RustInterner<'tcx>>, ) -> Arc<chalk_solve::rust_ir::TraitDatum<RustInterner<'tcx>>> { let def_id = trait_id.0; - let trait_def = self.tcx.trait_def(def_id); + let trait_def = self.interner.tcx.trait_def(def_id); - let bound_vars = bound_vars_for_item(self.tcx, def_id); + let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(&self.interner, bound_vars); - let predicates = self.tcx.predicates_defined_on(def_id).predicates; - let where_clauses: Vec<_> = predicates - .iter() - .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) - .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect(); + + let where_clauses = self.where_clauses_for(def_id, bound_vars); + let associated_ty_ids: Vec<_> = self + .interner .tcx .associated_items(def_id) .in_definition_order() @@ -93,24 +110,37 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t .map(|i| chalk_ir::AssocTypeId(i.def_id)) .collect(); - let well_known = - if self.tcx.lang_items().sized_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::Sized) - } else if self.tcx.lang_items().copy_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::Copy) - } else if self.tcx.lang_items().clone_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::Clone) - } else if self.tcx.lang_items().drop_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::Drop) - } else if self.tcx.lang_items().fn_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::Fn) - } else if self.tcx.lang_items().fn_once_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::FnOnce) - } else if self.tcx.lang_items().fn_mut_trait().map(|t| def_id == t).unwrap_or(false) { - Some(chalk_solve::rust_ir::WellKnownTrait::FnMut) - } else { - None - }; + let well_known = if self.interner.tcx.lang_items().sized_trait() == Some(def_id) { + Some(chalk_solve::rust_ir::WellKnownTrait::Sized) + } else if self.interner.tcx.lang_items().copy_trait() == Some(def_id) { + Some(chalk_solve::rust_ir::WellKnownTrait::Copy) + } else if self.interner.tcx.lang_items().clone_trait() == Some(def_id) { + Some(chalk_solve::rust_ir::WellKnownTrait::Clone) + } else if self.interner.tcx.lang_items().drop_trait() == Some(def_id) { + Some(chalk_solve::rust_ir::WellKnownTrait::Drop) + } else if self.interner.tcx.lang_items().fn_trait() == Some(def_id) { + Some(chalk_solve::rust_ir::WellKnownTrait::Fn) + } else if self + .interner + .tcx + .lang_items() + .fn_once_trait() + .map(|t| def_id == t) + .unwrap_or(false) + { + Some(chalk_solve::rust_ir::WellKnownTrait::FnOnce) + } else if self + .interner + .tcx + .lang_items() + .fn_mut_trait() + .map(|t| def_id == t) + .unwrap_or(false) + { + Some(chalk_solve::rust_ir::WellKnownTrait::FnMut) + } else { + None + }; Arc::new(chalk_solve::rust_ir::TraitDatum { id: trait_id, binders: chalk_ir::Binders::new( @@ -121,7 +151,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t auto: trait_def.has_auto_impl, marker: trait_def.is_marker, upstream: !def_id.is_local(), - fundamental: self.tcx.has_attr(def_id, sym::fundamental), + fundamental: self.interner.tcx.has_attr(def_id, sym::fundamental), non_enumerable: true, coinductive: false, }, @@ -136,45 +166,50 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t ) -> Arc<chalk_solve::rust_ir::AdtDatum<RustInterner<'tcx>>> { let adt_def = adt_id.0; - let bound_vars = bound_vars_for_item(self.tcx, adt_def.did); + let bound_vars = bound_vars_for_item(self.interner.tcx, adt_def.did); let binders = binders_for(&self.interner, bound_vars); - let predicates = self.tcx.predicates_of(adt_def.did).predicates; - let where_clauses: Vec<_> = predicates + let where_clauses = self.where_clauses_for(adt_def.did, bound_vars); + + let variants: Vec<_> = adt_def + .variants .iter() - .map(|(wc, _)| wc.subst(self.tcx, bound_vars)) - .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)) - .collect(); - let fields = match adt_def.adt_kind() { - ty::AdtKind::Struct | ty::AdtKind::Union => { - let variant = adt_def.non_enum_variant(); - variant + .map(|variant| chalk_solve::rust_ir::AdtVariantDatum { + fields: variant .fields .iter() - .map(|field| { - self.tcx - .type_of(field.did) - .subst(self.tcx, bound_vars) - .lower_into(&self.interner) - }) - .collect() - } - // FIXME(chalk): handle enums; force_impl_for requires this - ty::AdtKind::Enum => vec![], - }; - let struct_datum = Arc::new(chalk_solve::rust_ir::AdtDatum { + .map(|field| field.ty(self.interner.tcx, bound_vars).lower_into(&self.interner)) + .collect(), + }) + .collect(); + Arc::new(chalk_solve::rust_ir::AdtDatum { id: adt_id, binders: chalk_ir::Binders::new( binders, - chalk_solve::rust_ir::AdtDatumBound { fields, where_clauses }, + chalk_solve::rust_ir::AdtDatumBound { variants, where_clauses }, ), flags: chalk_solve::rust_ir::AdtFlags { upstream: !adt_def.did.is_local(), fundamental: adt_def.is_fundamental(), phantom_data: adt_def.is_phantom_data(), }, - }); - struct_datum + kind: match adt_def.adt_kind() { + ty::AdtKind::Struct => chalk_solve::rust_ir::AdtKind::Struct, + ty::AdtKind::Union => chalk_solve::rust_ir::AdtKind::Union, + ty::AdtKind::Enum => chalk_solve::rust_ir::AdtKind::Enum, + }, + }) + } + + fn adt_repr( + &self, + adt_id: chalk_ir::AdtId<RustInterner<'tcx>>, + ) -> chalk_solve::rust_ir::AdtRepr { + let adt_def = adt_id.0; + chalk_solve::rust_ir::AdtRepr { + repr_c: adt_def.repr.c(), + repr_packed: adt_def.repr.packed(), + } } fn fn_def_datum( @@ -182,30 +217,25 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t fn_def_id: chalk_ir::FnDefId<RustInterner<'tcx>>, ) -> Arc<chalk_solve::rust_ir::FnDefDatum<RustInterner<'tcx>>> { let def_id = fn_def_id.0; - let bound_vars = bound_vars_for_item(self.tcx, def_id); + let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(&self.interner, bound_vars); - let predicates = self.tcx.predicates_defined_on(def_id).predicates; - let where_clauses: Vec<_> = predicates - .iter() - .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) - .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect(); + let where_clauses = self.where_clauses_for(def_id, bound_vars); - let sig = self.tcx.fn_sig(def_id); - let inputs_and_output = sig.inputs_and_output(); + let sig = self.interner.tcx.fn_sig(def_id); let (inputs_and_output, iobinders, _) = crate::chalk::lowering::collect_bound_vars( &self.interner, - self.tcx, - &inputs_and_output, + self.interner.tcx, + &sig.inputs_and_output().subst(self.interner.tcx, bound_vars), ); let argument_types = inputs_and_output[..inputs_and_output.len() - 1] .iter() - .map(|t| t.subst(self.tcx, &bound_vars).lower_into(&self.interner)) + .map(|t| t.subst(self.interner.tcx, &bound_vars).lower_into(&self.interner)) .collect(); let return_type = inputs_and_output[inputs_and_output.len() - 1] - .subst(self.tcx, &bound_vars) + .subst(self.interner.tcx, &bound_vars) .lower_into(&self.interner); let bound = chalk_solve::rust_ir::FnDefDatumBound { @@ -217,7 +247,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t }; Arc::new(chalk_solve::rust_ir::FnDefDatum { id: fn_def_id, - abi: sig.abi(), + sig: sig.lower_into(&self.interner), binders: chalk_ir::Binders::new(binders, bound), }) } @@ -227,17 +257,19 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t impl_id: chalk_ir::ImplId<RustInterner<'tcx>>, ) -> Arc<chalk_solve::rust_ir::ImplDatum<RustInterner<'tcx>>> { let def_id = impl_id.0; - let bound_vars = bound_vars_for_item(self.tcx, def_id); + let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(&self.interner, bound_vars); - let trait_ref = self.tcx.impl_trait_ref(def_id).expect("not an impl"); - let trait_ref = trait_ref.subst(self.tcx, bound_vars); + let trait_ref = self.interner.tcx.impl_trait_ref(def_id).expect("not an impl"); + let trait_ref = trait_ref.subst(self.interner.tcx, bound_vars); + let mut regions_substitutor = lowering::RegionsSubstitutor::new( + self.interner.tcx, + self.restatic_placeholder, + self.reempty_placeholder, + ); + let trait_ref = trait_ref.fold_with(&mut regions_substitutor); - let predicates = self.tcx.predicates_of(def_id).predicates; - let where_clauses: Vec<_> = predicates - .iter() - .map(|(wc, _)| wc.subst(self.tcx, bound_vars)) - .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect(); + let where_clauses = self.where_clauses_for(def_id, bound_vars); let value = chalk_solve::rust_ir::ImplDatumBound { trait_ref: trait_ref.lower_into(&self.interner), @@ -256,6 +288,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t &self, trait_id: chalk_ir::TraitId<RustInterner<'tcx>>, parameters: &[chalk_ir::GenericArg<RustInterner<'tcx>>], + _binders: &chalk_ir::CanonicalVarKinds<RustInterner<'tcx>>, ) -> Vec<chalk_ir::ImplId<RustInterner<'tcx>>> { let def_id = trait_id.0; @@ -263,14 +296,20 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t // require us to be able to interconvert `Ty<'tcx>`, and we're // not there yet. - let all_impls = self.tcx.all_impls(def_id); + let all_impls = self.interner.tcx.all_impls(def_id); let matched_impls = all_impls.filter(|impl_def_id| { use chalk_ir::could_match::CouldMatch; - let trait_ref = self.tcx.impl_trait_ref(*impl_def_id).unwrap(); - let bound_vars = bound_vars_for_item(self.tcx, *impl_def_id); + let trait_ref = self.interner.tcx.impl_trait_ref(*impl_def_id).unwrap(); + let bound_vars = bound_vars_for_item(self.interner.tcx, *impl_def_id); let self_ty = trait_ref.self_ty(); - let self_ty = self_ty.subst(self.tcx, bound_vars); + let self_ty = self_ty.subst(self.interner.tcx, bound_vars); + let mut regions_substitutor = lowering::RegionsSubstitutor::new( + self.interner.tcx, + self.restatic_placeholder, + self.reempty_placeholder, + ); + let self_ty = self_ty.fold_with(&mut regions_substitutor); let lowered_ty = self_ty.lower_into(&self.interner); parameters[0].assert_ty_ref(&self.interner).could_match(&self.interner, &lowered_ty) @@ -283,21 +322,75 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t fn impl_provided_for( &self, auto_trait_id: chalk_ir::TraitId<RustInterner<'tcx>>, - adt_id: chalk_ir::AdtId<RustInterner<'tcx>>, + app_ty: &chalk_ir::ApplicationTy<RustInterner<'tcx>>, ) -> bool { + use chalk_ir::Scalar::*; + use chalk_ir::TypeName::*; + let trait_def_id = auto_trait_id.0; - let adt_def = adt_id.0; - let all_impls = self.tcx.all_impls(trait_def_id); + let all_impls = self.interner.tcx.all_impls(trait_def_id); for impl_def_id in all_impls { - let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); + let trait_ref = self.interner.tcx.impl_trait_ref(impl_def_id).unwrap(); let self_ty = trait_ref.self_ty(); - match self_ty.kind { - ty::Adt(impl_adt_def, _) => { - if impl_adt_def == adt_def { - return true; + let provides = match (self_ty.kind(), app_ty.name) { + (&ty::Adt(impl_adt_def, ..), Adt(id)) => impl_adt_def.did == id.0.did, + (_, AssociatedType(_ty_id)) => { + // FIXME(chalk): See https://github.com/rust-lang/rust/pull/77152#discussion_r494484774 + false + } + (ty::Bool, Scalar(Bool)) => true, + (ty::Char, Scalar(Char)) => true, + (ty::Int(ty1), Scalar(Int(ty2))) => match (ty1, ty2) { + (ast::IntTy::Isize, chalk_ir::IntTy::Isize) + | (ast::IntTy::I8, chalk_ir::IntTy::I8) + | (ast::IntTy::I16, chalk_ir::IntTy::I16) + | (ast::IntTy::I32, chalk_ir::IntTy::I32) + | (ast::IntTy::I64, chalk_ir::IntTy::I64) + | (ast::IntTy::I128, chalk_ir::IntTy::I128) => true, + _ => false, + }, + (ty::Uint(ty1), Scalar(Uint(ty2))) => match (ty1, ty2) { + (ast::UintTy::Usize, chalk_ir::UintTy::Usize) + | (ast::UintTy::U8, chalk_ir::UintTy::U8) + | (ast::UintTy::U16, chalk_ir::UintTy::U16) + | (ast::UintTy::U32, chalk_ir::UintTy::U32) + | (ast::UintTy::U64, chalk_ir::UintTy::U64) + | (ast::UintTy::U128, chalk_ir::UintTy::U128) => true, + _ => false, + }, + (ty::Float(ty1), Scalar(Float(ty2))) => match (ty1, ty2) { + (ast::FloatTy::F32, chalk_ir::FloatTy::F32) + | (ast::FloatTy::F64, chalk_ir::FloatTy::F64) => true, + _ => false, + }, + (&ty::Tuple(..), Tuple(..)) => true, + (&ty::Array(..), Array) => true, + (&ty::Slice(..), Slice) => true, + (&ty::RawPtr(type_and_mut), Raw(mutability)) => { + match (type_and_mut.mutbl, mutability) { + (ast::Mutability::Mut, chalk_ir::Mutability::Mut) => true, + (ast::Mutability::Mut, chalk_ir::Mutability::Not) => false, + (ast::Mutability::Not, chalk_ir::Mutability::Mut) => false, + (ast::Mutability::Not, chalk_ir::Mutability::Not) => true, } } - _ => {} + (&ty::Ref(.., mutability1), Ref(mutability2)) => match (mutability1, mutability2) { + (ast::Mutability::Mut, chalk_ir::Mutability::Mut) => true, + (ast::Mutability::Mut, chalk_ir::Mutability::Not) => false, + (ast::Mutability::Not, chalk_ir::Mutability::Mut) => false, + (ast::Mutability::Not, chalk_ir::Mutability::Not) => true, + }, + (&ty::Opaque(def_id, ..), OpaqueType(opaque_ty_id)) => def_id == opaque_ty_id.0, + (&ty::FnDef(def_id, ..), FnDef(fn_def_id)) => def_id == fn_def_id.0, + (&ty::Str, Str) => true, + (&ty::Never, Never) => true, + (&ty::Closure(def_id, ..), Closure(closure_id)) => def_id == closure_id.0, + (&ty::Foreign(def_id), Foreign(foreign_def_id)) => def_id == foreign_def_id.0, + (&ty::Error(..), Error) => false, + _ => false, + }; + if provides { + return true; } } false @@ -308,7 +401,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t associated_ty_id: chalk_solve::rust_ir::AssociatedTyValueId<RustInterner<'tcx>>, ) -> Arc<chalk_solve::rust_ir::AssociatedTyValue<RustInterner<'tcx>>> { let def_id = associated_ty_id.0; - let assoc_item = self.tcx.associated_item(def_id); + let assoc_item = self.interner.tcx.associated_item(def_id); let impl_id = match assoc_item.container { AssocItemContainer::TraitContainer(def_id) => def_id, _ => unimplemented!("Not possible??"), @@ -317,9 +410,9 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t AssocKind::Type => {} _ => unimplemented!("Not possible??"), } - let bound_vars = bound_vars_for_item(self.tcx, def_id); + let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(&self.interner, bound_vars); - let ty = self.tcx.type_of(def_id); + let ty = self.interner.tcx.type_of(def_id); Arc::new(chalk_solve::rust_ir::AssociatedTyValue { impl_id: chalk_ir::ImplId(impl_id), @@ -346,78 +439,20 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t &self, opaque_ty_id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>, ) -> Arc<chalk_solve::rust_ir::OpaqueTyDatum<RustInterner<'tcx>>> { - let bound_vars = bound_vars_for_item(self.tcx, opaque_ty_id.0); + let bound_vars = bound_vars_for_item(self.interner.tcx, opaque_ty_id.0); let binders = binders_for(&self.interner, bound_vars); - let predicates = self.tcx.predicates_defined_on(opaque_ty_id.0).predicates; - let where_clauses: Vec<_> = predicates - .iter() - .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) - .filter_map(|wc| LowerInto::<Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>::lower_into(wc, &self.interner)).collect(); + let where_clauses = self.where_clauses_for(opaque_ty_id.0, bound_vars); let value = chalk_solve::rust_ir::OpaqueTyDatumBound { - bounds: chalk_ir::Binders::new(binders, where_clauses), + bounds: chalk_ir::Binders::new(binders.clone(), vec![]), + where_clauses: chalk_ir::Binders::new(binders, where_clauses), }; Arc::new(chalk_solve::rust_ir::OpaqueTyDatum { opaque_ty_id, - bound: chalk_ir::Binders::new(chalk_ir::VariableKinds::new(&self.interner), value), + bound: chalk_ir::Binders::empty(&self.interner, value), }) } - /// Since Chalk can't handle all Rust types currently, we have to handle - /// some specially for now. Over time, these `Some` returns will change to - /// `None` and eventually this function will be removed. - fn force_impl_for( - &self, - well_known: chalk_solve::rust_ir::WellKnownTrait, - ty: &chalk_ir::TyData<RustInterner<'tcx>>, - ) -> Option<bool> { - use chalk_ir::TyData::*; - match well_known { - chalk_solve::rust_ir::WellKnownTrait::Sized => match ty { - Apply(apply) => match apply.name { - chalk_ir::TypeName::Adt(chalk_ir::AdtId(adt_def)) => match adt_def.adt_kind() { - ty::AdtKind::Struct | ty::AdtKind::Union => None, - ty::AdtKind::Enum => { - let constraint = self.tcx.adt_sized_constraint(adt_def.did); - if !constraint.0.is_empty() { unimplemented!() } else { Some(true) } - } - }, - _ => None, - }, - Dyn(_) - | Alias(_) - | Placeholder(_) - | Function(_) - | InferenceVar(_, _) - | BoundVar(_) => None, - }, - chalk_solve::rust_ir::WellKnownTrait::Copy - | chalk_solve::rust_ir::WellKnownTrait::Clone => match ty { - Apply(apply) => match apply.name { - chalk_ir::TypeName::Adt(chalk_ir::AdtId(adt_def)) => match adt_def.adt_kind() { - ty::AdtKind::Struct | ty::AdtKind::Union => None, - ty::AdtKind::Enum => { - let constraint = self.tcx.adt_sized_constraint(adt_def.did); - if !constraint.0.is_empty() { unimplemented!() } else { Some(true) } - } - }, - _ => None, - }, - Dyn(_) - | Alias(_) - | Placeholder(_) - | Function(_) - | InferenceVar(_, _) - | BoundVar(_) => None, - }, - chalk_solve::rust_ir::WellKnownTrait::Drop => None, - chalk_solve::rust_ir::WellKnownTrait::Fn => None, - chalk_solve::rust_ir::WellKnownTrait::FnMut => None, - chalk_solve::rust_ir::WellKnownTrait::FnOnce => None, - chalk_solve::rust_ir::WellKnownTrait::Unsize => None, - } - } - fn program_clauses_for_env( &self, environment: &chalk_ir::Environment<RustInterner<'tcx>>, @@ -430,21 +465,24 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t well_known_trait: chalk_solve::rust_ir::WellKnownTrait, ) -> Option<chalk_ir::TraitId<RustInterner<'tcx>>> { use chalk_solve::rust_ir::WellKnownTrait::*; + let lang_items = self.interner.tcx.lang_items(); let def_id = match well_known_trait { - Sized => self.tcx.lang_items().sized_trait(), - Copy => self.tcx.lang_items().copy_trait(), - Clone => self.tcx.lang_items().clone_trait(), - Drop => self.tcx.lang_items().drop_trait(), - Fn => self.tcx.lang_items().fn_trait(), - FnMut => self.tcx.lang_items().fn_mut_trait(), - FnOnce => self.tcx.lang_items().fn_once_trait(), - Unsize => self.tcx.lang_items().unsize_trait(), + Sized => lang_items.sized_trait(), + Copy => lang_items.copy_trait(), + Clone => lang_items.clone_trait(), + Drop => lang_items.drop_trait(), + Fn => lang_items.fn_trait(), + FnMut => lang_items.fn_mut_trait(), + FnOnce => lang_items.fn_once_trait(), + Unsize => lang_items.unsize_trait(), + Unpin => lang_items.unpin_trait(), + CoerceUnsized => lang_items.coerce_unsized_trait(), }; def_id.map(chalk_ir::TraitId) } fn is_object_safe(&self, trait_id: chalk_ir::TraitId<RustInterner<'tcx>>) -> bool { - self.tcx.is_object_safe(trait_id.0) + self.interner.tcx.is_object_safe(trait_id.0) } fn hidden_opaque_type( @@ -452,7 +490,10 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t _id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>, ) -> chalk_ir::Ty<RustInterner<'tcx>> { // FIXME(chalk): actually get hidden ty - self.tcx.mk_ty(ty::Tuple(self.tcx.intern_substs(&[]))).lower_into(&self.interner) + self.interner + .tcx + .mk_ty(ty::Tuple(self.interner.tcx.intern_substs(&[]))) + .lower_into(&self.interner) } fn closure_kind( @@ -460,7 +501,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>, substs: &chalk_ir::Substitution<RustInterner<'tcx>>, ) -> chalk_solve::rust_ir::ClosureKind { - let kind = &substs.parameters(&self.interner)[substs.len(&self.interner) - 3]; + let kind = &substs.as_slice(&self.interner)[substs.len(&self.interner) - 3]; match kind.assert_ty_ref(&self.interner).data(&self.interner) { chalk_ir::TyData::Apply(apply) => match apply.name { chalk_ir::TypeName::Scalar(scalar) => match scalar { @@ -484,10 +525,10 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t substs: &chalk_ir::Substitution<RustInterner<'tcx>>, ) -> chalk_ir::Binders<chalk_solve::rust_ir::FnDefInputsAndOutputDatum<RustInterner<'tcx>>> { - let sig = &substs.parameters(&self.interner)[substs.len(&self.interner) - 2]; + let sig = &substs.as_slice(&self.interner)[substs.len(&self.interner) - 2]; match sig.assert_ty_ref(&self.interner).data(&self.interner) { chalk_ir::TyData::Function(f) => { - let substitution = f.substitution.parameters(&self.interner); + let substitution = f.substitution.as_slice(&self.interner); let return_type = substitution.last().unwrap().assert_ty_ref(&self.interner).clone(); // Closure arguments are tupled @@ -506,7 +547,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t }; chalk_ir::Binders::new( - chalk_ir::VariableKinds::from( + chalk_ir::VariableKinds::from_iter( &self.interner, (0..f.num_binders).map(|_| chalk_ir::VariableKind::Lifetime), ), @@ -523,7 +564,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t substs: &chalk_ir::Substitution<RustInterner<'tcx>>, ) -> chalk_ir::Binders<chalk_ir::Ty<RustInterner<'tcx>>> { let inputs_and_output = self.closure_inputs_and_output(_closure_id, substs); - let tuple = substs.parameters(&self.interner).last().unwrap().assert_ty_ref(&self.interner); + let tuple = substs.as_slice(&self.interner).last().unwrap().assert_ty_ref(&self.interner); inputs_and_output.map_ref(|_| tuple.clone()) } @@ -532,8 +573,8 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>, substs: &chalk_ir::Substitution<RustInterner<'tcx>>, ) -> chalk_ir::Substitution<RustInterner<'tcx>> { - let substitution = &substs.parameters(&self.interner)[0..substs.len(&self.interner) - 3]; - chalk_ir::Substitution::from(&self.interner, substitution) + let substitution = &substs.as_slice(&self.interner)[0..substs.len(&self.interner) - 3]; + chalk_ir::Substitution::from_iter(&self.interner, substitution) } } @@ -573,7 +614,7 @@ fn binders_for<'tcx>( interner: &RustInterner<'tcx>, bound_vars: SubstsRef<'tcx>, ) -> chalk_ir::VariableKinds<RustInterner<'tcx>> { - chalk_ir::VariableKinds::from( + chalk_ir::VariableKinds::from_iter( interner, bound_vars.iter().map(|arg| match arg.unpack() { ty::subst::GenericArgKind::Lifetime(_re) => chalk_ir::VariableKind::Lifetime, diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index a043fa3f4c8..1e1841a57f8 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -31,9 +31,7 @@ //! not. To lower anything wrapped in a `Binder`, we first deeply find any bound //! variables from the current `Binder`. -use rustc_middle::traits::{ - ChalkEnvironmentAndGoal, ChalkEnvironmentClause, ChalkRustInterner as RustInterner, -}; +use rustc_middle::traits::{ChalkEnvironmentAndGoal, ChalkRustInterner as RustInterner}; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{ @@ -41,10 +39,10 @@ use rustc_middle::ty::{ }; use rustc_span::def_id::DefId; +use chalk_ir::{FnSig, ForeignDefId}; +use rustc_hir::Unsafety; use std::collections::btree_map::{BTreeMap, Entry}; -use chalk_ir::fold::shift::Shift; - /// Essentially an `Into` with a `&RustInterner` parameter crate trait LowerInto<'tcx, T> { /// Lower a rustc construct (e.g., `ty::TraitPredicate`) to a chalk type, consuming `self`. @@ -56,7 +54,13 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Substitution<RustInterner<'tcx>>> for Subst self, interner: &RustInterner<'tcx>, ) -> chalk_ir::Substitution<RustInterner<'tcx>> { - chalk_ir::Substitution::from(interner, self.iter().map(|s| s.lower_into(interner))) + chalk_ir::Substitution::from_iter(interner, self.iter().map(|s| s.lower_into(interner))) + } +} + +impl<'tcx> LowerInto<'tcx, SubstsRef<'tcx>> for &chalk_ir::Substitution<RustInterner<'tcx>> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> SubstsRef<'tcx> { + interner.tcx.mk_substs(self.iter(interner).map(|subst| subst.lower_into(interner))) } } @@ -76,107 +80,51 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<' self, interner: &RustInterner<'tcx>, ) -> chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'tcx>>> { - let clauses = self.environment.into_iter().filter_map(|clause| match clause { - ChalkEnvironmentClause::Predicate(predicate) => { - // FIXME(chalk): forall - match predicate.bound_atom(interner.tcx).skip_binder() { - ty::PredicateAtom::Trait(predicate, _) => { - let predicate = ty::Binder::bind(predicate); - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &predicate); - - Some( - chalk_ir::ProgramClauseData(chalk_ir::Binders::new( - binders, - chalk_ir::ProgramClauseImplication { - consequence: chalk_ir::DomainGoal::FromEnv( - chalk_ir::FromEnv::Trait( - predicate.trait_ref.lower_into(interner), - ), - ), - conditions: chalk_ir::Goals::new(interner), - priority: chalk_ir::ClausePriority::High, - }, - )) - .intern(interner), - ) - } - ty::PredicateAtom::RegionOutlives(predicate) => { - let predicate = ty::Binder::bind(predicate); - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &predicate); - - Some( - chalk_ir::ProgramClauseData(chalk_ir::Binders::new( - binders, - chalk_ir::ProgramClauseImplication { - consequence: chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::LifetimeOutlives( - chalk_ir::LifetimeOutlives { - a: predicate.0.lower_into(interner), - b: predicate.1.lower_into(interner), - }, - ), - ), - conditions: chalk_ir::Goals::new(interner), - priority: chalk_ir::ClausePriority::High, - }, - )) - .intern(interner), - ) - } - // FIXME(chalk): need to add TypeOutlives - ty::PredicateAtom::TypeOutlives(_) => None, - ty::PredicateAtom::Projection(predicate) => { - let predicate = ty::Binder::bind(predicate); - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &predicate); - - Some( - chalk_ir::ProgramClauseData(chalk_ir::Binders::new( - binders, - chalk_ir::ProgramClauseImplication { - consequence: chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::AliasEq( - predicate.lower_into(interner), - ), - ), - conditions: chalk_ir::Goals::new(interner), - priority: chalk_ir::ClausePriority::High, - }, - )) - .intern(interner), - ) - } - ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => { - bug!("unexpected predicate {}", predicate) - } + let clauses = self.environment.into_iter().map(|predicate| { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &predicate.bound_atom(interner.tcx)); + let consequence = match predicate { + ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { + chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty(ty.lower_into(interner))) } - } - ChalkEnvironmentClause::TypeFromEnv(ty) => Some( - chalk_ir::ProgramClauseData(chalk_ir::Binders::new( - chalk_ir::VariableKinds::new(interner), - chalk_ir::ProgramClauseImplication { - consequence: chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty( - ty.lower_into(interner).shifted_in(interner), - )), - conditions: chalk_ir::Goals::new(interner), - priority: chalk_ir::ClausePriority::High, - }, - )) - .intern(interner), - ), + ty::PredicateAtom::Trait(predicate, _) => chalk_ir::DomainGoal::FromEnv( + chalk_ir::FromEnv::Trait(predicate.trait_ref.lower_into(interner)), + ), + ty::PredicateAtom::RegionOutlives(predicate) => chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { + a: predicate.0.lower_into(interner), + b: predicate.1.lower_into(interner), + }), + ), + ty::PredicateAtom::TypeOutlives(predicate) => chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { + ty: predicate.0.lower_into(interner), + lifetime: predicate.1.lower_into(interner), + }), + ), + ty::PredicateAtom::Projection(predicate) => chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)), + ), + ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => bug!("unexpected predicate {}", predicate), + }; + let value = chalk_ir::ProgramClauseImplication { + consequence, + conditions: chalk_ir::Goals::empty(interner), + priority: chalk_ir::ClausePriority::High, + constraints: chalk_ir::Constraints::empty(interner), + }; + chalk_ir::ProgramClauseData(chalk_ir::Binders::new(binders, value)).intern(interner) }); let goal: chalk_ir::GoalData<RustInterner<'tcx>> = self.goal.lower_into(&interner); chalk_ir::InEnvironment { environment: chalk_ir::Environment { - clauses: chalk_ir::ProgramClauses::from(&interner, clauses), + clauses: chalk_ir::ProgramClauses::from_iter(&interner, clauses), }, goal: goal.intern(&interner), } @@ -185,63 +133,52 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<' impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predicate<'tcx> { fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData<RustInterner<'tcx>> { - // FIXME(chalk): forall - match self.bound_atom(interner.tcx).skip_binder() { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &self.bound_atom(interner.tcx)); + + let value = match predicate { ty::PredicateAtom::Trait(predicate, _) => { - ty::Binder::bind(predicate).lower_into(interner) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)), + )) } ty::PredicateAtom::RegionOutlives(predicate) => { - let predicate = ty::Binder::bind(predicate); - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &predicate); - - chalk_ir::GoalData::Quantified( - chalk_ir::QuantifierKind::ForAll, - chalk_ir::Binders::new( - binders, - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { - a: predicate.0.lower_into(interner), - b: predicate.1.lower_into(interner), - }), - )) - .intern(interner), - ), - ) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { + a: predicate.0.lower_into(interner), + b: predicate.1.lower_into(interner), + }), + )) } - // FIXME(chalk): TypeOutlives - ty::PredicateAtom::TypeOutlives(_predicate) => { - chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) + ty::PredicateAtom::TypeOutlives(predicate) => { + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { + ty: predicate.0.lower_into(interner), + lifetime: predicate.1.lower_into(interner), + }), + )) } ty::PredicateAtom::Projection(predicate) => { - ty::Binder::bind(predicate).lower_into(interner) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)), + )) } ty::PredicateAtom::WellFormed(arg) => match arg.unpack() { - GenericArgKind::Type(ty) => match ty.kind { + GenericArgKind::Type(ty) => match ty.kind() { // FIXME(chalk): In Chalk, a placeholder is WellFormed if it // `FromEnv`. However, when we "lower" Params, we don't update // the environment. - ty::Placeholder(..) => chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)), - - _ => { - let (ty, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &ty::Binder::bind(ty)); - - chalk_ir::GoalData::Quantified( - chalk_ir::QuantifierKind::ForAll, - chalk_ir::Binders::new( - binders, - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed( - chalk_ir::WellFormed::Ty(ty.lower_into(interner)), - )) - .intern(interner), - ), - ) + ty::Placeholder(..) => { + chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner)) } + + _ => chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed( + chalk_ir::WellFormed::Ty(ty.lower_into(interner)), + )), }, // FIXME(chalk): handle well formed consts GenericArgKind::Const(..) => { - chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) + chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner)) } GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt), }, @@ -258,9 +195,17 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi | ty::PredicateAtom::Subtype(..) | ty::PredicateAtom::ConstEvaluatable(..) | ty::PredicateAtom::ConstEquate(..) => { - chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) + chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner)) } - } + ty::PredicateAtom::TypeWellFormedFromEnv(ty) => chalk_ir::GoalData::DomainGoal( + chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty(ty.lower_into(interner))), + ), + }; + + chalk_ir::GoalData::Quantified( + chalk_ir::QuantifierKind::ForAll, + chalk_ir::Binders::new(binders, value.intern(interner)), + ) } } @@ -275,25 +220,6 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::TraitRef<RustInterner<'tcx>>> } } -impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> - for ty::PolyTraitPredicate<'tcx> -{ - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData<RustInterner<'tcx>> { - let (ty, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self); - - chalk_ir::GoalData::Quantified( - chalk_ir::QuantifierKind::ForAll, - chalk_ir::Binders::new( - binders, - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::Implemented(ty.trait_ref.lower_into(interner)), - )) - .intern(interner), - ), - ) - } -} - impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq<RustInterner<'tcx>>> for rustc_middle::ty::ProjectionPredicate<'tcx> { @@ -305,25 +231,6 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq<RustInterner<'tcx>>> } } -impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> - for ty::PolyProjectionPredicate<'tcx> -{ - fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData<RustInterner<'tcx>> { - let (ty, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self); - - chalk_ir::GoalData::Quantified( - chalk_ir::QuantifierKind::ForAll, - chalk_ir::Binders::new( - binders, - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::AliasEq(ty.lower_into(interner)), - )) - .intern(interner), - ), - ) - } -} - impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> { fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Ty<RustInterner<'tcx>> { use chalk_ir::TyData; @@ -340,7 +247,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> { let uint = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Uint(i)), empty()); let float = |f| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Float(f)), empty()); - match self.kind { + match *self.kind() { Bool => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Bool), empty()), Char => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Char), empty()), Int(ty) => match ty { @@ -364,7 +271,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> { ast::FloatTy::F64 => float(chalk_ir::FloatTy::F64), }, Adt(def, substs) => apply(struct_ty(def.did), substs.lower_into(interner)), - Foreign(_def_id) => unimplemented!(), + Foreign(def_id) => apply(chalk_ir::TypeName::Foreign(ForeignDefId(def_id)), empty()), Str => apply(chalk_ir::TypeName::Str, empty()), Array(ty, len) => { let value = match len.val { @@ -381,7 +288,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> { }; apply( chalk_ir::TypeName::Array, - chalk_ir::Substitution::from( + chalk_ir::Substitution::from_iter( interner, &[ chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), @@ -415,7 +322,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> { }; apply( name, - chalk_ir::Substitution::from( + chalk_ir::Substitution::from_iter( interner, &[ chalk_ir::GenericArgData::Lifetime(region.lower_into(interner)) @@ -432,9 +339,10 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> { FnPtr(sig) => { let (inputs_and_outputs, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &sig.inputs_and_output()); - TyData::Function(chalk_ir::Fn { + TyData::Function(chalk_ir::FnPointer { num_binders: binders.len(interner), - substitution: chalk_ir::Substitution::from( + sig: sig.lower_into(interner), + substitution: chalk_ir::Substitution::from_iter( interner, inputs_and_outputs.iter().map(|ty| { chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner) @@ -485,6 +393,112 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> { } } +impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty<RustInterner<'tcx>> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> Ty<'tcx> { + use chalk_ir::TyData; + use rustc_ast::ast; + + let kind = match self.data(interner) { + TyData::Apply(application_ty) => match application_ty.name { + chalk_ir::TypeName::Adt(struct_id) => { + ty::Adt(struct_id.0, application_ty.substitution.lower_into(interner)) + } + chalk_ir::TypeName::Scalar(scalar) => match scalar { + chalk_ir::Scalar::Bool => ty::Bool, + chalk_ir::Scalar::Char => ty::Char, + chalk_ir::Scalar::Int(int_ty) => match int_ty { + chalk_ir::IntTy::Isize => ty::Int(ast::IntTy::Isize), + chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8), + chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16), + chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32), + chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64), + chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128), + }, + chalk_ir::Scalar::Uint(int_ty) => match int_ty { + chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize), + chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8), + chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16), + chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32), + chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64), + chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128), + }, + chalk_ir::Scalar::Float(float_ty) => match float_ty { + chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32), + chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64), + }, + }, + chalk_ir::TypeName::Array => unimplemented!(), + chalk_ir::TypeName::FnDef(id) => { + ty::FnDef(id.0, application_ty.substitution.lower_into(interner)) + } + chalk_ir::TypeName::Closure(closure) => { + ty::Closure(closure.0, application_ty.substitution.lower_into(interner)) + } + chalk_ir::TypeName::Never => ty::Never, + chalk_ir::TypeName::Tuple(_size) => { + ty::Tuple(application_ty.substitution.lower_into(interner)) + } + chalk_ir::TypeName::Slice => ty::Slice( + application_ty.substitution.as_slice(interner)[0] + .ty(interner) + .unwrap() + .lower_into(interner), + ), + chalk_ir::TypeName::Raw(mutbl) => ty::RawPtr(ty::TypeAndMut { + ty: application_ty.substitution.as_slice(interner)[0] + .ty(interner) + .unwrap() + .lower_into(interner), + mutbl: match mutbl { + chalk_ir::Mutability::Mut => ast::Mutability::Mut, + chalk_ir::Mutability::Not => ast::Mutability::Not, + }, + }), + chalk_ir::TypeName::Ref(mutbl) => ty::Ref( + application_ty.substitution.as_slice(interner)[0] + .lifetime(interner) + .unwrap() + .lower_into(interner), + application_ty.substitution.as_slice(interner)[1] + .ty(interner) + .unwrap() + .lower_into(interner), + match mutbl { + chalk_ir::Mutability::Mut => ast::Mutability::Mut, + chalk_ir::Mutability::Not => ast::Mutability::Not, + }, + ), + chalk_ir::TypeName::Str => ty::Str, + chalk_ir::TypeName::OpaqueType(opaque_ty) => { + ty::Opaque(opaque_ty.0, application_ty.substitution.lower_into(interner)) + } + chalk_ir::TypeName::AssociatedType(assoc_ty) => ty::Projection(ty::ProjectionTy { + substs: application_ty.substitution.lower_into(interner), + item_def_id: assoc_ty.0, + }), + chalk_ir::TypeName::Foreign(def_id) => ty::Foreign(def_id.0), + chalk_ir::TypeName::Error => unimplemented!(), + }, + TyData::Placeholder(placeholder) => ty::Placeholder(ty::Placeholder { + universe: ty::UniverseIndex::from_usize(placeholder.ui.counter), + name: ty::BoundVar::from_usize(placeholder.idx), + }), + TyData::Alias(_alias_ty) => unimplemented!(), + TyData::Function(_quantified_ty) => unimplemented!(), + TyData::BoundVar(_bound) => ty::Bound( + ty::DebruijnIndex::from_usize(_bound.debruijn.depth() as usize), + ty::BoundTy { + var: ty::BoundVar::from_usize(_bound.index), + kind: ty::BoundTyKind::Anon, + }, + ), + TyData::InferenceVar(_, _) => unimplemented!(), + TyData::Dyn(_) => unimplemented!(), + }; + interner.tcx.mk_ty(kind) + } +} + impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'tcx> { fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Lifetime<RustInterner<'tcx>> { use rustc_middle::ty::RegionKind::*; @@ -522,6 +536,59 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'t } } +impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'tcx>> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> Region<'tcx> { + let kind = match self.data(interner) { + chalk_ir::LifetimeData::BoundVar(var) => ty::RegionKind::ReLateBound( + ty::DebruijnIndex::from_u32(var.debruijn.depth()), + ty::BoundRegion::BrAnon(var.index as u32), + ), + chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(), + chalk_ir::LifetimeData::Placeholder(p) => { + ty::RegionKind::RePlaceholder(ty::Placeholder { + universe: ty::UniverseIndex::from_usize(p.ui.counter), + name: ty::BoundRegion::BrAnon(p.idx as u32), + }) + } + chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), + }; + interner.tcx.mk_region(kind) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Const<RustInterner<'tcx>>> for ty::Const<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Const<RustInterner<'tcx>> { + let ty = self.ty.lower_into(interner); + let value = match self.val { + ty::ConstKind::Value(val) => { + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: val }) + } + ty::ConstKind::Bound(db, bound) => chalk_ir::ConstValue::BoundVar( + chalk_ir::BoundVar::new(chalk_ir::DebruijnIndex::new(db.as_u32()), bound.index()), + ), + _ => unimplemented!("Const not implemented. {:?}", self), + }; + chalk_ir::ConstData { ty, value }.intern(interner) + } +} + +impl<'tcx> LowerInto<'tcx, ty::Const<'tcx>> for &chalk_ir::Const<RustInterner<'tcx>> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> ty::Const<'tcx> { + let data = self.data(interner); + let ty = data.ty.lower_into(interner); + let val = match data.value { + chalk_ir::ConstValue::BoundVar(var) => ty::ConstKind::Bound( + ty::DebruijnIndex::from_u32(var.debruijn.depth()), + ty::BoundVar::from_u32(var.index as u32), + ), + chalk_ir::ConstValue::InferenceVar(_var) => unimplemented!(), + chalk_ir::ConstValue::Placeholder(_p) => unimplemented!(), + chalk_ir::ConstValue::Concrete(c) => ty::ConstKind::Value(c.interned), + }; + ty::Const { ty, val } + } +} + impl<'tcx> LowerInto<'tcx, chalk_ir::GenericArg<RustInterner<'tcx>>> for GenericArg<'tcx> { fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GenericArg<RustInterner<'tcx>> { match self.unpack() { @@ -531,18 +598,35 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GenericArg<RustInterner<'tcx>>> for Generic ty::subst::GenericArgKind::Lifetime(lifetime) => { chalk_ir::GenericArgData::Lifetime(lifetime.lower_into(interner)) } - ty::subst::GenericArgKind::Const(_) => chalk_ir::GenericArgData::Ty( - chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { - name: chalk_ir::TypeName::Tuple(0), - substitution: chalk_ir::Substitution::empty(interner), - }) - .intern(interner), - ), + ty::subst::GenericArgKind::Const(c) => { + chalk_ir::GenericArgData::Const(c.lower_into(interner)) + } } .intern(interner) } } +impl<'tcx> LowerInto<'tcx, ty::subst::GenericArg<'tcx>> + for &chalk_ir::GenericArg<RustInterner<'tcx>> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> ty::subst::GenericArg<'tcx> { + match self.data(interner) { + chalk_ir::GenericArgData::Ty(ty) => { + let t: Ty<'tcx> = ty.lower_into(interner); + t.into() + } + chalk_ir::GenericArgData::Lifetime(lifetime) => { + let r: Region<'tcx> = lifetime.lower_into(interner); + r.into() + } + chalk_ir::GenericArgData::Const(c) => { + let c: ty::Const<'tcx> = c.lower_into(interner); + interner.tcx.mk_const(c).into() + } + } + } +} + // We lower into an Option here since there are some predicates which Chalk // doesn't have a representation for yet (as a `WhereClause`), but are so common // that we just are accepting the unsoundness for now. The `Option` will @@ -554,41 +638,39 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<' self, interner: &RustInterner<'tcx>, ) -> Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> { - // FIXME(chalk): forall - match self.bound_atom(interner.tcx).skip_binder() { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &self.bound_atom(interner.tcx)); + let value = match predicate { ty::PredicateAtom::Trait(predicate, _) => { - let predicate = ty::Binder::bind(predicate); - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &predicate); - - Some(chalk_ir::Binders::new( - binders, - chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)), - )) + Some(chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner))) } ty::PredicateAtom::RegionOutlives(predicate) => { - let predicate = ty::Binder::bind(predicate); - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &predicate); - - Some(chalk_ir::Binders::new( - binders, - chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { - a: predicate.0.lower_into(interner), - b: predicate.1.lower_into(interner), - }), - )) + Some(chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { + a: predicate.0.lower_into(interner), + b: predicate.1.lower_into(interner), + })) + } + ty::PredicateAtom::TypeOutlives(predicate) => { + Some(chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { + ty: predicate.0.lower_into(interner), + lifetime: predicate.1.lower_into(interner), + })) + } + ty::PredicateAtom::Projection(predicate) => { + Some(chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner))) } - ty::PredicateAtom::TypeOutlives(_predicate) => None, - ty::PredicateAtom::Projection(_predicate) => None, ty::PredicateAtom::WellFormed(_ty) => None, ty::PredicateAtom::ObjectSafe(..) | ty::PredicateAtom::ClosureKind(..) | ty::PredicateAtom::Subtype(..) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => bug!("unexpected predicate {}", &self), - } + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + bug!("unexpected predicate {}", &self) + } + }; + value.map(|value| chalk_ir::Binders::new(binders, value)) } } @@ -601,30 +683,51 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Binders<chalk_ir::QuantifiedWhereClauses<Ru ) -> chalk_ir::Binders<chalk_ir::QuantifiedWhereClauses<RustInterner<'tcx>>> { let (predicates, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self); + let self_ty = interner.tcx.mk_ty(ty::Bound( + // This is going to be wrapped in a binder + ty::DebruijnIndex::from_usize(1), + ty::BoundTy { var: ty::BoundVar::from_usize(0), kind: ty::BoundTyKind::Anon }, + )); let where_clauses = predicates.into_iter().map(|predicate| match predicate { ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id, substs }) => { chalk_ir::Binders::new( - chalk_ir::VariableKinds::new(interner), + chalk_ir::VariableKinds::empty(interner), chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { trait_id: chalk_ir::TraitId(def_id), - substitution: substs.lower_into(interner), + substitution: interner + .tcx + .mk_substs_trait(self_ty, substs) + .lower_into(interner), }), ) } ty::ExistentialPredicate::Projection(_predicate) => unimplemented!(), ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new( - chalk_ir::VariableKinds::new(interner), + chalk_ir::VariableKinds::empty(interner), chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { trait_id: chalk_ir::TraitId(def_id), - substitution: chalk_ir::Substitution::empty(interner), + substitution: interner.tcx.mk_substs_trait(self_ty, &[]).lower_into(interner), }), ), }); - let value = chalk_ir::QuantifiedWhereClauses::from(interner, where_clauses); + let value = chalk_ir::QuantifiedWhereClauses::from_iter(interner, where_clauses); chalk_ir::Binders::new(binders, value) } } +impl<'tcx> LowerInto<'tcx, chalk_ir::FnSig<RustInterner<'tcx>>> for ty::Binder<ty::FnSig<'tcx>> { + fn lower_into(self, _interner: &RustInterner<'_>) -> FnSig<RustInterner<'tcx>> { + chalk_ir::FnSig { + abi: self.abi(), + safety: match self.unsafety() { + Unsafety::Normal => chalk_ir::Safety::Safe, + Unsafety::Unsafe => chalk_ir::Safety::Unsafe, + }, + variadic: self.c_variadic(), + } + } +} + /// To collect bound vars, we have to do two passes. In the first pass, we /// collect all `BoundRegion`s and `ty::Bound`s. In the second pass, we then /// replace `BrNamed` into `BrAnon`. The two separate passes are important, @@ -662,7 +765,8 @@ crate fn collect_bound_vars<'a, 'tcx, T: TypeFoldable<'tcx>>( .or_else(|| bug!("Skipped bound var index: ty={:?}, parameters={:?}", ty, parameters)); }); - let binders = chalk_ir::VariableKinds::from(interner, parameters.into_iter().map(|(_, v)| v)); + let binders = + chalk_ir::VariableKinds::from_iter(interner, parameters.into_iter().map(|(_, v)| v)); (new_ty, binders, named_parameters) } @@ -692,7 +796,7 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { } fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - match t.kind { + match *t.kind() { ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { match self.parameters.entry(bound_ty.var.as_u32()) { Entry::Vacant(entry) => { @@ -773,10 +877,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> { result } - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - t.super_fold_with(self) - } - fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { match r { ty::ReLateBound(index, br) if *index == self.binder_index => match br { @@ -807,16 +907,18 @@ crate struct ParamsSubstitutor<'tcx> { tcx: TyCtxt<'tcx>, binder_index: ty::DebruijnIndex, list: Vec<rustc_middle::ty::ParamTy>, + next_ty_placeholder: usize, crate params: rustc_data_structures::fx::FxHashMap<usize, rustc_middle::ty::ParamTy>, crate named_regions: BTreeMap<DefId, u32>, } impl<'tcx> ParamsSubstitutor<'tcx> { - crate fn new(tcx: TyCtxt<'tcx>) -> Self { + crate fn new(tcx: TyCtxt<'tcx>, next_ty_placeholder: usize) -> Self { ParamsSubstitutor { tcx, binder_index: ty::INNERMOST, list: vec![], + next_ty_placeholder, params: rustc_data_structures::fx::FxHashMap::default(), named_regions: BTreeMap::default(), } @@ -836,19 +938,19 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind { + match *t.kind() { // FIXME(chalk): currently we convert params to placeholders starting at // index `0`. To support placeholders, we'll actually need to do a // first pass to collect placeholders. Then we can insert params after. ty::Placeholder(_) => unimplemented!(), ty::Param(param) => match self.list.iter().position(|r| r == ¶m) { - Some(_idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + Some(idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::from_usize(0), - name: ty::BoundVar::from_usize(_idx), + name: ty::BoundVar::from_usize(idx), })), None => { self.list.push(param); - let idx = self.list.len() - 1; + let idx = self.list.len() - 1 + self.next_ty_placeholder; self.params.insert(idx, param); self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::from_usize(0), @@ -884,3 +986,83 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { } } } + +/// Used to collect `Placeholder`s. +crate struct PlaceholdersCollector { + universe_index: ty::UniverseIndex, + crate next_ty_placeholder: usize, + crate next_anon_region_placeholder: u32, +} + +impl PlaceholdersCollector { + crate fn new() -> Self { + PlaceholdersCollector { + universe_index: ty::UniverseIndex::ROOT, + next_ty_placeholder: 0, + next_anon_region_placeholder: 0, + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + match t.kind() { + ty::Placeholder(p) if p.universe == self.universe_index => { + self.next_ty_placeholder = self.next_ty_placeholder.max(p.name.as_usize() + 1); + } + + _ => (), + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: Region<'tcx>) -> bool { + match r { + ty::RePlaceholder(p) if p.universe == self.universe_index => { + if let ty::BoundRegion::BrAnon(anon) = p.name { + self.next_anon_region_placeholder = self.next_anon_region_placeholder.max(anon); + } + } + + _ => (), + }; + + r.super_visit_with(self) + } +} + +/// Used to substitute specific `Regions`s with placeholders. +crate struct RegionsSubstitutor<'tcx> { + tcx: TyCtxt<'tcx>, + restatic_placeholder: ty::Region<'tcx>, + reempty_placeholder: ty::Region<'tcx>, +} + +impl<'tcx> RegionsSubstitutor<'tcx> { + crate fn new( + tcx: TyCtxt<'tcx>, + restatic_placeholder: ty::Region<'tcx>, + reempty_placeholder: ty::Region<'tcx>, + ) -> Self { + RegionsSubstitutor { tcx, restatic_placeholder, reempty_placeholder } + } +} + +impl<'tcx> TypeFolder<'tcx> for RegionsSubstitutor<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { + match r { + ty::ReStatic => self.restatic_placeholder, + ty::ReEmpty(ui) => { + assert_eq!(ui.as_usize(), 0); + self.reempty_placeholder + } + + _ => r.super_fold_with(self), + } + } +} diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index f18b4ca65f6..63c5b884357 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -1,8 +1,7 @@ //! Calls `chalk-solve` to solve a `ty::Predicate` //! -//! In order to call `chalk-solve`, this file must convert a -//! `ChalkCanonicalGoal` into a Chalk ucanonical goal. It then calls Chalk, and -//! converts the answer back into rustc solution. +//! In order to call `chalk-solve`, this file must convert a `CanonicalChalkEnvironmentAndGoal` into +//! a Chalk uncanonical goal. It then calls Chalk, and converts the answer back into rustc solution. crate mod db; crate mod lowering; @@ -15,17 +14,17 @@ use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind}; use rustc_middle::traits::ChalkRustInterner; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{ - self, Bound, BoundVar, ParamTy, Region, RegionKind, Ty, TyCtxt, TypeFoldable, -}; +use rustc_middle::ty::{self, BoundVar, ParamTy, TyCtxt, TypeFoldable}; use rustc_infer::infer::canonical::{ Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse, }; -use rustc_infer::traits::{self, ChalkCanonicalGoal}; +use rustc_infer::traits::{self, CanonicalChalkEnvironmentAndGoal}; use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase; -use crate::chalk::lowering::{LowerInto, ParamsSubstitutor}; +use crate::chalk::lowering::{ + LowerInto, ParamsSubstitutor, PlaceholdersCollector, RegionsSubstitutor, +}; use chalk_solve::Solution; @@ -35,21 +34,40 @@ crate fn provide(p: &mut Providers) { crate fn evaluate_goal<'tcx>( tcx: TyCtxt<'tcx>, - obligation: ChalkCanonicalGoal<'tcx>, + obligation: CanonicalChalkEnvironmentAndGoal<'tcx>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> { let interner = ChalkRustInterner { tcx }; // Chalk doesn't have a notion of `Params`, so instead we use placeholders. - let mut params_substitutor = ParamsSubstitutor::new(tcx); + let mut placeholders_collector = PlaceholdersCollector::new(); + obligation.visit_with(&mut placeholders_collector); + + let restatic_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder), + })); + let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder + 1), + })); + + let mut params_substitutor = + ParamsSubstitutor::new(tcx, placeholders_collector.next_ty_placeholder); let obligation = obligation.fold_with(&mut params_substitutor); + // FIXME(chalk): we really should be substituting these back in the solution let _params: FxHashMap<usize, ParamTy> = params_substitutor.params; + + let mut regions_substitutor = + RegionsSubstitutor::new(tcx, restatic_placeholder, reempty_placeholder); + let obligation = obligation.fold_with(&mut regions_substitutor); + let max_universe = obligation.max_universe.index(); - let _lowered_goal: chalk_ir::UCanonical< + let lowered_goal: chalk_ir::UCanonical< chalk_ir::InEnvironment<chalk_ir::Goal<ChalkRustInterner<'tcx>>>, > = chalk_ir::UCanonical { canonical: chalk_ir::Canonical { - binders: chalk_ir::CanonicalVarKinds::from( + binders: chalk_ir::CanonicalVarKinds::from_iter( &interner, obligation.variables.iter().map(|v| match v.kind { CanonicalVarKind::PlaceholderTy(_ty) => unimplemented!(), @@ -81,108 +99,20 @@ crate fn evaluate_goal<'tcx>( universes: max_universe + 1, }; - let solver_choice = chalk_solve::SolverChoice::SLG { max_size: 32, expected_answers: None }; - let mut solver = solver_choice.into_solver::<ChalkRustInterner<'tcx>>(); - - let db = ChalkRustIrDatabase { tcx, interner }; - let solution = solver.solve(&db, &_lowered_goal); + use chalk_solve::Solver; + let mut solver = chalk_engine::solve::SLGSolver::new(32, None); + let db = ChalkRustIrDatabase { interner, restatic_placeholder, reempty_placeholder }; + let solution = chalk_solve::logging::with_tracing_logs(|| solver.solve(&db, &lowered_goal)); // Ideally, the code to convert *back* to rustc types would live close to // the code to convert *from* rustc types. Right now though, we don't // really need this and so it's really minimal. // Right now, we also treat a `Unique` solution the same as // `Ambig(Definite)`. This really isn't right. - let make_solution = |_subst: chalk_ir::Substitution<_>| { + let make_solution = |subst: chalk_ir::Substitution<_>| { let mut var_values: IndexVec<BoundVar, GenericArg<'tcx>> = IndexVec::new(); - _subst.parameters(&interner).iter().for_each(|p| { - // FIXME(chalk): we should move this elsewhere, since this is - // essentially inverse of lowering a `GenericArg`. - let _data = p.data(&interner); - match _data { - chalk_ir::GenericArgData::Ty(_t) => { - use chalk_ir::TyData; - use rustc_ast as ast; - - let _data = _t.data(&interner); - let kind = match _data { - TyData::Apply(_application_ty) => match _application_ty.name { - chalk_ir::TypeName::Adt(_struct_id) => unimplemented!(), - chalk_ir::TypeName::Scalar(scalar) => match scalar { - chalk_ir::Scalar::Bool => ty::Bool, - chalk_ir::Scalar::Char => ty::Char, - chalk_ir::Scalar::Int(int_ty) => match int_ty { - chalk_ir::IntTy::Isize => ty::Int(ast::IntTy::Isize), - chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8), - chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16), - chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32), - chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64), - chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128), - }, - chalk_ir::Scalar::Uint(int_ty) => match int_ty { - chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize), - chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8), - chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16), - chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32), - chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64), - chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128), - }, - chalk_ir::Scalar::Float(float_ty) => match float_ty { - chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32), - chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64), - }, - }, - chalk_ir::TypeName::Array => unimplemented!(), - chalk_ir::TypeName::FnDef(_) => unimplemented!(), - chalk_ir::TypeName::Closure(_) => unimplemented!(), - chalk_ir::TypeName::Never => unimplemented!(), - chalk_ir::TypeName::Tuple(_size) => unimplemented!(), - chalk_ir::TypeName::Slice => unimplemented!(), - chalk_ir::TypeName::Raw(_) => unimplemented!(), - chalk_ir::TypeName::Ref(_) => unimplemented!(), - chalk_ir::TypeName::Str => unimplemented!(), - chalk_ir::TypeName::OpaqueType(_ty) => unimplemented!(), - chalk_ir::TypeName::AssociatedType(_assoc_ty) => unimplemented!(), - chalk_ir::TypeName::Error => unimplemented!(), - }, - TyData::Placeholder(_placeholder) => { - unimplemented!(); - } - TyData::Alias(_alias_ty) => unimplemented!(), - TyData::Function(_quantified_ty) => unimplemented!(), - TyData::BoundVar(_bound) => Bound( - ty::DebruijnIndex::from_usize(_bound.debruijn.depth() as usize), - ty::BoundTy { - var: ty::BoundVar::from_usize(_bound.index), - kind: ty::BoundTyKind::Anon, - }, - ), - TyData::InferenceVar(_, _) => unimplemented!(), - TyData::Dyn(_) => unimplemented!(), - }; - let _ty: Ty<'_> = tcx.mk_ty(kind); - let _arg: GenericArg<'_> = _ty.into(); - var_values.push(_arg); - } - chalk_ir::GenericArgData::Lifetime(_l) => { - let _data = _l.data(&interner); - let _lifetime: Region<'_> = match _data { - chalk_ir::LifetimeData::BoundVar(_var) => { - tcx.mk_region(RegionKind::ReLateBound( - rustc_middle::ty::DebruijnIndex::from_usize( - _var.debruijn.depth() as usize - ), - rustc_middle::ty::BoundRegion::BrAnon(_var.index as u32), - )) - } - chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(), - chalk_ir::LifetimeData::Placeholder(_index) => unimplemented!(), - chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), - }; - let _arg: GenericArg<'_> = _lifetime.into(); - var_values.push(_arg); - } - chalk_ir::GenericArgData::Const(_) => unimplemented!(), - } + subst.as_slice(&interner).iter().for_each(|p| { + var_values.push(p.lower_into(&interner)); }); let sol = Canonical { max_universe: ty::UniverseIndex::from_usize(0), @@ -194,17 +124,17 @@ crate fn evaluate_goal<'tcx>( value: (), }, }; - &*tcx.arena.alloc(sol) + tcx.arena.alloc(sol) }; solution .map(|s| match s { - Solution::Unique(_subst) => { + Solution::Unique(subst) => { // FIXME(chalk): handle constraints - make_solution(_subst.value.subst) + make_solution(subst.value.subst) } - Solution::Ambig(_guidance) => { - match _guidance { - chalk_solve::Guidance::Definite(_subst) => make_solution(_subst.value), + Solution::Ambig(guidance) => { + match guidance { + chalk_solve::Guidance::Definite(subst) => make_solution(subst.value), chalk_solve::Guidance::Suggested(_) => unimplemented!(), chalk_solve::Guidance::Unknown => { // chalk_fulfill doesn't use the var_values here, so diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index ce00060b9b1..3ee391d6dc7 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -112,7 +112,7 @@ fn dropck_outlives<'tcx>( debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); - match ty.kind { + match ty.kind() { // All parameters live for the duration of the // function. ty::Param(..) => {} @@ -172,7 +172,7 @@ fn dtorck_constraint_for_ty<'tcx>( return Ok(()); } - match ty.kind { + match ty.kind() { ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index de3096eac9b..79308b032ec 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -103,7 +103,8 @@ fn compute_implied_outlives_bounds<'tcx>( | ty::PredicateAtom::ClosureKind(..) | ty::PredicateAtom::ObjectSafe(..) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => vec![], + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => vec![], ty::PredicateAtom::WellFormed(arg) => { wf_args.push(arg); vec![] diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index 6fea4732dda..d0b05beb4e6 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -4,7 +4,6 @@ #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] #![feature(nll)] -#![feature(or_patterns)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 83aee31a39f..3e7c9ac62eb 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -49,6 +49,7 @@ fn not_outlives_predicate(p: &ty::Predicate<'tcx>) -> bool { | ty::PredicateAtom::ClosureKind(..) | ty::PredicateAtom::Subtype(..) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => true, + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => true, } } diff --git a/compiler/rustc_ty/src/instance.rs b/compiler/rustc_ty/src/instance.rs index d0bd88af1ff..75bf8ea0bb8 100644 --- a/compiler/rustc_ty/src/instance.rs +++ b/compiler/rustc_ty/src/instance.rs @@ -53,7 +53,7 @@ fn inner_resolve_instance<'tcx>( let ty = tcx.type_of(def.def_id_for_type_of()); let item_type = tcx.subst_and_normalize_erasing_regions(substs, param_env, &ty); - let def = match item_type.kind { + let def = match *item_type.kind() { ty::FnDef(..) if { let f = item_type.fn_sig(tcx); @@ -68,7 +68,7 @@ fn inner_resolve_instance<'tcx>( if ty.needs_drop(tcx, param_env) { debug!(" => nontrivial drop glue"); - match ty.kind { + match *ty.kind() { ty::Closure(..) | ty::Generator(..) | ty::Tuple(..) @@ -231,7 +231,7 @@ fn resolve_associated_item<'tcx>( trait_closure_kind, )) } - traits::ImplSourceFnPointer(ref data) => match data.fn_ty.kind { + traits::ImplSourceFnPointer(ref data) => match data.fn_ty.kind() { ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), substs: rcvr_substs, @@ -250,7 +250,7 @@ fn resolve_associated_item<'tcx>( let self_ty = trait_ref.self_ty(); let is_copy = self_ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env); - match self_ty.kind { + match self_ty.kind() { _ if is_copy => (), ty::Array(..) | ty::Closure(..) | ty::Tuple(..) => {} _ => return Ok(None), diff --git a/compiler/rustc_ty/src/lib.rs b/compiler/rustc_ty/src/lib.rs index 6e9042d1ba7..904c0062a92 100644 --- a/compiler/rustc_ty/src/lib.rs +++ b/compiler/rustc_ty/src/lib.rs @@ -4,8 +4,7 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(bool_to_option)] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(nll)] #![recursion_limit = "256"] diff --git a/compiler/rustc_ty/src/needs_drop.rs b/compiler/rustc_ty/src/needs_drop.rs index c4af95205fe..0356bcec549 100644 --- a/compiler/rustc_ty/src/needs_drop.rs +++ b/compiler/rustc_ty/src/needs_drop.rs @@ -90,7 +90,7 @@ where }; for component in components { - match component.kind { + match *component.kind() { _ if component.is_copy_modulo_regions(tcx.at(DUMMY_SP), self.param_env) => (), ty::Closure(_, substs) => { @@ -106,7 +106,7 @@ where } let witness = substs.witness(); - let interior_tys = match &witness.kind { + let interior_tys = match witness.kind() { ty::GeneratorWitness(tys) => tcx.erase_late_bound_regions(tys), _ => { tcx.sess.delay_span_bug( diff --git a/compiler/rustc_ty/src/ty.rs b/compiler/rustc_ty/src/ty.rs index 0f1dee7e2e0..c4b6b64339a 100644 --- a/compiler/rustc_ty/src/ty.rs +++ b/compiler/rustc_ty/src/ty.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::svh::Svh; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -5,7 +6,9 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_infer::traits::util; use rustc_middle::hir::map as hir_map; use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_middle::ty::{ + self, Binder, Predicate, PredicateAtom, PredicateKind, ToPredicate, Ty, TyCtxt, WithConstness, +}; use rustc_session::CrateDisambiguator; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -18,7 +21,7 @@ fn sized_constraint_for_ty<'tcx>( ) -> Vec<Ty<'tcx>> { use ty::TyKind::*; - let result = match ty.kind { + let result = match ty.kind() { Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) | FnPtr(_) | Array(..) | Closure(..) | Generator(..) | Never => vec![], @@ -245,7 +248,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { } // Compute the bounds on Self and the type parameters. - let ty::InstantiatedPredicates { predicates, .. } = + let ty::InstantiatedPredicates { mut predicates, .. } = tcx.predicates_of(def_id).instantiate_identity(tcx); // Finally, we have to normalize the bounds in the environment, in @@ -260,11 +263,13 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { // are any errors at that point, so after type checking you can be // sure that this will succeed without errors anyway. - let unnormalized_env = ty::ParamEnv::new( - tcx.intern_predicates(&predicates), - traits::Reveal::UserFacing, - tcx.sess.opts.debugging_opts.chalk.then_some(def_id), - ); + if tcx.sess.opts.debugging_opts.chalk { + let environment = well_formed_types_in_env(tcx, def_id); + predicates.extend(environment); + } + + let unnormalized_env = + ty::ParamEnv::new(tcx.intern_predicates(&predicates), traits::Reveal::UserFacing); let body_id = def_id .as_local() @@ -276,6 +281,122 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { traits::normalize_param_env_or_error(tcx, def_id, unnormalized_env, cause) } +/// Elaborate the environment. +/// +/// Collect a list of `Predicate`'s used for building the `ParamEnv`. Adds `TypeWellFormedFromEnv`'s +/// that are assumed to be well-formed (because they come from the environment). +/// +/// Used only in chalk mode. +fn well_formed_types_in_env<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> &'tcx ty::List<Predicate<'tcx>> { + use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind}; + use rustc_middle::ty::subst::GenericArgKind; + + debug!("environment(def_id = {:?})", def_id); + + // The environment of an impl Trait type is its defining function's environment. + if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) { + return well_formed_types_in_env(tcx, parent); + } + + // Compute the bounds on `Self` and the type parameters. + let ty::InstantiatedPredicates { predicates, .. } = + tcx.predicates_of(def_id).instantiate_identity(tcx); + + let clauses = predicates.into_iter(); + + if !def_id.is_local() { + return ty::List::empty(); + } + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let node = tcx.hir().get(hir_id); + + enum NodeKind { + TraitImpl, + InherentImpl, + Fn, + Other, + }; + + let node_kind = match node { + Node::TraitItem(item) => match item.kind { + TraitItemKind::Fn(..) => NodeKind::Fn, + _ => NodeKind::Other, + }, + + Node::ImplItem(item) => match item.kind { + ImplItemKind::Fn(..) => NodeKind::Fn, + _ => NodeKind::Other, + }, + + Node::Item(item) => match item.kind { + ItemKind::Impl { of_trait: Some(_), .. } => NodeKind::TraitImpl, + ItemKind::Impl { of_trait: None, .. } => NodeKind::InherentImpl, + ItemKind::Fn(..) => NodeKind::Fn, + _ => NodeKind::Other, + }, + + Node::ForeignItem(item) => match item.kind { + ForeignItemKind::Fn(..) => NodeKind::Fn, + _ => NodeKind::Other, + }, + + // FIXME: closures? + _ => NodeKind::Other, + }; + + // FIXME(eddyb) isn't the unordered nature of this a hazard? + let mut inputs = FxIndexSet::default(); + + match node_kind { + // In a trait impl, we assume that the header trait ref and all its + // constituents are well-formed. + NodeKind::TraitImpl => { + let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl"); + + // FIXME(chalk): this has problems because of late-bound regions + //inputs.extend(trait_ref.substs.iter().flat_map(|arg| arg.walk())); + inputs.extend(trait_ref.substs.iter()); + } + + // In an inherent impl, we assume that the receiver type and all its + // constituents are well-formed. + NodeKind::InherentImpl => { + let self_ty = tcx.type_of(def_id); + inputs.extend(self_ty.walk()); + } + + // In an fn, we assume that the arguments and all their constituents are + // well-formed. + NodeKind::Fn => { + let fn_sig = tcx.fn_sig(def_id); + let fn_sig = tcx.liberate_late_bound_regions(def_id, &fn_sig); + + inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk())); + } + + NodeKind::Other => (), + } + let input_clauses = inputs.into_iter().filter_map(|arg| { + match arg.unpack() { + GenericArgKind::Type(ty) => { + let binder = Binder::dummy(PredicateAtom::TypeWellFormedFromEnv(ty)); + Some(tcx.mk_predicate(PredicateKind::ForAll(binder))) + } + + // FIXME(eddyb) no WF conditions from lifetimes? + GenericArgKind::Lifetime(_) => None, + + // FIXME(eddyb) support const generics in Chalk + GenericArgKind::Const(_) => None, + } + }); + + tcx.mk_predicates(clauses.chain(input_clauses)) +} + fn param_env_reveal_all_normalized(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { tcx.param_env(def_id).with_reveal_all_normalized(tcx) } @@ -344,7 +465,7 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Ty<'_>> { } let self_ty = trait_ref.self_ty(); - let self_ty_matches = match self_ty.kind { + let self_ty_matches = match self_ty.kind() { ty::Dynamic(ref data, ty::ReStatic) => data.principal().is_none(), _ => false, }; @@ -398,21 +519,21 @@ fn associated_type_projection_predicates( let pred = obligation.predicate; match pred.skip_binders() { ty::PredicateAtom::Trait(tr, _) => { - if let ty::Projection(p) = tr.self_ty().kind { + if let ty::Projection(p) = *tr.self_ty().kind() { if p == assoc_item_ty { return Some(pred); } } } ty::PredicateAtom::Projection(proj) => { - if let ty::Projection(p) = proj.projection_ty.self_ty().kind { + if let ty::Projection(p) = *proj.projection_ty.self_ty().kind() { if p == assoc_item_ty { return Some(pred); } } } ty::PredicateAtom::TypeOutlives(outlives) => { - if let ty::Projection(p) = outlives.0.kind { + if let ty::Projection(p) = *outlives.0.kind() { if p == assoc_item_ty { return Some(pred); } @@ -449,14 +570,15 @@ fn opaque_type_projection_predicates( let pred = obligation.predicate; match pred.skip_binders() { ty::PredicateAtom::Trait(tr, _) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = tr.self_ty().kind { + if let ty::Opaque(opaque_def_id, opaque_substs) = *tr.self_ty().kind() { if opaque_def_id == def_id && opaque_substs == substs { return Some(pred); } } } ty::PredicateAtom::Projection(proj) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = proj.projection_ty.self_ty().kind + if let ty::Opaque(opaque_def_id, opaque_substs) = + *proj.projection_ty.self_ty().kind() { if opaque_def_id == def_id && opaque_substs == substs { return Some(pred); @@ -464,7 +586,7 @@ fn opaque_type_projection_predicates( } } ty::PredicateAtom::TypeOutlives(outlives) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = outlives.0.kind { + if let ty::Opaque(opaque_def_id, opaque_substs) = *outlives.0.kind() { if opaque_def_id == def_id && opaque_substs == substs { return Some(pred); } diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml index 0a6bfaef431..e3ba0bea7e8 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_typeck/Cargo.toml @@ -11,6 +11,7 @@ doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } tracing = "0.1" +rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 84dab6de958..b54de1d0916 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -1,8 +1,9 @@ use crate::astconv::{ AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; +use crate::errors::AssocTypeBindingNotAllowed; use rustc_ast::ast::ParamKindOrd; -use rustc_errors::{pluralize, struct_span_err, DiagnosticId, ErrorReported}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{GenericArg, GenericArgs}; @@ -367,7 +368,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if position != GenericArgPosition::Type && !args.bindings.is_empty() { - Self::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); + AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); } let explicit_late_bound = @@ -392,7 +393,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if silent { - return Err(true); + return Err((0i32, None)); } // Unfortunately lifetime and type parameter mismatches are typically styled @@ -441,16 +442,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for span in spans { err.span_label(span, label.as_str()); } - err.emit(); - Err(true) + assert_ne!(bound, provided); + Err((bound as i32 - provided as i32, Some(err))) }; - let mut arg_count_correct = Ok(()); let mut unexpected_spans = vec![]; + let mut lifetime_count_correct = Ok(()); if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes { - arg_count_correct = check_kind_count( + lifetime_count_correct = check_kind_count( "lifetime", param_counts.lifetimes, param_counts.lifetimes, @@ -458,12 +459,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { 0, &mut unexpected_spans, explicit_late_bound == ExplicitLateBound::Yes, - ) - .and(arg_count_correct); + ); } + // FIXME(const_generics:defaults) + let mut const_count_correct = Ok(()); if !infer_args || arg_counts.consts > param_counts.consts { - arg_count_correct = check_kind_count( + const_count_correct = check_kind_count( "const", param_counts.consts, param_counts.consts, @@ -471,13 +473,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg_counts.lifetimes + arg_counts.types, &mut unexpected_spans, false, - ) - .and(arg_count_correct); + ); } + // Note that type errors are currently be emitted *after* const errors. + let mut type_count_correct = Ok(()); if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize { - arg_count_correct = check_kind_count( + type_count_correct = check_kind_count( "type", param_counts.types - defaults.types - has_self as usize, param_counts.types - has_self as usize, @@ -485,14 +488,54 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg_counts.lifetimes, &mut unexpected_spans, false, - ) - .and(arg_count_correct); + ); + } + + // Emit a help message if it's possible that a type could be surrounded in braces + if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct { + if let Err((_, Some(ref mut type_err))) = type_count_correct { + let possible_matches = args.args[arg_counts.lifetimes..] + .iter() + .filter(|arg| { + matches!( + arg, + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. }) + ) + }) + .take(c_mismatch.max(0) as usize); + for arg in possible_matches { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + type_err.multipart_suggestion( + "If this generic argument was intended as a const parameter, \ + try surrounding it with braces:", + suggestions, + Applicability::MaybeIncorrect, + ); + } + } } + let emit_correct = + |correct: Result<(), (_, Option<rustc_errors::DiagnosticBuilder<'_>>)>| match correct { + Ok(()) => Ok(()), + Err((_, None)) => Err(()), + Err((_, Some(mut err))) => { + err.emit(); + Err(()) + } + }; + + let arg_count_correct = emit_correct(lifetime_count_correct) + .and(emit_correct(const_count_correct)) + .and(emit_correct(type_count_correct)); + GenericArgCountResult { explicit_late_bound, - correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch { - reported: if reported_err { Some(ErrorReported) } else { None }, + correct: arg_count_correct.map_err(|()| GenericArgCountMismatch { + reported: Some(ErrorReported), invalid_args: unexpected_spans, }), } @@ -544,13 +587,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Emits an error regarding forbidden type binding associations pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0229, - "associated type bindings are not allowed here" - ); - err.span_label(span, "associated type not allowed here").emit(); + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); } /// Prohibits explicit lifetime arguments if late-bound lifetime parameters diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 80dd26e9154..a743dc1cd20 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -7,6 +7,10 @@ mod generics; use crate::bounds::Bounds; use crate::collect::PlaceholderHirTyCollector; +use crate::errors::{ + AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits, + TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified, +}; use crate::middle::resolve_lifetime as rl; use crate::require_c_abi_if_c_variadic; use rustc_ast::util::lev_distance::find_best_match_for_name; @@ -684,14 +688,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if unbound.is_none() { unbound = Some(&ptr.trait_ref); } else { - struct_span_err!( - tcx.sess, - span, - E0203, - "type parameter has more than one relaxed default \ - bound, only one is supported" - ) - .emit(); + tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span }); } } } @@ -927,18 +924,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { dup_bindings .entry(assoc_ty.def_id) .and_modify(|prev_span| { - struct_span_err!( - self.tcx().sess, - binding.span, - E0719, - "the value of the associated type `{}` (from trait `{}`) \ - is already specified", - binding.item_name, - tcx.def_path_str(assoc_ty.container.id()) - ) - .span_label(binding.span, "re-bound here") - .span_label(*prev_span, format!("`{}` bound here first", binding.item_name)) - .emit(); + self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified { + span: binding.span, + prev_span: *prev_span, + item_name: binding.item_name, + def_path: tcx.def_path_str(assoc_ty.container.id()), + }); }) .or_insert(binding.span); } @@ -1051,13 +1042,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if regular_traits.is_empty() && auto_traits.is_empty() { - struct_span_err!( - tcx.sess, - span, - E0224, - "at least one trait is required for an object type" - ) - .emit(); + tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span }); return tcx.ty_error(); } @@ -1454,7 +1439,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Check if we have an enum variant. let mut variant_resolution = None; - if let ty::Adt(adt_def, _) = qself_ty.kind { + if let ty::Adt(adt_def, _) = qself_ty.kind() { if adt_def.is_enum() { let variant_def = adt_def .variants @@ -1474,8 +1459,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Find the type of the associated item, and the trait where the associated // item is declared. - let bound = match (&qself_ty.kind, qself_res) { - (_, Res::SelfTy(Some(_), Some(impl_def_id))) => { + let bound = match (&qself_ty.kind(), qself_res) { + (_, Res::SelfTy(Some(_), Some((impl_def_id, _)))) => { // `Self` in an impl of a trait -- we have a concrete self type and a // trait reference. let trait_ref = match tcx.impl_trait_ref(impl_def_id) { @@ -1932,12 +1917,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.prohibit_generics(path.segments); tcx.types.self_param } - Res::SelfTy(_, Some(def_id)) => { + Res::SelfTy(_, Some((def_id, forbid_generic))) => { // `Self` in impl (we know the concrete type). assert_eq!(opt_self_ty, None); self.prohibit_generics(path.segments); // Try to evaluate any array length constants. - self.normalize_ty(span, tcx.at(span).type_of(def_id)) + let normalized_ty = self.normalize_ty(span, tcx.at(span).type_of(def_id)); + if forbid_generic && normalized_ty.needs_subst() { + let mut err = tcx.sess.struct_span_err( + path.span, + "generic `Self` types are currently not permitted in anonymous constants", + ); + if let Some(hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Impl { self_ty, .. }, + .. + })) = tcx.hir().get_if_local(def_id) + { + err.span_note(self_ty.span, "not a concrete type"); + } + err.emit(); + tcx.ty_error() + } else { + normalized_ty + } } Res::Def(DefKind::AssocTy, def_id) => { debug_assert!(path.segments.len() >= 2); @@ -2059,15 +2061,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.normalize_ty(ast_ty.span, array_ty) } hir::TyKind::Typeof(ref _e) => { - struct_span_err!( - tcx.sess, - ast_ty.span, - E0516, - "`typeof` is a reserved keyword but unimplemented" - ) - .span_label(ast_ty.span, "reserved keyword") - .emit(); - + tcx.sess.emit_err(TypeofReservedKeywordUsed { span: ast_ty.span }); tcx.ty_error() } hir::TyKind::Infer => { @@ -2283,13 +2277,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // error. let r = derived_region_bounds[0]; if derived_region_bounds[1..].iter().any(|r1| r != *r1) { - struct_span_err!( - tcx.sess, - span, - E0227, - "ambiguous lifetime bound, explicit lifetime bound required" - ) - .emit(); + tcx.sess.emit_err(AmbiguousLifetimeBound { span }); } Some(r) } diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index afd4413069e..7cb23dc0537 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -1,12 +1,15 @@ use crate::check::coercion::CoerceMany; use crate::check::{Diverges, Expectation, FnCtxt, Needs}; -use rustc_hir as hir; -use rustc_hir::ExprKind; +use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty::Ty; +use rustc_infer::traits::Obligation; +use rustc_middle::ty::{self, ToPredicate, Ty}; use rustc_span::Span; -use rustc_trait_selection::traits::ObligationCauseCode; -use rustc_trait_selection::traits::{IfExpressionCause, MatchExpressionArmCause, ObligationCause}; +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, +}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_match( @@ -14,7 +17,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, scrut: &'tcx hir::Expr<'tcx>, arms: &'tcx [hir::Arm<'tcx>], - expected: Expectation<'tcx>, + orig_expected: Expectation<'tcx>, match_src: hir::MatchSource, ) -> Ty<'tcx> { let tcx = self.tcx; @@ -22,7 +25,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { use hir::MatchSource::*; let (source_if, if_no_else, force_scrutinee_bool) = match match_src { IfDesugar { contains_else_clause } => (true, !contains_else_clause, true), - IfLetDesugar { contains_else_clause } => (true, !contains_else_clause, false), + IfLetDesugar { contains_else_clause, .. } => (true, !contains_else_clause, false), WhileDesugar => (false, false, true), _ => (false, false, false), }; @@ -69,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // type in that case) let mut all_arms_diverge = Diverges::WarnedAlways; - let expected = expected.adjust_for_branches(self); + let expected = orig_expected.adjust_for_branches(self); let mut coercion = { let coerce_first = match expected { @@ -112,6 +115,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_with_expectation(&arm.body, expected) }; all_arms_diverge &= self.diverges.get(); + + // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` + // we check if the different arms would work with boxed trait objects instead and + // provide a structured suggestion in that case. + let opt_suggest_box_span = match ( + orig_expected, + self.ret_coercion_impl_trait.map(|ty| (self.body_id.owner, ty)), + ) { + (Expectation::ExpectHasType(expected), Some((id, ty))) + if self.in_tail_expr && self.can_coerce(arm_ty, expected) => + { + let impl_trait_ret_ty = self.infcx.instantiate_opaque_types( + id, + self.body_id, + self.param_env, + &ty, + arm.body.span, + ); + let mut suggest_box = !impl_trait_ret_ty.obligations.is_empty(); + for o in impl_trait_ret_ty.obligations { + match o.predicate.skip_binders_unchecked() { + ty::PredicateAtom::Trait(t, constness) => { + let pred = ty::PredicateAtom::Trait( + ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: t.def_id(), + substs: self.infcx.tcx.mk_substs_trait(arm_ty, &[]), + }, + }, + constness, + ); + let obl = Obligation::new( + o.cause.clone(), + self.param_env, + pred.to_predicate(self.infcx.tcx), + ); + suggest_box &= self.infcx.predicate_must_hold_modulo_regions(&obl); + if !suggest_box { + // We've encountered some obligation that didn't hold, so the + // return expression can't just be boxed. We don't need to + // evaluate the rest of the obligations. + break; + } + } + _ => {} + } + } + // If all the obligations hold (or there are no obligations) the tail expression + // we can suggest to return a boxed trait object instead of an opaque type. + if suggest_box { self.ret_type_span } else { None } + } + _ => None, + }; + if source_if { let then_expr = &arms[0].body; match (i, if_no_else) { @@ -119,7 +176,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (_, true) => {} // Handled above to avoid duplicated type errors (#60254). (_, _) => { let then_ty = prior_arm_ty.unwrap(); - let cause = self.if_cause(expr.span, then_expr, &arm.body, then_ty, arm_ty); + let cause = self.if_cause( + expr.span, + then_expr, + &arm.body, + then_ty, + arm_ty, + opt_suggest_box_span, + ); coercion.coerce(self, &cause, &arm.body, arm_ty); } } @@ -142,6 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prior_arms: other_arms.clone(), last_ty: prior_arm_ty.unwrap(), scrut_hir_id: scrut.hir_id, + opt_suggest_box_span, }), ), }; @@ -266,6 +331,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else_expr: &'tcx hir::Expr<'tcx>, then_ty: Ty<'tcx>, else_ty: Ty<'tcx>, + opt_suggest_box_span: Option<Span>, ) -> ObligationCause<'tcx> { let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) { // The `if`/`else` isn't in one line in the output, include some context to make it @@ -353,8 +419,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error_sp, ObligationCauseCode::IfExpression(box IfExpressionCause { then: then_sp, + else_sp: error_sp, outer: outer_sp, semicolon: remove_semicolon, + opt_suggest_box_span, }), ) } diff --git a/compiler/rustc_typeck/src/check/autoderef.rs b/compiler/rustc_typeck/src/check/autoderef.rs index 97d2b3e5a8e..59c366ad7d7 100644 --- a/compiler/rustc_typeck/src/check/autoderef.rs +++ b/compiler/rustc_typeck/src/check/autoderef.rs @@ -12,7 +12,18 @@ use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> { - Autoderef::new(self, self.param_env, self.body_id, span, base_ty) + Autoderef::new(self, self.param_env, self.body_id, span, base_ty, span) + } + + /// Like `autoderef`, but provides a custom `Span` to use for calls to + /// an overloaded `Deref` operator + pub fn autoderef_overloaded_span( + &'a self, + span: Span, + base_ty: Ty<'tcx>, + overloaded_span: Span, + ) -> Autoderef<'a, 'tcx> { + Autoderef::new(self, self.param_env, self.body_id, span, base_ty, overloaded_span) } pub fn try_overloaded_deref( @@ -43,8 +54,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.try_overloaded_deref(autoderef.span(), source).and_then( |InferOk { value: method, obligations: o }| { obligations.extend(o); - if let ty::Ref(region, _, mutbl) = method.sig.output().kind { - Some(OverloadedDeref { region, mutbl }) + if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { + Some(OverloadedDeref { + region, + mutbl, + span: autoderef.overloaded_span(), + }) } else { None } diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 4ba64035ca4..740783aeb9d 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -114,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // If the callee is a bare function or a closure, then we're all set. - match adjusted_ty.kind { + match *adjusted_ty.kind() { ty::FnDef(..) | ty::FnPtr(_) => { let adjustments = self.adjust_steps(autoderef); self.apply_adjustments(callee_expr, adjustments); @@ -223,12 +223,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if borrow { // Check for &self vs &mut self in the method signature. Since this is either // the Fn or FnMut trait, it should be one of those. - let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind - { - (r, mutbl) - } else { - span_bug!(call_expr.span, "input to call/call_mut is not a ref?"); - }; + let (region, mutbl) = + if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind() { + (r, mutbl) + } else { + span_bug!(call_expr.span, "input to call/call_mut is not a ref?"); + }; let mutbl = match mutbl { hir::Mutability::Not => AutoBorrowMutability::Not, @@ -285,7 +285,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let (fn_sig, def_span) = match callee_ty.kind { + let (fn_sig, def_span) = match *callee_ty.kind() { ty::FnDef(def_id, _) => { (callee_ty.fn_sig(self.tcx), self.tcx.hir().span_if_local(def_id)) } diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index e41314e8ab0..5c2bdb86f76 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -97,7 +97,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ok(Some(PointerKind::Thin)); } - Ok(match t.kind { + Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), ty::Dynamic(ref tty, ..) => Some(PointerKind::Vtable(tty.principal_def_id())), ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { @@ -203,7 +203,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // For better error messages, check for some obviously unsized // cases now. We do a more thorough check at the end, once // inference is more completely known. - match cast_ty.kind { + match cast_ty.kind() { ty::Dynamic(..) | ty::Slice(..) => { check.report_cast_to_unsized_type(fcx); Err(ErrorReported) @@ -348,7 +348,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { fcx.ty_to_string(self.cast_ty) ); let mut sugg = None; - if let ty::Ref(reg, _, mutbl) = self.cast_ty.kind { + if let ty::Ref(reg, _, mutbl) = *self.cast_ty.kind() { if fcx .try_coerce( self.expr, @@ -370,7 +370,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { Applicability::MachineApplicable, ); } else if !matches!( - self.cast_ty.kind, + self.cast_ty.kind(), ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) ) { let mut label = true; @@ -474,7 +474,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { fcx.resolve_vars_if_possible(&self.expr_ty), tstr ); - match self.expr_ty.kind { + match self.expr_ty.kind() { ty::Ref(_, _, mt) => { let mtstr = mt.prefix_str(); if self.cast_ty.is_trait() { @@ -602,7 +602,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Some(t_from), Some(t_cast)) => (t_from, t_cast), // Function item types may need to be reified before casts. (None, Some(t_cast)) => { - match self.expr_ty.kind { + match *self.expr_ty.kind() { ty::FnDef(..) => { // Attempt a coercion to a fn pointer type. let f = fcx.normalize_associated_types_in( @@ -629,7 +629,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // a cast. ty::Ref(_, inner_ty, mutbl) => { return match t_cast { - Int(_) | Float => match inner_ty.kind { + Int(_) | Float => match *inner_ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Float(_) @@ -768,7 +768,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // array-ptr-cast. if m_expr.mutbl == hir::Mutability::Not && m_cast.mutbl == hir::Mutability::Not { - if let ty::Array(ety, _) = m_expr.ty.kind { + if let ty::Array(ety, _) = m_expr.ty.kind() { // Due to the limitations of LLVM global constants, // region pointers end up pointing at copies of // vector elements instead of the original values. @@ -817,7 +817,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - if let ty::Adt(d, _) = self.expr_ty.kind { + if let ty::Adt(d, _) = self.expr_ty.kind() { if d.has_dtor(fcx.tcx) { fcx.tcx.struct_span_lint_hir( lint::builtin::CENUM_IMPL_DROP_CAST, diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs new file mode 100644 index 00000000000..2daa0354acb --- /dev/null +++ b/compiler/rustc_typeck/src/check/check.rs @@ -0,0 +1,1344 @@ +use super::coercion::CoerceMany; +use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; +use super::*; + +use rustc_attr as attr; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{ItemKind, Node}; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::RegionVariableOrigin; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::util::{Discr, IntTypeExt, Representability}; +use rustc_middle::ty::{self, RegionKind, ToPredicate, Ty, TyCtxt}; +use rustc_session::config::EntryFnType; +use rustc_span::symbol::sym; +use rustc_span::{self, MultiSpan, Span}; +use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::{self, ObligationCauseCode}; + +pub fn check_wf_new(tcx: TyCtxt<'_>) { + let visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx); + tcx.hir().krate().par_visit_all_item_likes(&visit); +} + +pub(super) fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) { + if !tcx.sess.target.target.is_abi_supported(abi) { + struct_span_err!( + tcx.sess, + span, + E0570, + "The ABI `{}` is not supported for the current target", + abi + ) + .emit() + } +} + +/// Helper used for fns and closures. Does the grungy work of checking a function +/// body and returns the function context used for that purpose, since in the case of a fn item +/// there is still a bit more to do. +/// +/// * ... +/// * inherited: other fields inherited from the enclosing fn (if any) +pub(super) fn check_fn<'a, 'tcx>( + inherited: &'a Inherited<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + fn_sig: ty::FnSig<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + fn_id: hir::HirId, + body: &'tcx hir::Body<'tcx>, + can_be_generator: Option<hir::Movability>, +) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) { + let mut fn_sig = fn_sig; + + debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env); + + // Create the function context. This is either derived from scratch or, + // in the case of closures, based on the outer context. + let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); + *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id); + + let tcx = fcx.tcx; + let sess = tcx.sess; + let hir = tcx.hir(); + + let declared_ret_ty = fn_sig.output(); + + let revealed_ret_ty = + fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty, decl.output.span()); + debug!("check_fn: declared_ret_ty: {}, revealed_ret_ty: {}", declared_ret_ty, revealed_ret_ty); + fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty))); + fcx.ret_type_span = Some(decl.output.span()); + if let ty::Opaque(..) = declared_ret_ty.kind() { + fcx.ret_coercion_impl_trait = Some(declared_ret_ty); + } + fn_sig = tcx.mk_fn_sig( + fn_sig.inputs().iter().cloned(), + revealed_ret_ty, + fn_sig.c_variadic, + fn_sig.unsafety, + fn_sig.abi, + ); + + let span = body.value.span; + + fn_maybe_err(tcx, span, fn_sig.abi); + + if body.generator_kind.is_some() && can_be_generator.is_some() { + let yield_ty = fcx + .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); + fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); + + // Resume type defaults to `()` if the generator has no argument. + let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit()); + + fcx.resume_yield_tys = Some((resume_ty, yield_ty)); + } + + let outer_def_id = tcx.closure_base_def_id(hir.local_def_id(fn_id).to_def_id()).expect_local(); + let outer_hir_id = hir.local_def_id_to_hir_id(outer_def_id); + GatherLocalsVisitor::new(&fcx, outer_hir_id).visit_body(body); + + // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` + // (as it's created inside the body itself, not passed in from outside). + let maybe_va_list = if fn_sig.c_variadic { + let span = body.params.last().unwrap().span; + let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span)); + let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); + + Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()])) + } else { + None + }; + + // Add formal parameters. + let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); + let inputs_fn = fn_sig.inputs().iter().copied(); + for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { + // Check the pattern. + let ty_span = try { inputs_hir?.get(idx)?.span }; + fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); + + // Check that argument is Sized. + // The check for a non-trivial pattern is a hack to avoid duplicate warnings + // for simple cases like `fn foo(x: Trait)`, + // where we would error once on the parameter as a whole, and once on the binding `x`. + if param.pat.simple_ident().is_none() && !tcx.features().unsized_locals { + fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); + } + + fcx.write_ty(param.hir_id, param_ty); + } + + inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); + + fcx.in_tail_expr = true; + if let ty::Dynamic(..) = declared_ret_ty.kind() { + // FIXME: We need to verify that the return type is `Sized` after the return expression has + // been evaluated so that we have types available for all the nodes being returned, but that + // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this + // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, + // while keeping the current ordering we will ignore the tail expression's type because we + // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` + // because we will trigger "unreachable expression" lints unconditionally. + // Because of all of this, we perform a crude check to know whether the simplest `!Sized` + // case that a newcomer might make, returning a bare trait, and in that case we populate + // the tail expression's type so that the suggestion will be correct, but ignore all other + // possible cases. + fcx.check_expr(&body.value); + fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); + tcx.sess.delay_span_bug(decl.output.span(), "`!Sized` return type"); + } else { + fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); + fcx.check_return_expr(&body.value); + } + fcx.in_tail_expr = false; + + // We insert the deferred_generator_interiors entry after visiting the body. + // This ensures that all nested generators appear before the entry of this generator. + // resolve_generator_interiors relies on this property. + let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) { + let interior = fcx + .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }); + fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind)); + + let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); + Some(GeneratorTypes { + resume_ty, + yield_ty, + interior, + movability: can_be_generator.unwrap(), + }) + } else { + None + }; + + // Finalize the return check by taking the LUB of the return types + // we saw and assigning it to the expected return type. This isn't + // really expected to fail, since the coercions would have failed + // earlier when trying to find a LUB. + // + // However, the behavior around `!` is sort of complex. In the + // event that the `actual_return_ty` comes back as `!`, that + // indicates that the fn either does not return or "returns" only + // values of type `!`. In this case, if there is an expected + // return type that is *not* `!`, that should be ok. But if the + // return type is being inferred, we want to "fallback" to `!`: + // + // let x = move || panic!(); + // + // To allow for that, I am creating a type variable with diverging + // fallback. This was deemed ever so slightly better than unifying + // the return value with `!` because it allows for the caller to + // make more assumptions about the return type (e.g., they could do + // + // let y: Option<u32> = Some(x()); + // + // which would then cause this return type to become `u32`, not + // `!`). + let coercion = fcx.ret_coercion.take().unwrap().into_inner(); + let mut actual_return_ty = coercion.complete(&fcx); + if actual_return_ty.is_never() { + actual_return_ty = fcx.next_diverging_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::DivergingFn, + span, + }); + } + fcx.demand_suptype(span, revealed_ret_ty, actual_return_ty); + + // Check that the main return type implements the termination trait. + if let Some(term_id) = tcx.lang_items().termination() { + if let Some((def_id, EntryFnType::Main)) = tcx.entry_fn(LOCAL_CRATE) { + let main_id = hir.local_def_id_to_hir_id(def_id); + if main_id == fn_id { + let substs = tcx.mk_substs_trait(declared_ret_ty, &[]); + let trait_ref = ty::TraitRef::new(term_id, substs); + let return_ty_span = decl.output.span(); + let cause = traits::ObligationCause::new( + return_ty_span, + fn_id, + ObligationCauseCode::MainFunctionType, + ); + + inherited.register_predicate(traits::Obligation::new( + cause, + param_env, + trait_ref.without_const().to_predicate(tcx), + )); + } + } + } + + // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` + if let Some(panic_impl_did) = tcx.lang_items().panic_impl() { + if panic_impl_did == hir.local_def_id(fn_id).to_def_id() { + if let Some(panic_info_did) = tcx.lang_items().panic_info() { + if *declared_ret_ty.kind() != ty::Never { + sess.span_err(decl.output.span(), "return type should be `!`"); + } + + let inputs = fn_sig.inputs(); + let span = hir.span(fn_id); + if inputs.len() == 1 { + let arg_is_panic_info = match *inputs[0].kind() { + ty::Ref(region, ty, mutbl) => match *ty.kind() { + ty::Adt(ref adt, _) => { + adt.did == panic_info_did + && mutbl == hir::Mutability::Not + && *region != RegionKind::ReStatic + } + _ => false, + }, + _ => false, + }; + + if !arg_is_panic_info { + sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`"); + } + + if let Node::Item(item) = hir.get(fn_id) { + if let ItemKind::Fn(_, ref generics, _) = item.kind { + if !generics.params.is_empty() { + sess.span_err(span, "should have no type parameters"); + } + } + } + } else { + let span = sess.source_map().guess_head_span(span); + sess.span_err(span, "function should have one argument"); + } + } else { + sess.err("language item required, but not found: `panic_info`"); + } + } + } + + // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !` + if let Some(alloc_error_handler_did) = tcx.lang_items().oom() { + if alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() { + if let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() { + if *declared_ret_ty.kind() != ty::Never { + sess.span_err(decl.output.span(), "return type should be `!`"); + } + + let inputs = fn_sig.inputs(); + let span = hir.span(fn_id); + if inputs.len() == 1 { + let arg_is_alloc_layout = match inputs[0].kind() { + ty::Adt(ref adt, _) => adt.did == alloc_layout_did, + _ => false, + }; + + if !arg_is_alloc_layout { + sess.span_err(decl.inputs[0].span, "argument should be `Layout`"); + } + + if let Node::Item(item) = hir.get(fn_id) { + if let ItemKind::Fn(_, ref generics, _) = item.kind { + if !generics.params.is_empty() { + sess.span_err( + span, + "`#[alloc_error_handler]` function should have no type \ + parameters", + ); + } + } + } + } else { + let span = sess.source_map().guess_head_span(span); + sess.span_err(span, "function should have one argument"); + } + } else { + sess.err("language item required, but not found: `alloc_layout`"); + } + } + } + + (fcx, gen_ty) +} + +pub(super) fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { + let def_id = tcx.hir().local_def_id(id); + let def = tcx.adt_def(def_id); + def.destructor(tcx); // force the destructor to be evaluated + check_representable(tcx, span, def_id); + + if def.repr.simd() { + check_simd(tcx, span, def_id); + } + + check_transparent(tcx, span, def); + check_packed(tcx, span, def); +} + +pub(super) fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { + let def_id = tcx.hir().local_def_id(id); + let def = tcx.adt_def(def_id); + def.destructor(tcx); // force the destructor to be evaluated + check_representable(tcx, span, def_id); + check_transparent(tcx, span, def); + check_union_fields(tcx, span, def_id); + check_packed(tcx, span, def); +} + +/// When the `#![feature(untagged_unions)]` gate is active, +/// check that the fields of the `union` does not contain fields that need dropping. +pub(super) fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { + let item_type = tcx.type_of(item_def_id); + if let ty::Adt(def, substs) = item_type.kind() { + assert!(def.is_union()); + let fields = &def.non_enum_variant().fields; + 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) { + 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") + .emit(); + return false; + } + } + } else { + span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind()); + } + true +} + +/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` +/// projections that would result in "inheriting lifetimes". +pub(super) fn check_opaque<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + substs: SubstsRef<'tcx>, + span: Span, + origin: &hir::OpaqueTyOrigin, +) { + check_opaque_for_inheriting_lifetimes(tcx, def_id, span); + check_opaque_for_cycles(tcx, def_id, substs, span, origin); +} + +/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result +/// in "inheriting lifetimes". +pub(super) fn check_opaque_for_inheriting_lifetimes( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + span: Span, +) { + let item = tcx.hir().expect_item(tcx.hir().local_def_id_to_hir_id(def_id)); + debug!( + "check_opaque_for_inheriting_lifetimes: def_id={:?} span={:?} item={:?}", + def_id, span, item + ); + + #[derive(Debug)] + struct ProhibitOpaqueVisitor<'tcx> { + opaque_identity_ty: Ty<'tcx>, + generics: &'tcx ty::Generics, + ty: Option<Ty<'tcx>>, + }; + + impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t); + if t != self.opaque_identity_ty && t.super_visit_with(self) { + self.ty = Some(t); + return true; + } + false + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + debug!("check_opaque_for_inheriting_lifetimes: (visit_region) r={:?}", r); + if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r { + return *index < self.generics.parent_count as u32; + } + + r.super_visit_with(self) + } + + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + if let ty::ConstKind::Unevaluated(..) = c.val { + // FIXME(#72219) We currenctly don't detect lifetimes within substs + // which would violate this check. Even though the particular substitution is not used + // within the const, this should still be fixed. + return false; + } + c.super_visit_with(self) + } + } + + if let ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn, + .. + }) = item.kind + { + let mut visitor = ProhibitOpaqueVisitor { + opaque_identity_ty: tcx.mk_opaque( + def_id.to_def_id(), + InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), + ), + generics: tcx.generics_of(def_id), + ty: None, + }; + let prohibit_opaque = tcx + .predicates_of(def_id) + .predicates + .iter() + .any(|(predicate, _)| predicate.visit_with(&mut visitor)); + debug!( + "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor={:?}", + prohibit_opaque, visitor + ); + + if prohibit_opaque { + let is_async = match item.kind { + ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin { + hir::OpaqueTyOrigin::AsyncFn => true, + _ => false, + }, + _ => unreachable!(), + }; + + let mut err = struct_span_err!( + tcx.sess, + span, + E0760, + "`{}` return type cannot contain a projection or `Self` that references lifetimes from \ + a parent scope", + if is_async { "async fn" } else { "impl Trait" }, + ); + + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) { + if snippet == "Self" { + if let Some(ty) = visitor.ty { + err.span_suggestion( + span, + "consider spelling out the type instead", + format!("{:?}", ty), + Applicability::MaybeIncorrect, + ); + } + } + } + err.emit(); + } + } +} + +/// Checks that an opaque type does not contain cycles. +pub(super) fn check_opaque_for_cycles<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + substs: SubstsRef<'tcx>, + span: Span, + origin: &hir::OpaqueTyOrigin, +) { + if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs) + { + match origin { + hir::OpaqueTyOrigin::AsyncFn => async_opaque_type_cycle_error(tcx, span), + hir::OpaqueTyOrigin::Binding => { + binding_opaque_type_cycle_error(tcx, def_id, span, partially_expanded_type) + } + _ => opaque_type_cycle_error(tcx, def_id, span), + } + } +} + +pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { + debug!( + "check_item_type(it.hir_id={}, it.name={})", + it.hir_id, + tcx.def_path_str(tcx.hir().local_def_id(it.hir_id).to_def_id()) + ); + let _indenter = indenter(); + match it.kind { + // Consts can play a role in type-checking, so they are included here. + hir::ItemKind::Static(..) => { + let def_id = tcx.hir().local_def_id(it.hir_id); + tcx.ensure().typeck(def_id); + maybe_check_static_with_link_section(tcx, def_id, it.span); + } + hir::ItemKind::Const(..) => { + tcx.ensure().typeck(tcx.hir().local_def_id(it.hir_id)); + } + hir::ItemKind::Enum(ref enum_definition, _) => { + check_enum(tcx, it.span, &enum_definition.variants, it.hir_id); + } + hir::ItemKind::Fn(..) => {} // entirely within check_item_body + hir::ItemKind::Impl { ref items, .. } => { + debug!("ItemKind::Impl {} with id {}", it.ident, it.hir_id); + let impl_def_id = tcx.hir().local_def_id(it.hir_id); + if let Some(impl_trait_ref) = tcx.impl_trait_ref(impl_def_id) { + check_impl_items_against_trait(tcx, it.span, impl_def_id, impl_trait_ref, items); + let trait_def_id = impl_trait_ref.def_id; + check_on_unimplemented(tcx, trait_def_id, it); + } + } + hir::ItemKind::Trait(_, _, _, _, ref items) => { + let def_id = tcx.hir().local_def_id(it.hir_id); + check_on_unimplemented(tcx, def_id.to_def_id(), it); + + for item in items.iter() { + let item = tcx.hir().trait_item(item.id); + if let hir::TraitItemKind::Fn(sig, _) = &item.kind { + let abi = sig.header.abi; + fn_maybe_err(tcx, item.ident.span, abi); + } + } + } + hir::ItemKind::Struct(..) => { + check_struct(tcx, it.hir_id, it.span); + } + hir::ItemKind::Union(..) => { + check_union(tcx, it.hir_id, it.span); + } + hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { + // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting + // `async-std` (and `pub async fn` in general). + // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! + // See https://github.com/rust-lang/rust/issues/75100 + if !tcx.sess.opts.actually_rustdoc { + let def_id = tcx.hir().local_def_id(it.hir_id); + + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + check_opaque(tcx, def_id, substs, it.span, &origin); + } + } + hir::ItemKind::TyAlias(..) => { + let def_id = tcx.hir().local_def_id(it.hir_id); + let pty_ty = tcx.type_of(def_id); + let generics = tcx.generics_of(def_id); + check_type_params_are_used(tcx, &generics, pty_ty); + } + hir::ItemKind::ForeignMod(ref m) => { + check_abi(tcx, it.span, m.abi); + + if m.abi == Abi::RustIntrinsic { + for item in m.items { + intrinsic::check_intrinsic_type(tcx, item); + } + } else if m.abi == Abi::PlatformIntrinsic { + for item in m.items { + intrinsic::check_platform_intrinsic_type(tcx, item); + } + } else { + for item in m.items { + let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id)); + let own_counts = generics.own_counts(); + if generics.params.len() - own_counts.lifetimes != 0 { + let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) { + (_, 0) => ("type", "types", Some("u32")), + // We don't specify an example value, because we can't generate + // a valid value for any type. + (0, _) => ("const", "consts", None), + _ => ("type or const", "types or consts", None), + }; + struct_span_err!( + tcx.sess, + item.span, + E0044, + "foreign items may not have {} parameters", + kinds, + ) + .span_label(item.span, &format!("can't have {} parameters", kinds)) + .help( + // FIXME: once we start storing spans for type arguments, turn this + // into a suggestion. + &format!( + "replace the {} parameters with concrete {}{}", + kinds, + kinds_pl, + egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(), + ), + ) + .emit(); + } + + if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.kind { + require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span); + } + } + } + } + _ => { /* nothing to do */ } + } +} + +pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, trait_def_id: DefId, item: &hir::Item<'_>) { + let item_def_id = tcx.hir().local_def_id(item.hir_id); + // an error would be reported if this fails. + let _ = traits::OnUnimplementedDirective::of_item(tcx, trait_def_id, item_def_id.to_def_id()); +} + +pub(super) fn check_specialization_validity<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def: &ty::TraitDef, + trait_item: &ty::AssocItem, + impl_id: DefId, + impl_item: &hir::ImplItem<'_>, +) { + let kind = match impl_item.kind { + hir::ImplItemKind::Const(..) => ty::AssocKind::Const, + hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, + hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type, + }; + + let ancestors = match trait_def.ancestors(tcx, impl_id) { + Ok(ancestors) => ancestors, + Err(_) => return, + }; + let mut ancestor_impls = ancestors + .skip(1) + .filter_map(|parent| { + if parent.is_from_trait() { + None + } else { + Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id))) + } + }) + .peekable(); + + if ancestor_impls.peek().is_none() { + // No parent, nothing to specialize. + return; + } + + let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| { + match parent_item { + // Parent impl exists, and contains the parent item we're trying to specialize, but + // doesn't mark it `default`. + Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => { + Some(Err(parent_impl.def_id())) + } + + // Parent impl contains item and makes it specializable. + Some(_) => Some(Ok(())), + + // Parent impl doesn't mention the item. This means it's inherited from the + // grandparent. In that case, if parent is a `default impl`, inherited items use the + // "defaultness" from the grandparent, else they are final. + None => { + if tcx.impl_defaultness(parent_impl.def_id()).is_default() { + None + } else { + Some(Err(parent_impl.def_id())) + } + } + } + }); + + // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the + // item. This is allowed, the item isn't actually getting specialized here. + let result = opt_result.unwrap_or(Ok(())); + + if let Err(parent_impl) = result { + report_forbidden_specialization(tcx, impl_item, parent_impl); + } +} + +pub(super) fn check_impl_items_against_trait<'tcx>( + tcx: TyCtxt<'tcx>, + full_impl_span: Span, + impl_id: LocalDefId, + impl_trait_ref: ty::TraitRef<'tcx>, + impl_item_refs: &[hir::ImplItemRef<'_>], +) { + let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); + + // If the trait reference itself is erroneous (so the compilation is going + // to fail), skip checking the items here -- the `impl_item` table in `tcx` + // isn't populated for such impls. + if impl_trait_ref.references_error() { + return; + } + + // Negative impls are not expected to have any items + match tcx.impl_polarity(impl_id) { + ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {} + ty::ImplPolarity::Negative => { + if let [first_item_ref, ..] = impl_item_refs { + let first_item_span = tcx.hir().impl_item(first_item_ref.id).span; + struct_span_err!( + tcx.sess, + first_item_span, + E0749, + "negative impls cannot have any items" + ) + .emit(); + } + return; + } + } + + // Locate trait definition and items + let trait_def = tcx.trait_def(impl_trait_ref.def_id); + + let impl_items = || impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id)); + + // Check existing impl methods to see if they are both present in trait + // and compatible with trait signature + for impl_item in impl_items() { + let namespace = impl_item.kind.namespace(); + let ty_impl_item = tcx.associated_item(tcx.hir().local_def_id(impl_item.hir_id)); + let ty_trait_item = tcx + .associated_items(impl_trait_ref.def_id) + .find_by_name_and_namespace(tcx, ty_impl_item.ident, namespace, impl_trait_ref.def_id) + .or_else(|| { + // Not compatible, but needed for the error message + tcx.associated_items(impl_trait_ref.def_id) + .filter_by_name(tcx, ty_impl_item.ident, impl_trait_ref.def_id) + .next() + }); + + // Check that impl definition matches trait definition + if let Some(ty_trait_item) = ty_trait_item { + match impl_item.kind { + hir::ImplItemKind::Const(..) => { + // Find associated const definition. + if ty_trait_item.kind == ty::AssocKind::Const { + compare_const_impl( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + ); + } else { + let mut err = struct_span_err!( + tcx.sess, + impl_item.span, + E0323, + "item `{}` is an associated const, \ + which doesn't match its trait `{}`", + ty_impl_item.ident, + impl_trait_ref.print_only_trait_path() + ); + err.span_label(impl_item.span, "does not match trait"); + // We can only get the spans from local trait definition + // Same for E0324 and E0325 + if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) { + err.span_label(trait_span, "item in trait"); + } + err.emit() + } + } + hir::ImplItemKind::Fn(..) => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + if ty_trait_item.kind == ty::AssocKind::Fn { + compare_impl_method( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); + } else { + let mut err = struct_span_err!( + tcx.sess, + impl_item.span, + E0324, + "item `{}` is an associated method, \ + which doesn't match its trait `{}`", + ty_impl_item.ident, + impl_trait_ref.print_only_trait_path() + ); + err.span_label(impl_item.span, "does not match trait"); + if let Some(trait_span) = opt_trait_span { + err.span_label(trait_span, "item in trait"); + } + err.emit() + } + } + hir::ImplItemKind::TyAlias(_) => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + if ty_trait_item.kind == ty::AssocKind::Type { + compare_ty_impl( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); + } else { + let mut err = struct_span_err!( + tcx.sess, + impl_item.span, + E0325, + "item `{}` is an associated type, \ + which doesn't match its trait `{}`", + ty_impl_item.ident, + impl_trait_ref.print_only_trait_path() + ); + err.span_label(impl_item.span, "does not match trait"); + if let Some(trait_span) = opt_trait_span { + err.span_label(trait_span, "item in trait"); + } + err.emit() + } + } + } + + check_specialization_validity( + tcx, + trait_def, + &ty_trait_item, + impl_id.to_def_id(), + impl_item, + ); + } + } + + // Check for missing items from trait + let mut missing_items = Vec::new(); + if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { + for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { + let is_implemented = ancestors + .leaf_def(tcx, trait_item.ident, trait_item.kind) + .map(|node_item| !node_item.defining_node.is_from_trait()) + .unwrap_or(false); + + if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { + if !trait_item.defaultness.has_value() { + missing_items.push(*trait_item); + } + } + } + } + + if !missing_items.is_empty() { + missing_items_err(tcx, impl_span, &missing_items, full_impl_span); + } +} + +/// Checks whether a type can be represented in memory. In particular, it +/// identifies types that contain themselves without indirection through a +/// pointer, which would mean their size is unbounded. +pub(super) fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool { + let rty = tcx.type_of(item_def_id); + + // Check that it is possible to represent this type. This call identifies + // (1) types that contain themselves and (2) types that contain a different + // recursive type. It is only necessary to throw an error on those that + // contain themselves. For case 2, there must be an inner type that will be + // caught by case 1. + match rty.is_representable(tcx, sp) { + Representability::SelfRecursive(spans) => { + recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans); + return false; + } + Representability::Representable | Representability::ContainsRecursive => (), + } + true +} + +pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { + let t = tcx.type_of(def_id); + if let ty::Adt(def, substs) = t.kind() { + if def.is_struct() { + let fields = &def.non_enum_variant().fields; + if fields.is_empty() { + struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit(); + return; + } + let e = fields[0].ty(tcx, substs); + if !fields.iter().all(|f| f.ty(tcx, substs) == e) { + struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous") + .span_label(sp, "SIMD elements must have the same type") + .emit(); + return; + } + match e.kind() { + ty::Param(_) => { /* struct<T>(T, T, T, T) is ok */ } + _ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ } + _ => { + struct_span_err!( + tcx.sess, + sp, + E0077, + "SIMD vector element type should be machine type" + ) + .emit(); + return; + } + } + } + } +} + +pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: &ty::AdtDef) { + let repr = def.repr; + if repr.packed() { + for attr in tcx.get_attrs(def.did).iter() { + for r in attr::find_repr_attrs(&tcx.sess, attr) { + if let attr::ReprPacked(pack) = r { + if let Some(repr_pack) = repr.pack { + if pack as u64 != repr_pack.bytes() { + struct_span_err!( + tcx.sess, + sp, + E0634, + "type has conflicting packed representation hints" + ) + .emit(); + } + } + } + } + } + if repr.align.is_some() { + struct_span_err!( + tcx.sess, + sp, + E0587, + "type has conflicting packed and align representation hints" + ) + .emit(); + } else { + if let Some(def_spans) = check_packed_inner(tcx, def.did, &mut vec![]) { + let mut err = struct_span_err!( + tcx.sess, + sp, + E0588, + "packed type cannot transitively contain a `#[repr(align)]` type" + ); + + err.span_note( + tcx.def_span(def_spans[0].0), + &format!( + "`{}` has a `#[repr(align)]` attribute", + tcx.item_name(def_spans[0].0) + ), + ); + + if def_spans.len() > 2 { + let mut first = true; + for (adt_def, span) in def_spans.iter().skip(1).rev() { + let ident = tcx.item_name(*adt_def); + err.span_note( + *span, + &if first { + format!( + "`{}` contains a field of type `{}`", + tcx.type_of(def.did), + ident + ) + } else { + format!("...which contains a field of type `{}`", ident) + }, + ); + first = false; + } + } + + err.emit(); + } + } + } +} + +pub(super) fn check_packed_inner( + tcx: TyCtxt<'_>, + def_id: DefId, + stack: &mut Vec<DefId>, +) -> Option<Vec<(DefId, Span)>> { + if let ty::Adt(def, substs) = tcx.type_of(def_id).kind() { + if def.is_struct() || def.is_union() { + if def.repr.align.is_some() { + return Some(vec![(def.did, DUMMY_SP)]); + } + + stack.push(def_id); + for field in &def.non_enum_variant().fields { + if let ty::Adt(def, _) = field.ty(tcx, substs).kind() { + if !stack.contains(&def.did) { + if let Some(mut defs) = check_packed_inner(tcx, def.did, stack) { + defs.push((def.did, field.ident.span)); + return Some(defs); + } + } + } + } + stack.pop(); + } + } + + None +} + +pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: &'tcx ty::AdtDef) { + if !adt.repr.transparent() { + return; + } + let sp = tcx.sess.source_map().guess_head_span(sp); + + if adt.is_union() && !tcx.features().transparent_unions { + feature_err( + &tcx.sess.parse_sess, + sym::transparent_unions, + sp, + "transparent unions are unstable", + ) + .emit(); + } + + if adt.variants.len() != 1 { + bad_variant_count(tcx, adt, sp, adt.did); + if adt.variants.is_empty() { + // Don't bother checking the fields. No variants (and thus no fields) exist. + return; + } + } + + // For each field, figure out if it's known to be a ZST and align(1) + let field_infos = adt.all_fields().map(|field| { + let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did)); + let param_env = tcx.param_env(field.did); + let layout = tcx.layout_of(param_env.and(ty)); + // We are currently checking the type this field came from, so it must be local + let span = tcx.hir().span_if_local(field.did).unwrap(); + let zst = layout.map(|layout| layout.is_zst()).unwrap_or(false); + let align1 = layout.map(|layout| layout.align.abi.bytes() == 1).unwrap_or(false); + (span, zst, align1) + }); + + let non_zst_fields = + field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None }); + let non_zst_count = non_zst_fields.clone().count(); + if non_zst_count != 1 { + bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp); + } + for (span, zst, align1) in field_infos { + if zst && !align1 { + struct_span_err!( + tcx.sess, + span, + E0691, + "zero-sized field in transparent {} has alignment larger than 1", + adt.descr(), + ) + .span_label(span, "has alignment larger than 1") + .emit(); + } + } +} + +#[allow(trivial_numeric_casts)] +pub fn check_enum<'tcx>( + tcx: TyCtxt<'tcx>, + sp: Span, + vs: &'tcx [hir::Variant<'tcx>], + id: hir::HirId, +) { + let def_id = tcx.hir().local_def_id(id); + let def = tcx.adt_def(def_id); + def.destructor(tcx); // force the destructor to be evaluated + + if vs.is_empty() { + let attributes = tcx.get_attrs(def_id.to_def_id()); + if let Some(attr) = tcx.sess.find_by_name(&attributes, sym::repr) { + struct_span_err!( + tcx.sess, + attr.span, + E0084, + "unsupported representation for zero-variant enum" + ) + .span_label(sp, "zero-variant enum") + .emit(); + } + } + + let repr_type_ty = def.repr.discr_type().to_ty(tcx); + if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 { + if !tcx.features().repr128 { + feature_err( + &tcx.sess.parse_sess, + sym::repr128, + sp, + "repr with 128-bit type is unstable", + ) + .emit(); + } + } + + for v in vs { + if let Some(ref e) = v.disr_expr { + tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id)); + } + } + + if tcx.adt_def(def_id).repr.int.is_none() && tcx.features().arbitrary_enum_discriminant { + let is_unit = |var: &hir::Variant<'_>| match var.data { + hir::VariantData::Unit(..) => true, + _ => false, + }; + + let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); + let has_non_units = vs.iter().any(|var| !is_unit(var)); + let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var)); + let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var)); + + if disr_non_unit || (disr_units && has_non_units) { + let mut err = + struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified"); + err.emit(); + } + } + + let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len()); + for ((_, discr), v) in def.discriminants(tcx).zip(vs) { + // Check for duplicate discriminant values + if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) { + let variant_did = def.variants[VariantIdx::new(i)].def_id; + let variant_i_hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.expect_local()); + let variant_i = tcx.hir().expect_variant(variant_i_hir_id); + let i_span = match variant_i.disr_expr { + Some(ref expr) => tcx.hir().span(expr.hir_id), + None => tcx.hir().span(variant_i_hir_id), + }; + let span = match v.disr_expr { + Some(ref expr) => tcx.hir().span(expr.hir_id), + None => v.span, + }; + struct_span_err!( + tcx.sess, + span, + E0081, + "discriminant value `{}` already exists", + disr_vals[i] + ) + .span_label(i_span, format!("first use of `{}`", disr_vals[i])) + .span_label(span, format!("enum already has `{}`", disr_vals[i])) + .emit(); + } + disr_vals.push(discr); + } + + check_representable(tcx, sp, def_id); + check_transparent(tcx, sp, def); +} + +pub(super) fn check_type_params_are_used<'tcx>( + tcx: TyCtxt<'tcx>, + generics: &ty::Generics, + ty: Ty<'tcx>, +) { + debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty); + + assert_eq!(generics.parent, None); + + if generics.own_counts().types == 0 { + return; + } + + let mut params_used = BitSet::new_empty(generics.params.len()); + + if ty.references_error() { + // If there is already another error, do not emit + // an error for not using a type parameter. + assert!(tcx.sess.has_errors()); + return; + } + + for leaf in ty.walk() { + if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { + if let ty::Param(param) = leaf_ty.kind() { + debug!("found use of ty param {:?}", param); + params_used.insert(param.index); + } + } + } + + for param in &generics.params { + if !params_used.contains(param.index) { + if let ty::GenericParamDefKind::Type { .. } = param.kind { + let span = tcx.def_span(param.def_id); + struct_span_err!( + tcx.sess, + span, + E0091, + "type parameter `{}` is unused", + param.name, + ) + .span_label(span, "unused type parameter") + .emit(); + } + } + } +} + +pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { + tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx }); +} + +pub(super) fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { + wfcheck::check_item_well_formed(tcx, def_id); +} + +pub(super) fn check_trait_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { + wfcheck::check_trait_item(tcx, def_id); +} + +pub(super) fn check_impl_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { + wfcheck::check_impl_item(tcx, def_id); +} + +fn async_opaque_type_cycle_error(tcx: TyCtxt<'tcx>, span: Span) { + struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing") + .span_label(span, "recursive `async fn`") + .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") + .emit(); +} + +/// Emit an error for recursive opaque types. +/// +/// If this is a return `impl Trait`, find the item's return expressions and point at them. For +/// direct recursion this is enough, but for indirect recursion also point at the last intermediary +/// `impl Trait`. +/// +/// If all the return expressions evaluate to `!`, then we explain that the error will go away +/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. +fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { + let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); + + let mut label = false; + if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) { + let typeck_results = tcx.typeck(tcx.hir().local_def_id(hir_id)); + if visitor + .returns + .iter() + .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id)) + .all(|ty| matches!(ty.kind(), ty::Never)) + { + let spans = visitor + .returns + .iter() + .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some()) + .map(|expr| expr.span) + .collect::<Vec<Span>>(); + let span_len = spans.len(); + if span_len == 1 { + err.span_label(spans[0], "this returned value is of `!` type"); + } else { + let mut multispan: MultiSpan = spans.clone().into(); + for span in spans { + multispan + .push_span_label(span, "this returned value is of `!` type".to_string()); + } + err.span_note(multispan, "these returned values have a concrete \"never\" type"); + } + err.help("this error will resolve once the item's body returns a concrete type"); + } else { + let mut seen = FxHashSet::default(); + seen.insert(span); + err.span_label(span, "recursive opaque type"); + label = true; + for (sp, ty) in visitor + .returns + .iter() + .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t))) + .filter(|(_, ty)| !matches!(ty.kind(), ty::Never)) + { + struct VisitTypes(Vec<DefId>); + impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes { + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + match *t.kind() { + ty::Opaque(def, _) => { + self.0.push(def); + false + } + _ => t.super_visit_with(self), + } + } + } + let mut visitor = VisitTypes(vec![]); + ty.visit_with(&mut visitor); + for def_id in visitor.0 { + let ty_span = tcx.def_span(def_id); + if !seen.contains(&ty_span) { + err.span_label(ty_span, &format!("returning this opaque type `{}`", ty)); + seen.insert(ty_span); + } + err.span_label(sp, &format!("returning here with type `{}`", ty)); + } + } + } + } + if !label { + err.span_label(span, "cannot resolve opaque type"); + } + err.emit(); +} diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 97f7e4537ce..8898a545228 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -170,7 +170,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { debug!("deduce_expectations_from_expected_type(expected_ty={:?})", expected_ty); - match expected_ty.kind { + match *expected_ty.kind() { ty::Dynamic(ref object_type, ..) => { let sig = object_type.projection_bounds().find_map(|pb| { let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self); @@ -268,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let arg_param_ty = self.resolve_vars_if_possible(&arg_param_ty); debug!("deduce_sig_from_projection: arg_param_ty={:?}", arg_param_ty); - match arg_param_ty.kind { + match arg_param_ty.kind() { ty::Tuple(tys) => tys.into_iter().map(|k| k.expect_ty()).collect::<Vec<_>>(), _ => return None, } @@ -611,7 +611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // be inferring. let ret_ty = ret_coercion.borrow().expected_ty(); let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); - let ret_vid = match ret_ty.kind { + let ret_vid = match *ret_ty.kind() { ty::Infer(ty::TyVar(ret_vid)) => ret_vid, _ => span_bug!( self.tcx.def_span(expr_def_id), diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index f0802c45ae0..4addee1a4c9 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -37,7 +37,7 @@ use crate::astconv::AstConv; use crate::check::FnCtxt; -use rustc_errors::{struct_span_err, DiagnosticBuilder}; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; @@ -51,7 +51,7 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Ty, TypeAndMut}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; -use rustc_span::{self, Span}; +use rustc_span::{self, BytePos, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; @@ -197,7 +197,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // // Note: does not attempt to resolve type variables we encounter. // See above for details. - match b.kind { + match *b.kind() { ty::RawPtr(mt_b) => { return self.coerce_unsafe_ptr(a, b, mt_b.mutbl); } @@ -207,7 +207,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { _ => {} } - match a.kind { + match *a.kind() { ty::FnDef(..) => { // Function items are coercible to any closure // type; function pointers are not (that would @@ -252,7 +252,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // to type check, we will construct the type that `&M*expr` would // yield. - let (r_a, mt_a) = match a.kind { + let (r_a, mt_a) = match *a.kind() { ty::Ref(r_a, ty, mutbl) => { let mt_a = ty::TypeAndMut { ty, mutbl }; coerce_mutbls(mt_a.mutbl, mutbl_b)?; @@ -415,7 +415,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Now apply the autoref. We have to extract the region out of // the final ref type we got. - let r_borrow = match ty.kind { + let r_borrow = match ty.kind() { ty::Ref(r_borrow, _, _) => r_borrow, _ => span_bug!(span, "expected a ref type, got {:?}", ty), }; @@ -490,7 +490,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // that, at which point we will need extra checks on the target here. // Handle reborrows before selecting `Source: CoerceUnsized<Target>`. - let reborrow = match (&source.kind, &target.kind) { + let reborrow = match (source.kind(), target.kind()) { (&ty::Ref(_, ty_a, mutbl_a), &ty::Ref(_, _, mutbl_b)) => { coerce_mutbls(mutbl_a, mutbl_b)?; @@ -588,7 +588,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { { if unsize_did == trait_pred.def_id() { let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty(); - if let ty::Tuple(..) = unsize_ty.kind { + if let ty::Tuple(..) = unsize_ty.kind() { debug!("coerce_unsized: found unsized tuple coercion"); has_unsized_tuple_coercion = true; } @@ -608,7 +608,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let self_ty = trait_pred.skip_binder().self_ty(); let unsize_ty = trait_pred.skip_binder().trait_ref.substs[1].expect_ty(); debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); - match (&self_ty.kind, &unsize_ty.kind) { + match (&self_ty.kind(), &unsize_ty.kind()) { (ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) if self.type_var_is_sized(*v) => { @@ -672,7 +672,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { F: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, { - if let ty::FnPtr(fn_ty_b) = b.kind { + if let ty::FnPtr(fn_ty_b) = b.kind() { if let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) = (fn_ty_a.unsafety(), fn_ty_b.unsafety()) { @@ -712,7 +712,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let b = self.shallow_resolve(b); debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); - match b.kind { + match b.kind() { ty::FnPtr(b_sig) => { let a_sig = a.fn_sig(self.tcx); // Intrinsics are not coercible to function pointers @@ -721,7 +721,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). - if let ty::FnDef(def_id, _) = a.kind { + if let ty::FnDef(def_id, _) = *a.kind() { if b_sig.unsafety() == hir::Unsafety::Normal && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() { @@ -771,7 +771,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let b = self.shallow_resolve(b); - match b.kind { + match b.kind() { ty::FnPtr(fn_ty) if substs_a.as_closure().upvar_tys().next().is_none() => { // We coerce the closure, which has fn type // `extern "rust-call" fn((arg0,arg1,...)) -> _` @@ -802,7 +802,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ) -> CoerceResult<'tcx> { debug!("coerce_unsafe_ptr(a={:?}, b={:?})", a, b); - let (is_ref, mt_a) = match a.kind { + let (is_ref, mt_a) = match *a.kind() { ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }), ty::RawPtr(mt) => (false, mt), _ => return self.unify_and(a, b, identity), @@ -912,10 +912,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } }; - if is_capturing_closure(&prev_ty.kind) || is_capturing_closure(&new_ty.kind) { + if is_capturing_closure(&prev_ty.kind()) || is_capturing_closure(&new_ty.kind()) { (None, None) } else { - match (&prev_ty.kind, &new_ty.kind) { + match (&prev_ty.kind(), &new_ty.kind()) { (&ty::FnDef(..), &ty::FnDef(..)) => { // Don't reify if the function types have a LUB, i.e., they // are the same function and their parameters have a LUB. @@ -969,12 +969,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Reify both sides and return the reified fn pointer type. let fn_ptr = self.tcx.mk_fn_ptr(sig); - let prev_adjustment = match prev_ty.kind { + let prev_adjustment = match prev_ty.kind() { ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())), ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), _ => unreachable!(), }; - let next_adjustment = match new_ty.kind { + let next_adjustment = match new_ty.kind() { ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())), ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), _ => unreachable!(), @@ -1024,7 +1024,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let noop = match self.typeck_results.borrow().expr_adjustments(expr) { &[Adjustment { kind: Adjust::Deref(_), .. }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl_adj)), .. }] => { - match self.node_ty(expr.hir_id).kind { + match *self.node_ty(expr.hir_id).kind() { ty::Ref(_, _, mt_orig) => { let mutbl_adj: hir::Mutability = mutbl_adj.into(); // Reborrow that we can safely ignore, because @@ -1459,7 +1459,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.borrow().as_ref(), fn_output) { - self.add_impl_trait_explanation(&mut err, fcx, expected, *sp, fn_output); + self.add_impl_trait_explanation(&mut err, cause, fcx, expected, *sp, fn_output); } err } @@ -1467,6 +1467,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fn add_impl_trait_explanation<'a>( &self, err: &mut DiagnosticBuilder<'a>, + cause: &ObligationCause<'tcx>, fcx: &FnCtxt<'a, 'tcx>, expected: Ty<'tcx>, sp: Span, @@ -1501,7 +1502,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if let hir::TyKind::OpaqueDef(..) = ty.kind { let ty = AstConv::ast_ty_to_ty(fcx, ty); // Get the `impl Trait`'s `DefId`. - if let ty::Opaque(def_id, _) = ty.kind { + if let ty::Opaque(def_id, _) = ty.kind() { let hir_id = fcx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); // Get the `impl Trait`'s `Item` so that we can get its trait bounds and // get the `Trait`'s `DefId`. @@ -1523,10 +1524,30 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { }; if has_impl { if is_object_safe { - err.help(&format!( - "you can instead return a boxed trait object using `Box<dyn {}>`", - &snippet[5..] - )); + err.multipart_suggestion( + "you could change the return type to be a boxed trait object", + vec![ + (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()), + (return_sp.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ); + let sugg = vec![sp, cause.span] + .into_iter() + .flat_map(|sp| { + vec![ + (sp.shrink_to_lo(), "Box::new(".to_string()), + (sp.shrink_to_hi(), ")".to_string()), + ] + .into_iter() + }) + .collect::<Vec<_>>(); + err.multipart_suggestion( + "if you change the return type to expect trait objects, box the returned \ + expressions", + sugg, + Applicability::MaybeIncorrect, + ); } else { err.help(&format!( "if the trait `{}` were object safe, you could return a boxed trait object", @@ -1535,14 +1556,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } err.note(trait_obj_msg); } - err.help("alternatively, create a new `enum` with a variant for each returned type"); + err.help("you could instead create a new `enum` with a variant for each returned type"); } fn is_return_ty_unsized(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool { if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id) { if let hir::FnRetTy::Return(ty) = fn_decl.output { let ty = AstConv::ast_ty_to_ty(fcx, ty); - if let ty::Dynamic(..) = ty.kind { + if let ty::Dynamic(..) = ty.kind() { return true; } } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 7adcd7b472e..7aa54e0ebcc 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -1,3 +1,4 @@ +use crate::errors::LifetimesOrBoundsMismatchOnTrait; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -201,11 +202,8 @@ fn compare_predicate_entailment<'tcx>( // The key step here is to update the caller_bounds's predicates to be // the new hybrid bounds we computed. 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, - None, - ); + 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, @@ -366,24 +364,18 @@ fn check_region_bounds_on_impl_item<'tcx>( let item_kind = assoc_item_kind_str(impl_m); let def_span = tcx.sess.source_map().guess_head_span(span); let span = tcx.hir().get_generics(impl_m.def_id).map(|g| g.span).unwrap_or(def_span); - let mut err = struct_span_err!( - tcx.sess, + let generics_span = if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { + let def_sp = tcx.sess.source_map().guess_head_span(sp); + Some(tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp)) + } else { + None + }; + tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { span, - E0195, - "lifetime parameters or bounds on {} `{}` do not match the trait declaration", item_kind, - impl_m.ident, - ); - err.span_label(span, &format!("lifetimes do not match {} in trait", item_kind)); - if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { - let def_sp = tcx.sess.source_map().guess_head_span(sp); - let sp = tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp); - err.span_label( - sp, - &format!("lifetimes in impl do not match this {} in trait", item_kind), - ); - } - err.emit(); + ident: impl_m.ident, + generics_span, + }); return Err(ErrorReported); } @@ -1125,11 +1117,8 @@ fn compare_type_predicate_entailment<'tcx>( debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds); let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id); - let param_env = ty::ParamEnv::new( - tcx.intern_predicates(&hybrid_preds.predicates), - Reveal::UserFacing, - None, - ); + 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_ty.def_id, @@ -1232,7 +1221,7 @@ fn compare_projection_bounds<'tcx>( }) .to_predicate(tcx), ); - ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing, None) + ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing) }; tcx.infer_ctxt().enter(move |infcx| { diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 5dc5480c335..247bbf637ce 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -186,7 +186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, expr_ty: Ty<'tcx>, ) { - if let ty::Adt(expected_adt, substs) = expected.kind { + if let ty::Adt(expected_adt, substs) = expected.kind() { if !expected_adt.is_enum() { return; } @@ -362,15 +362,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn replace_prefix<A, B, C>(&self, s: A, old: B, new: C) -> Option<String> - where - A: AsRef<str>, - B: AsRef<str>, - C: AsRef<str>, - { - let s = s.as_ref(); - let old = old.as_ref(); - if s.starts_with(old) { Some(new.as_ref().to_owned() + &s[old.len()..]) } else { None } + fn replace_prefix(&self, s: &str, old: &str, new: &str) -> Option<String> { + if let Some(stripped) = s.strip_prefix(old) { + Some(new.to_string() + stripped) + } else { + None + } } /// This function is used to determine potential "simple" improvements or users' errors and @@ -413,12 +410,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `ExprKind::DropTemps` is semantically irrelevant for these suggestions. let expr = expr.peel_drop_temps(); - match (&expr.kind, &expected.kind, &checked_ty.kind) { - (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.kind, &check.kind) { + match (&expr.kind, expected.kind(), checked_ty.kind()) { + (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) { (&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(src) = self.replace_prefix(src, "b\"", "\"") { + if let Some(src) = self.replace_prefix(&src, "b\"", "\"") { return Some(( sp, "consider removing the leading `b`", @@ -432,7 +429,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (&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(src) = self.replace_prefix(src, "\"", "b\"") { + if let Some(src) = self.replace_prefix(&src, "\"", "b\"") { return Some(( sp, "consider adding a leading `b`", @@ -557,7 +554,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we may want to suggest removing a `&`. if sm.is_imported(expr.span) { if let Ok(src) = sm.span_to_snippet(sp) { - if let Some(src) = self.replace_prefix(src, "&", "") { + if let Some(src) = self.replace_prefix(&src, "&", "") { return Some(( sp, "consider removing the borrow", @@ -594,7 +591,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match mutbl_a { hir::Mutability::Mut => { if let Some(s) = - self.replace_prefix(src, "&mut ", new_prefix) + self.replace_prefix(&src, "&mut ", &new_prefix) { Some((s, Applicability::MachineApplicable)) } else { @@ -603,7 +600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } hir::Mutability::Not => { if let Some(s) = - self.replace_prefix(src, "&", new_prefix) + self.replace_prefix(&src, "&", &new_prefix) { Some((s, Applicability::Unspecified)) } else { @@ -617,7 +614,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match mutbl_a { hir::Mutability::Mut => { if let Some(s) = - self.replace_prefix(src, "&mut ", new_prefix) + self.replace_prefix(&src, "&mut ", &new_prefix) { Some((s, Applicability::MachineApplicable)) } else { @@ -626,7 +623,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } hir::Mutability::Not => { if let Some(s) = - self.replace_prefix(src, "&", new_prefix) + self.replace_prefix(&src, "&", &new_prefix) { Some((s, Applicability::MachineApplicable)) } else { @@ -774,7 +771,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suffix_suggestion = with_opt_paren(&format_args!( "{}{}", if matches!( - (&expected_ty.kind, &checked_ty.kind), + (&expected_ty.kind(), &checked_ty.kind()), (ty::Int(_) | ty::Uint(_), ty::Float(_)) ) { // Remove fractional part from literal, for example `42.0f32` into `42` @@ -790,7 +787,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let is_negative_int = |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnNeg, ..)); - let is_uint = |ty: Ty<'_>| matches!(ty.kind, ty::Uint(..)); + let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..)); let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id); @@ -857,7 +854,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion(expr.span, msg, suggestion, Applicability::MachineApplicable); }; - match (&expected_ty.kind, &checked_ty.kind) { + match (&expected_ty.kind(), &checked_ty.kind()) { (&ty::Int(ref exp), &ty::Int(ref found)) => { let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width()) { diff --git a/compiler/rustc_typeck/src/check/diverges.rs b/compiler/rustc_typeck/src/check/diverges.rs new file mode 100644 index 00000000000..963a93a95c2 --- /dev/null +++ b/compiler/rustc_typeck/src/check/diverges.rs @@ -0,0 +1,78 @@ +use rustc_span::source_map::DUMMY_SP; +use rustc_span::{self, Span}; +use std::{cmp, ops}; + +/// Tracks whether executing a node may exit normally (versus +/// return/break/panic, which "diverge", leaving dead code in their +/// wake). Tracked semi-automatically (through type variables marked +/// as diverging), with some manual adjustments for control-flow +/// primitives (approximating a CFG). +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum Diverges { + /// Potentially unknown, some cases converge, + /// others require a CFG to determine them. + Maybe, + + /// Definitely known to diverge and therefore + /// not reach the next sibling or its parent. + Always { + /// The `Span` points to the expression + /// that caused us to diverge + /// (e.g. `return`, `break`, etc). + span: Span, + /// In some cases (e.g. a `match` expression + /// where all arms diverge), we may be + /// able to provide a more informative + /// message to the user. + /// If this is `None`, a default message + /// will be generated, which is suitable + /// for most cases. + custom_note: Option<&'static str>, + }, + + /// Same as `Always` but with a reachability + /// warning already emitted. + WarnedAlways, +} + +// Convenience impls for combining `Diverges`. + +impl ops::BitAnd for Diverges { + type Output = Self; + fn bitand(self, other: Self) -> Self { + cmp::min(self, other) + } +} + +impl ops::BitOr for Diverges { + type Output = Self; + fn bitor(self, other: Self) -> Self { + cmp::max(self, other) + } +} + +impl ops::BitAndAssign for Diverges { + fn bitand_assign(&mut self, other: Self) { + *self = *self & other; + } +} + +impl ops::BitOrAssign for Diverges { + fn bitor_assign(&mut self, other: Self) { + *self = *self | other; + } +} + +impl Diverges { + /// Creates a `Diverges::Always` with the provided `span` and the default note message. + pub(super) fn always(span: Span) -> Diverges { + Diverges::Always { span, custom_note: None } + } + + pub(super) fn is_always(self) -> bool { + // Enum comparison ignores the + // contents of fields, so we just + // fill them in with garbage here. + self >= Diverges::Always { span: DUMMY_SP, custom_note: None } + } +} diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 434886538fb..ae94a6df5fd 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -34,7 +34,7 @@ use rustc_trait_selection::traits::{ObligationCause, TraitEngine, TraitEngineExt pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorReported> { let dtor_self_type = tcx.type_of(drop_impl_did); let dtor_predicates = tcx.predicates_of(drop_impl_did); - match dtor_self_type.kind { + match dtor_self_type.kind() { ty::Adt(adt_def, self_to_impl_substs) => { ensure_drop_params_and_item_params_correspond( tcx, diff --git a/compiler/rustc_typeck/src/check/expectation.rs b/compiler/rustc_typeck/src/check/expectation.rs new file mode 100644 index 00000000000..fd6fe1406c8 --- /dev/null +++ b/compiler/rustc_typeck/src/check/expectation.rs @@ -0,0 +1,117 @@ +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{self, Span}; + +use super::Expectation::*; +use super::FnCtxt; + +/// When type-checking an expression, we propagate downward +/// whatever type hint we are able in the form of an `Expectation`. +#[derive(Copy, Clone, Debug)] +pub enum Expectation<'tcx> { + /// We know nothing about what type this expression should have. + NoExpectation, + + /// This expression should have the type given (or some subtype). + ExpectHasType(Ty<'tcx>), + + /// This expression will be cast to the `Ty`. + ExpectCastableToType(Ty<'tcx>), + + /// This rvalue expression will be wrapped in `&` or `Box` and coerced + /// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`. + ExpectRvalueLikeUnsized(Ty<'tcx>), +} + +impl<'a, 'tcx> Expectation<'tcx> { + // Disregard "castable to" expectations because they + // can lead us astray. Consider for example `if cond + // {22} else {c} as u8` -- if we propagate the + // "castable to u8" constraint to 22, it will pick the + // type 22u8, which is overly constrained (c might not + // be a u8). In effect, the problem is that the + // "castable to" expectation is not the tightest thing + // we can say, so we want to drop it in this case. + // The tightest thing we can say is "must unify with + // else branch". Note that in the case of a "has type" + // constraint, this limitation does not hold. + + // If the expected type is just a type variable, then don't use + // an expected type. Otherwise, we might write parts of the type + // when checking the 'then' block which are incompatible with the + // 'else' branch. + pub(super) fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { + match *self { + ExpectHasType(ety) => { + let ety = fcx.shallow_resolve(ety); + if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation } + } + ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety), + _ => NoExpectation, + } + } + + /// Provides an expectation for an rvalue expression given an *optional* + /// hint, which is not required for type safety (the resulting type might + /// be checked higher up, as is the case with `&expr` and `box expr`), but + /// is useful in determining the concrete type. + /// + /// The primary use case is where the expected type is a fat pointer, + /// like `&[isize]`. For example, consider the following statement: + /// + /// let x: &[isize] = &[1, 2, 3]; + /// + /// In this case, the expected type for the `&[1, 2, 3]` expression is + /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the + /// expectation `ExpectHasType([isize])`, that would be too strong -- + /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`. + /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced + /// to the type `&[isize]`. Therefore, we propagate this more limited hint, + /// which still is useful, because it informs integer literals and the like. + /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 + /// for examples of where this comes up,. + pub(super) fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> { + match fcx.tcx.struct_tail_without_normalization(ty).kind() { + ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty), + _ => ExpectHasType(ty), + } + } + + // Resolves `expected` by a single level if it is a variable. If + // there is no expected type or resolution is not possible (e.g., + // no constraints yet present), just returns `None`. + fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { + match self { + NoExpectation => NoExpectation, + ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(&t)), + ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(&t)), + ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(&t)), + } + } + + pub(super) fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> { + match self.resolve(fcx) { + NoExpectation => None, + ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty), + } + } + + /// It sometimes happens that we want to turn an expectation into + /// a **hard constraint** (i.e., something that must be satisfied + /// for the program to type-check). `only_has_type` will return + /// such a constraint, if it exists. + pub(super) fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> { + match self.resolve(fcx) { + ExpectHasType(ty) => Some(ty), + NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None, + } + } + + /// Like `only_has_type`, but instead of returning `None` if no + /// hard constraint exists, creates a fresh type variable. + pub(super) fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> { + self.only_has_type(fcx).unwrap_or_else(|| { + fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }) + }) + } +} diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 1573fb96791..af800eab67a 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -14,8 +14,13 @@ use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExp use crate::check::FnCtxt; use crate::check::Needs; use crate::check::TupleArgumentsFlag::DontTupleArguments; +use crate::errors::{ + FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, + YieldExprOutsideOfGenerator, +}; use crate::type_error_struct; +use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use rustc_ast as ast; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashMap; @@ -296,7 +301,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn check_expr_box(&self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>) -> Ty<'tcx> { - let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind { + let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind() { ty::Adt(def, _) if def.is_box() => Expectation::rvalue_hint(self, ty.boxed_ty()), _ => NoExpectation, }); @@ -346,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::UnOp::UnNot => { let result = self.check_user_unop(expr, oprnd_t, unop); // If it's builtin, we can reuse the type, this helps inference. - if !(oprnd_t.is_integral() || oprnd_t.kind == ty::Bool) { + if !(oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool) { oprnd_t = result; } } @@ -371,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { - match ty.kind { + match ty.kind() { ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { if oprnd.is_syntactic_place_expr() { // Places may legitimately have unsized types. @@ -434,19 +439,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This is maybe too permissive, since it allows // `let u = &raw const Box::new((1,)).0`, which creates an // immediately dangling raw pointer. - self.typeck_results.borrow().adjustments().get(base.hir_id).map_or(false, |x| { - x.iter().any(|adj| if let Adjust::Deref(_) = adj.kind { true } else { false }) - }) + self.typeck_results + .borrow() + .adjustments() + .get(base.hir_id) + .map_or(false, |x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) }); if !is_named { - struct_span_err!( - self.tcx.sess, - oprnd.span, - E0745, - "cannot take address of a temporary" - ) - .span_label(oprnd.span, "temporary value") - .emit(); + self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span }) } } @@ -473,7 +473,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, }; - if let ty::FnDef(..) = ty.kind { + if let ty::FnDef(..) = ty.kind() { let fn_sig = ty.fn_sig(tcx); if !tcx.features().unsized_locals { // We want to remove some Sized bounds from std functions, @@ -665,13 +665,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { if self.ret_coercion.is_none() { - struct_span_err!( - self.tcx.sess, - expr.span, - E0572, - "return statement outside of function body", - ) - .emit(); + self.tcx.sess.emit_err(ReturnStmtOutsideOfFnBody { span: expr.span }); } else if let Some(ref e) = expr_opt { if self.ret_coercion_span.borrow().is_none() { *self.ret_coercion_span.borrow_mut() = Some(e.span); @@ -740,6 +734,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_span: &Span, ) { if !lhs.is_syntactic_place_expr() { + // 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, "invalid left-hand side of assignment", @@ -764,9 +759,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs: &'tcx hir::Expr<'tcx>, span: &Span, ) -> Ty<'tcx> { - 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)); - let expected_ty = expected.coercion_target_type(self, expr.span); if expected_ty == self.tcx.types.bool { // The expected type is `bool` but this will result in `()` so we can reasonably @@ -774,20 +766,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // The likely cause of this is `if foo = bar { .. }`. let actual_ty = self.tcx.mk_unit(); let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap(); - let msg = "try comparing for equality"; - let left = self.tcx.sess.source_map().span_to_snippet(lhs.span); - let right = self.tcx.sess.source_map().span_to_snippet(rhs.span); - if let (Ok(left), Ok(right)) = (left, right) { - let help = format!("{} == {}", left, right); - err.span_suggestion(expr.span, msg, help, Applicability::MaybeIncorrect); + let lhs_ty = self.check_expr(&lhs); + let rhs_ty = self.check_expr(&rhs); + if self.can_coerce(lhs_ty, rhs_ty) { + if !lhs.is_syntactic_place_expr() { + // Do not suggest `if let x = y` as `==` is way more likely to be the intention. + if let hir::Node::Expr(hir::Expr { + kind: ExprKind::Match(_, _, hir::MatchSource::IfDesugar { .. }), + .. + }) = self.tcx.hir().get( + self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(expr.hir_id)), + ) { + // Likely `if let` intended. + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to use pattern matching", + "let ".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + err.span_suggestion_verbose( + *span, + "you might have meant to compare for equality", + "==".to_string(), + Applicability::MaybeIncorrect, + ); } else { - err.help(msg); + // Do this to cause extra errors about the assignment. + let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); + let _ = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs)); } - err.emit(); - } else { - self.check_lhs_assignable(lhs, "E0070", span); + + if self.sess().if_let_suggestions.borrow().get(&expr.span).is_some() { + // We already emitted an `if let` suggestion due to an identifier not found. + err.delay_as_bug(); + } else { + err.emit(); + } + return self.tcx.ty_error(); } + 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)); + self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); if lhs_ty.references_error() || rhs_ty.references_error() { @@ -922,7 +946,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error, Some(args), ) { - if let ty::Adt(..) = rcvr_t.kind { + if let ty::Adt(..) = rcvr_t.kind() { // Try alternative arbitrary self types that could fulfill this call. // FIXME: probe for all types that *could* be arbitrary self-types, not // just this list. @@ -973,7 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let element_ty = if !args.is_empty() { let coerce_to = expected .to_option(self) - .and_then(|uty| match uty.kind { + .and_then(|uty| match *uty.kind() { ty::Array(ty, _) | ty::Slice(ty) => Some(ty), _ => None, }) @@ -1011,7 +1035,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let count = self.to_const(count); let uty = match expected { - ExpectHasType(uty) => match uty.kind { + ExpectHasType(uty) => match *uty.kind() { ty::Array(ty, _) | ty::Slice(ty) => Some(ty), _ => None, }, @@ -1048,7 +1072,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let flds = expected.only_has_type(self).and_then(|ty| { let ty = self.resolve_vars_with_obligations(ty); - match ty.kind { + match ty.kind() { ty::Tuple(ref flds) => Some(&flds[..]), _ => None, } @@ -1091,14 +1115,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Prohibit struct expressions when non-exhaustive flag is set. let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type"); if !adt.did.is_local() && variant.is_field_list_non_exhaustive() { - struct_span_err!( - self.tcx.sess, - expr.span, - E0639, - "cannot create non-exhaustive {} using struct expression", - adt.variant_descr() - ) - .emit(); + self.tcx + .sess + .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); } let error_happened = self.check_expr_struct_fields( @@ -1116,7 +1135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 { + match adt_ty.kind() { ty::Adt(adt, substs) if adt.is_struct() => { let fru_field_types = adt .non_enum_variant() @@ -1136,13 +1155,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .insert(expr.hir_id, fru_field_types); } _ => { - struct_span_err!( - self.tcx.sess, - base_expr.span, - E0436, - "functional record update syntax requires a struct" - ) - .emit(); + self.tcx + .sess + .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); } } } @@ -1171,7 +1186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // re-link the regions that EIfEO can erase. self.demand_eqtype(span, adt_ty_hint, adt_ty); - let (substs, adt_kind, kind_name) = match &adt_ty.kind { + let (substs, adt_kind, kind_name) = match &adt_ty.kind() { &ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()), _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields"), }; @@ -1205,18 +1220,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { error_happened = true; if let Some(prev_span) = seen_fields.get(&ident) { - let mut err = struct_span_err!( - self.tcx.sess, - field.ident.span, - E0062, - "field `{}` specified more than once", - ident - ); - - err.span_label(field.ident.span, "used more than once"); - err.span_label(*prev_span, format!("first use of `{}`", ident)); - - err.emit(); + tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer { + span: field.ident.span, + prev_span: *prev_span, + ident, + }); } else { self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name, span); } @@ -1235,42 +1243,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.sess.span_err(span, "union expressions should have exactly one field"); } } else if check_completeness && !error_happened && !remaining_fields.is_empty() { - let len = remaining_fields.len(); - - let mut displayable_field_names = - remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>(); - - displayable_field_names.sort(); + let no_accessible_remaining_fields = remaining_fields + .iter() + .find(|(_, (_, field))| { + field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) + }) + .is_none(); - let truncated_fields_error = if len <= 3 { - String::new() + if no_accessible_remaining_fields { + self.report_no_accessible_fields(adt_ty, span); } else { - format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" }) - }; - - let remaining_fields_names = displayable_field_names - .iter() - .take(3) - .map(|n| format!("`{}`", n)) - .collect::<Vec<_>>() - .join(", "); - - struct_span_err!( - tcx.sess, - span, - E0063, - "missing field{} {}{} in initializer of `{}`", - pluralize!(remaining_fields.len()), - remaining_fields_names, - truncated_fields_error, - adt_ty - ) - .span_label( - span, - format!("missing {}{}", remaining_fields_names, truncated_fields_error), - ) - .emit(); + self.report_missing_field(adt_ty, span, remaining_fields); + } } + error_happened } @@ -1287,6 +1273,79 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Report an error for a struct field expression when there are fields which aren't provided. + /// + /// ```ignore (diagnostic) + /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo` + /// --> src/main.rs:8:5 + /// | + /// 8 | foo::Foo {}; + /// | ^^^^^^^^ missing `you_can_use_this_field` + /// + /// error: aborting due to previous error + /// ``` + fn report_missing_field( + &self, + adt_ty: Ty<'tcx>, + span: Span, + remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>, + ) { + let tcx = self.tcx; + let len = remaining_fields.len(); + + let mut displayable_field_names = + remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>(); + + displayable_field_names.sort(); + + let truncated_fields_error = if len <= 3 { + String::new() + } else { + format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" }) + }; + + let remaining_fields_names = displayable_field_names + .iter() + .take(3) + .map(|n| format!("`{}`", n)) + .collect::<Vec<_>>() + .join(", "); + + struct_span_err!( + tcx.sess, + span, + E0063, + "missing field{} {}{} in initializer of `{}`", + pluralize!(remaining_fields.len()), + remaining_fields_names, + truncated_fields_error, + adt_ty + ) + .span_label(span, format!("missing {}{}", remaining_fields_names, truncated_fields_error)) + .emit(); + } + + /// Report an error for a struct field expression when there are no visible fields. + /// + /// ```ignore (diagnostic) + /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields + /// --> src/main.rs:8:5 + /// | + /// 8 | foo::Foo {}; + /// | ^^^^^^^^ + /// + /// error: aborting due to previous error + /// ``` + fn report_no_accessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) { + self.tcx.sess.span_err( + span, + &format!( + "cannot construct `{}` with struct literal syntax due to inaccessible fields", + adt_ty, + ), + ); + } + fn report_unknown_field( &self, ty: Ty<'tcx>, @@ -1302,7 +1361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut err = self.type_error_struct_with_diag( field.ident.span, - |actual| match ty.kind { + |actual| match ty.kind() { ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!( self.tcx.sess, field.ident.span, @@ -1352,7 +1411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } else { - match ty.kind { + match ty.kind() { ty::Adt(adt, ..) => { if adt.is_enum() { err.span_label( @@ -1439,7 +1498,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut private_candidate = None; let mut autoderef = self.autoderef(expr.span, expr_t); while let Some((base_t, _)) = autoderef.next() { - match base_t.kind { + match base_t.kind() { ty::Adt(base_def, substs) if !base_def.is_enum() => { debug!("struct named {:?}", base_t); let (ident, def_scope) = @@ -1542,9 +1601,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!( "suggest_await_on_field_access: normalized_ty={:?}, ty_kind={:?}", self.resolve_vars_if_possible(&normalized_ty), - normalized_ty.kind, + normalized_ty.kind(), ); - if let ty::Adt(def, _) = normalized_ty.kind { + if let ty::Adt(def, _) = normalized_ty.kind() { // no field access on enum type if !def.is_enum() { if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) @@ -1574,7 +1633,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let mut err = self.no_such_field_err(field.span, field, expr_t); - match expr_t.peel_refs().kind { + match *expr_t.peel_refs().kind() { ty::Array(_, len) => { self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); } @@ -1794,7 +1853,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base_t ); // Try to give some advice about indexing tuples. - if let ty::Tuple(..) = base_t.kind { + if let ty::Tuple(..) = base_t.kind() { let mut needs_note = true; // If the index is an integer, we can show the actual // fixed expression: @@ -1847,13 +1906,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_unit() } _ => { - struct_span_err!( - self.tcx.sess, - expr.span, - E0627, - "yield expression outside of generator literal" - ) - .emit(); + self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span }); self.tcx.mk_unit() } } @@ -1879,7 +1932,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // function. if is_input { let ty = self.structurally_resolved_type(expr.span, &ty); - match ty.kind { + match *ty.kind() { ty::FnDef(..) => { let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx)); self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No); @@ -1927,7 +1980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { - Some(match ty.kind { + Some(match ty.kind() { ty::Bool => "true", ty::Char => "'a'", ty::Int(_) | ty::Uint(_) => "42", diff --git a/compiler/rustc_typeck/src/check/fn_ctxt.rs b/compiler/rustc_typeck/src/check/fn_ctxt.rs new file mode 100644 index 00000000000..a03b8064b59 --- /dev/null +++ b/compiler/rustc_typeck/src/check/fn_ctxt.rs @@ -0,0 +1,3200 @@ +// ignore-tidy-filelength +// FIXME: This file seems to have too much functionality wrapped into it, +// leading to it being too long. +// Splitting this file may involve abstracting functionality into other files. + +use super::callee::{self, DeferredCallResolution}; +use super::coercion::{CoerceMany, DynamicCoerceMany}; +use super::method::{self, MethodCallee, SelfSource}; +use super::Expectation::*; +use super::TupleArgumentsFlag::*; +use super::{ + potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, EnclosingBreakables, + Expectation, FallbackMode, Inherited, LocalTy, Needs, TupleArgumentsFlag, UnsafetyState, +}; +use crate::astconv::{ + AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg, +}; + +use rustc_ast as ast; +use rustc_ast::util::parser::ExprPrecedence; +use rustc_data_structures::captures::Captures; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::ErrorReported; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{ExprKind, GenericArg, ItemKind, Node, QPath}; +use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; +use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_infer::infer::{self, InferOk, InferResult}; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, +}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::{ + self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts, +}; +use rustc_middle::ty::{ + self, AdtKind, CanonicalUserType, Const, DefIdTree, GenericParamDefKind, ToPolyTraitRef, + ToPredicate, Ty, TyCtxt, UserType, +}; +use rustc_session::{lint, Session}; +use rustc_span::hygiene::DesugaringKind; +use rustc_span::source_map::{original_sp, DUMMY_SP}; +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::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::{ + self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, +}; + +use std::cell::{Cell, RefCell}; +use std::collections::hash_map::Entry; +use std::iter; +use std::mem::replace; +use std::ops::Deref; +use std::slice; + +pub struct FnCtxt<'a, 'tcx> { + pub(super) body_id: hir::HirId, + + /// The parameter environment used for proving trait obligations + /// in this function. This can change when we descend into + /// closures (as they bring new things into scope), hence it is + /// not part of `Inherited` (as of the time of this writing, + /// closures do not yet change the environment, but they will + /// eventually). + pub(super) param_env: ty::ParamEnv<'tcx>, + + /// Number of errors that had been reported when we started + /// checking this function. On exit, if we find that *more* errors + /// have been reported, we will skip regionck and other work that + /// expects the types within the function to be consistent. + // FIXME(matthewjasper) This should not exist, and it's not correct + // if type checking is run in parallel. + err_count_on_creation: usize, + + /// If `Some`, this stores coercion information for returned + /// expressions. If `None`, this is in a context where return is + /// inappropriate, such as a const expression. + /// + /// This is a `RefCell<DynamicCoerceMany>`, which means that we + /// can track all the return expressions and then use them to + /// compute a useful coercion from the set, similar to a match + /// expression or other branching context. You can use methods + /// like `expected_ty` to access the declared return type (if + /// any). + pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>, + + pub(super) ret_coercion_impl_trait: Option<Ty<'tcx>>, + + pub(super) ret_type_span: Option<Span>, + + /// Used exclusively to reduce cost of advanced evaluation used for + /// more helpful diagnostics. + pub(super) in_tail_expr: bool, + + /// First span of a return site that we find. Used in error messages. + pub(super) ret_coercion_span: RefCell<Option<Span>>, + + pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, + + pub(super) ps: RefCell<UnsafetyState>, + + /// Whether the last checked node generates a divergence (e.g., + /// `return` will set this to `Always`). In general, when entering + /// an expression or other node in the tree, the initial value + /// indicates whether prior parts of the containing expression may + /// have diverged. It is then typically set to `Maybe` (and the + /// old value remembered) for processing the subparts of the + /// current expression. As each subpart is processed, they may set + /// the flag to `Always`, etc. Finally, at the end, we take the + /// result and "union" it with the original value, so that when we + /// return the flag indicates if any subpart of the parent + /// expression (up to and including this part) has diverged. So, + /// if you read it after evaluating a subexpression `X`, the value + /// you get indicates whether any subexpression that was + /// evaluating up to and including `X` diverged. + /// + /// We currently use this flag only for diagnostic purposes: + /// + /// - To warn about unreachable code: if, after processing a + /// sub-expression but before we have applied the effects of the + /// current node, we see that the flag is set to `Always`, we + /// can issue a warning. This corresponds to something like + /// `foo(return)`; we warn on the `foo()` expression. (We then + /// update the flag to `WarnedAlways` to suppress duplicate + /// reports.) Similarly, if we traverse to a fresh statement (or + /// tail expression) from a `Always` setting, we will issue a + /// warning. This corresponds to something like `{return; + /// foo();}` or `{return; 22}`, where we would warn on the + /// `foo()` or `22`. + /// + /// An expression represents dead code if, after checking it, + /// the diverges flag is set to something other than `Maybe`. + pub(super) diverges: Cell<Diverges>, + + /// Whether any child nodes have any type errors. + pub(super) has_errors: Cell<bool>, + + pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>, + + pub(super) inh: &'a Inherited<'a, 'tcx>, +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn new( + inh: &'a Inherited<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ) -> FnCtxt<'a, 'tcx> { + FnCtxt { + body_id, + param_env, + err_count_on_creation: inh.tcx.sess.err_count(), + ret_coercion: None, + ret_coercion_impl_trait: None, + ret_type_span: None, + in_tail_expr: false, + ret_coercion_span: RefCell::new(None), + resume_yield_tys: None, + ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), + diverges: Cell::new(Diverges::Maybe), + has_errors: Cell::new(false), + enclosing_breakables: RefCell::new(EnclosingBreakables { + stack: Vec::new(), + by_id: Default::default(), + }), + inh, + } + } + + pub fn sess(&self) -> &Session { + &self.tcx.sess + } + + pub fn errors_reported_since_creation(&self) -> bool { + self.tcx.sess.err_count() > self.err_count_on_creation + } + + /// Produces warning on the given node, if the current point in the + /// function is unreachable, and there hasn't been another warning. + pub(super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) { + // FIXME: Combine these two 'if' expressions into one once + // let chains are implemented + if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() { + // If span arose from a desugaring of `if` or `while`, then it is the condition itself, + // which diverges, that we are about to lint on. This gives suboptimal diagnostics. + // Instead, stop here so that the `if`- or `while`-expression's block is linted instead. + if !span.is_desugaring(DesugaringKind::CondTemporary) + && !span.is_desugaring(DesugaringKind::Async) + && !orig_span.is_desugaring(DesugaringKind::Await) + { + self.diverges.set(Diverges::WarnedAlways); + + debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); + + self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { + let msg = format!("unreachable {}", kind); + lint.build(&msg) + .span_label(span, &msg) + .span_label( + orig_span, + custom_note + .unwrap_or("any code following this expression is unreachable"), + ) + .emit(); + }) + } + } + } + + pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { + ObligationCause::new(span, self.body_id, code) + } + + pub fn misc(&self, span: Span) -> ObligationCause<'tcx> { + self.cause(span, ObligationCauseCode::MiscObligation) + } + + /// Resolves type and const variables in `ty` if possible. Unlike the infcx + /// version (resolve_vars_if_possible), this version will + /// also select obligations if it seems useful, in an effort + /// to get more type information. + pub(super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { + debug!("resolve_vars_with_obligations(ty={:?})", ty); + + // No Infer()? Nothing needs doing. + if !ty.has_infer_types_or_consts() { + debug!("resolve_vars_with_obligations: ty={:?}", ty); + return ty; + } + + // If `ty` is a type variable, see whether we already know what it is. + ty = self.resolve_vars_if_possible(&ty); + if !ty.has_infer_types_or_consts() { + debug!("resolve_vars_with_obligations: ty={:?}", ty); + return ty; + } + + // If not, try resolving pending obligations as much as + // possible. This can help substantially when there are + // indirect dependencies that don't seem worth tracking + // precisely. + self.select_obligations_where_possible(false, |_| {}); + ty = self.resolve_vars_if_possible(&ty); + + debug!("resolve_vars_with_obligations: ty={:?}", ty); + ty + } + + pub(super) fn record_deferred_call_resolution( + &self, + closure_def_id: DefId, + r: DeferredCallResolution<'tcx>, + ) { + let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); + deferred_call_resolutions.entry(closure_def_id).or_default().push(r); + } + + pub(super) fn remove_deferred_call_resolutions( + &self, + closure_def_id: DefId, + ) -> Vec<DeferredCallResolution<'tcx>> { + let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); + deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![]) + } + + pub fn tag(&self) -> String { + format!("{:p}", self) + } + + pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> { + self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| { + span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid)) + }) + } + + #[inline] + pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) { + debug!( + "write_ty({:?}, {:?}) in fcx {}", + id, + self.resolve_vars_if_possible(&ty), + self.tag() + ); + self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); + + if ty.references_error() { + self.has_errors.set(true); + self.set_tainted_by_errors(); + } + } + + pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) { + self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index); + } + + fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), ErrorReported>) { + self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); + } + + pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) { + debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method); + self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); + self.write_substs(hir_id, method.substs); + + // When the method is confirmed, the `method.substs` includes + // parameters from not just the method, but also the impl of + // the method -- in particular, the `Self` type will be fully + // resolved. However, those are not something that the "user + // specified" -- i.e., those types come from the inferred type + // of the receiver, not something the user wrote. So when we + // create the user-substs, we want to replace those earlier + // types with just the types that the user actually wrote -- + // that is, those that appear on the *method itself*. + // + // As an example, if the user wrote something like + // `foo.bar::<u32>(...)` -- the `Self` type here will be the + // type of `foo` (possibly adjusted), but we don't want to + // include that. We want just the `[_, u32]` part. + if !method.substs.is_noop() { + let method_generics = self.tcx.generics_of(method.def_id); + if !method_generics.params.is_empty() { + let user_type_annotation = self.infcx.probe(|_| { + let user_substs = UserSubsts { + substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| { + let i = param.index as usize; + if i < method_generics.parent_count { + self.infcx.var_for_def(DUMMY_SP, param) + } else { + method.substs[i] + } + }), + user_self_ty: None, // not relevant here + }; + + self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( + method.def_id, + user_substs, + )) + }); + + debug!("write_method_call: user_type_annotation={:?}", user_type_annotation); + self.write_user_type_annotation(hir_id, user_type_annotation); + } + } + } + + pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) { + if !substs.is_noop() { + debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag()); + + self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs); + } + } + + /// Given the substs that we just converted from the HIR, try to + /// canonicalize them and store them as user-given substitutions + /// (i.e., substitutions that must be respected by the NLL check). + /// + /// This should be invoked **before any unifications have + /// occurred**, so that annotations like `Vec<_>` are preserved + /// properly. + pub fn write_user_type_annotation_from_substs( + &self, + hir_id: hir::HirId, + def_id: DefId, + substs: SubstsRef<'tcx>, + user_self_ty: Option<UserSelfTy<'tcx>>, + ) { + debug!( + "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \ + user_self_ty={:?} in fcx {}", + hir_id, + def_id, + substs, + user_self_ty, + self.tag(), + ); + + if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { + let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( + def_id, + UserSubsts { substs, user_self_ty }, + )); + debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized); + self.write_user_type_annotation(hir_id, canonicalized); + } + } + + pub fn write_user_type_annotation( + &self, + hir_id: hir::HirId, + canonical_user_type_annotation: CanonicalUserType<'tcx>, + ) { + debug!( + "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}", + hir_id, + canonical_user_type_annotation, + self.tag(), + ); + + if !canonical_user_type_annotation.is_identity() { + self.typeck_results + .borrow_mut() + .user_provided_types_mut() + .insert(hir_id, canonical_user_type_annotation); + } else { + debug!("write_user_type_annotation: skipping identity substs"); + } + } + + pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) { + debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj); + + if adj.is_empty() { + return; + } + + let autoborrow_mut = adj.iter().any(|adj| { + matches!(adj, &Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), + .. + }) + }); + + match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) { + Entry::Vacant(entry) => { + entry.insert(adj); + } + Entry::Occupied(mut entry) => { + debug!(" - composing on top of {:?}", entry.get()); + match (&entry.get()[..], &adj[..]) { + // Applying any adjustment on top of a NeverToAny + // is a valid NeverToAny adjustment, because it can't + // be reached. + (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return, + (&[ + Adjustment { kind: Adjust::Deref(_), .. }, + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, + ], &[ + Adjustment { kind: Adjust::Deref(_), .. }, + .. // Any following adjustments are allowed. + ]) => { + // A reborrow has no effect before a dereference. + } + // FIXME: currently we never try to compose autoderefs + // and ReifyFnPointer/UnsafeFnPointer, but we could. + _ => + bug!("while adjusting {:?}, can't compose {:?} and {:?}", + expr, entry.get(), adj) + }; + *entry.get_mut() = adj; + } + } + + // If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`. + // In this case implicit use of `Deref` and `Index` within `<expr>` should + // instead be `DerefMut` and `IndexMut`, so fix those up. + if autoborrow_mut { + self.convert_place_derefs_to_mutable(expr); + } + } + + /// Basically whenever we are converting from a type scheme into + /// the fn body space, we always want to normalize associated + /// types as well. This function combines the two. + fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T + where + T: TypeFoldable<'tcx>, + { + let value = value.subst(self.tcx, substs); + let result = self.normalize_associated_types_in(span, &value); + debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result); + result + } + + /// As `instantiate_type_scheme`, but for the bounds found in a + /// generic type scheme. + fn instantiate_bounds( + &self, + span: Span, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) { + let bounds = self.tcx.predicates_of(def_id); + let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect(); + let result = bounds.instantiate(self.tcx, substs); + let result = self.normalize_associated_types_in(span, &result); + debug!( + "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", + bounds, substs, result, spans, + ); + (result, spans) + } + + /// Replaces the opaque types from the given value with type variables, + /// and records the `OpaqueTypeMap` for later use during writeback. See + /// `InferCtxt::instantiate_opaque_types` for more details. + pub(super) fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>( + &self, + parent_id: hir::HirId, + value: &T, + value_span: Span, + ) -> T { + let parent_def_id = self.tcx.hir().local_def_id(parent_id); + debug!( + "instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})", + parent_def_id, value + ); + + let (value, opaque_type_map) = + self.register_infer_ok_obligations(self.instantiate_opaque_types( + parent_def_id, + self.body_id, + self.param_env, + value, + value_span, + )); + + let mut opaque_types = self.opaque_types.borrow_mut(); + let mut opaque_types_vars = self.opaque_types_vars.borrow_mut(); + for (ty, decl) in opaque_type_map { + let _ = opaque_types.insert(ty, decl); + let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type); + } + + value + } + + pub(super) fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T + where + T: TypeFoldable<'tcx>, + { + self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value) + } + + pub(super) fn normalize_associated_types_in_as_infer_ok<T>( + &self, + span: Span, + value: &T, + ) -> InferOk<'tcx, T> + where + T: TypeFoldable<'tcx>, + { + self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value) + } + + pub fn require_type_meets( + &self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + def_id: DefId, + ) { + self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code)); + } + + pub fn require_type_is_sized( + &self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + ) { + if !ty.references_error() { + let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); + self.require_type_meets(ty, span, code, lang_item); + } + } + + pub fn require_type_is_sized_deferred( + &self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + ) { + if !ty.references_error() { + self.deferred_sized_obligations.borrow_mut().push((ty, span, code)); + } + } + + pub fn register_bound( + &self, + ty: Ty<'tcx>, + def_id: DefId, + cause: traits::ObligationCause<'tcx>, + ) { + if !ty.references_error() { + self.fulfillment_cx.borrow_mut().register_bound( + self, + self.param_env, + ty, + def_id, + cause, + ); + } + } + + pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { + let t = AstConv::ast_ty_to_ty(self, ast_t); + self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation); + t + } + + pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { + let ty = self.to_ty(ast_ty); + debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); + + if Self::can_contain_user_lifetime_bounds(ty) { + let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty)); + debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); + self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); + } + + ty + } + + pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> { + let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); + let c = ty::Const::from_anon_const(self.tcx, const_def_id); + self.register_wf_obligation( + c.into(), + self.tcx.hir().span(ast_c.hir_id), + ObligationCauseCode::MiscObligation, + ); + c + } + + pub fn const_arg_to_const( + &self, + ast_c: &hir::AnonConst, + param_def_id: DefId, + ) -> &'tcx ty::Const<'tcx> { + let const_def = ty::WithOptConstParam { + did: self.tcx.hir().local_def_id(ast_c.hir_id), + const_param_did: Some(param_def_id), + }; + let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def); + self.register_wf_obligation( + c.into(), + self.tcx.hir().span(ast_c.hir_id), + ObligationCauseCode::MiscObligation, + ); + c + } + + // If the type given by the user has free regions, save it for later, since + // NLL would like to enforce those. Also pass in types that involve + // projections, since those can resolve to `'static` bounds (modulo #54940, + // which hopefully will be fixed by the time you see this comment, dear + // reader, although I have my doubts). Also pass in types with inference + // types, because they may be repeated. Other sorts of things are already + // sufficiently enforced with erased regions. =) + fn can_contain_user_lifetime_bounds<T>(t: T) -> bool + where + T: TypeFoldable<'tcx>, + { + t.has_free_regions() || t.has_projections() || t.has_infer_types() + } + + pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { + match self.typeck_results.borrow().node_types().get(id) { + Some(&t) => t, + None if self.is_tainted_by_errors() => self.tcx.ty_error(), + None => { + bug!( + "no type for node {}: {} in fcx {}", + id, + self.tcx.hir().node_to_string(id), + self.tag() + ); + } + } + } + + /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. + pub fn register_wf_obligation( + &self, + arg: subst::GenericArg<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + ) { + // WF obligations never themselves fail, so no real need to give a detailed cause: + let cause = traits::ObligationCause::new(span, self.body_id, code); + self.register_predicate(traits::Obligation::new( + cause, + self.param_env, + ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx), + )); + } + + /// Registers obligations that all `substs` are well-formed. + pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) { + for arg in substs.iter().filter(|arg| { + matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) + }) { + self.register_wf_obligation(arg, expr.span, traits::MiscObligation); + } + } + + /// 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 + pub fn field_ty( + &self, + span: Span, + field: &'tcx ty::FieldDef, + substs: SubstsRef<'tcx>, + ) -> Ty<'tcx> { + self.normalize_associated_types_in(span, &field.ty(self.tcx, substs)) + } + + pub(super) fn check_casts(&self) { + let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); + for cast in deferred_cast_checks.drain(..) { + cast.check(self); + } + } + + pub(super) fn resolve_generator_interiors(&self, def_id: DefId) { + let mut generators = self.deferred_generator_interiors.borrow_mut(); + for (body_id, interior, kind) in generators.drain(..) { + self.select_obligations_where_possible(false, |_| {}); + super::generator_interior::resolve_interior(self, def_id, body_id, interior, kind); + } + } + + // Tries to apply a fallback to `ty` if it is an unsolved variable. + // + // - Unconstrained ints are replaced with `i32`. + // + // - Unconstrained floats are replaced with with `f64`. + // + // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]` + // is enabled. Otherwise, they are replaced with `()`. + // + // Fallback becomes very dubious if we have encountered type-checking errors. + // In that case, fallback to Error. + // The return value indicates whether fallback has occurred. + pub(super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool { + use rustc_middle::ty::error::UnconstrainedNumeric::Neither; + use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; + + assert!(ty.is_ty_infer()); + let fallback = match self.type_is_unconstrained_numeric(ty) { + _ if self.is_tainted_by_errors() => self.tcx().ty_error(), + UnconstrainedInt => self.tcx.types.i32, + UnconstrainedFloat => self.tcx.types.f64, + Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(), + Neither => { + // This type variable was created from the instantiation of an opaque + // type. The fact that we're attempting to perform fallback for it + // means that the function neither constrained it to a concrete + // type, nor to the opaque type itself. + // + // For example, in this code: + // + //``` + // type MyType = impl Copy; + // fn defining_use() -> MyType { true } + // fn other_use() -> MyType { defining_use() } + // ``` + // + // `defining_use` will constrain the instantiated inference + // variable to `bool`, while `other_use` will constrain + // the instantiated inference variable to `MyType`. + // + // When we process opaque types during writeback, we + // will handle cases like `other_use`, and not count + // them as defining usages + // + // However, we also need to handle cases like this: + // + // ```rust + // pub type Foo = impl Copy; + // fn produce() -> Option<Foo> { + // None + // } + // ``` + // + // In the above snippet, the inference variable created by + // instantiating `Option<Foo>` will be completely unconstrained. + // We treat this as a non-defining use by making the inference + // variable fall back to the opaque type itself. + if let FallbackMode::All = mode { + if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) { + debug!( + "fallback_if_possible: falling back opaque type var {:?} to {:?}", + ty, opaque_ty + ); + *opaque_ty + } else { + return false; + } + } else { + return false; + } + } + }; + debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback); + self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback); + true + } + + pub(super) fn select_all_obligations_or_error(&self) { + debug!("select_all_obligations_or_error"); + if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) { + self.report_fulfillment_errors(&errors, self.inh.body_id, false); + } + } + + /// Select as many obligations as we can at present. + pub(super) fn select_obligations_where_possible( + &self, + fallback_has_occurred: bool, + mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>), + ) { + let result = self.fulfillment_cx.borrow_mut().select_where_possible(self); + if let Err(mut errors) = result { + mutate_fullfillment_errors(&mut errors); + self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred); + } + } + + /// For the overloaded place expressions (`*x`, `x[3]`), the trait + /// returns a type of `&T`, but the actual type we assign to the + /// *expression* is `T`. So this function just peels off the return + /// type by one layer to yield `T`. + pub(super) fn make_overloaded_place_return_type( + &self, + method: MethodCallee<'tcx>, + ) -> ty::TypeAndMut<'tcx> { + // extract method return type, which will be &T; + let ret_ty = method.sig.output(); + + // method returns &T, but the type as visible to user is T, so deref + ret_ty.builtin_deref(true).unwrap() + } + + pub(super) fn check_method_argument_types( + &self, + sp: Span, + expr: &'tcx hir::Expr<'tcx>, + method: Result<MethodCallee<'tcx>, ()>, + args_no_rcvr: &'tcx [hir::Expr<'tcx>], + tuple_arguments: TupleArgumentsFlag, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let has_error = match method { + Ok(method) => method.substs.references_error() || method.sig.references_error(), + Err(_) => true, + }; + if has_error { + let err_inputs = self.err_args(args_no_rcvr.len()); + + let err_inputs = match tuple_arguments { + DontTupleArguments => err_inputs, + TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])], + }; + + self.check_argument_types( + sp, + expr, + &err_inputs[..], + &[], + args_no_rcvr, + false, + tuple_arguments, + None, + ); + return self.tcx.ty_error(); + } + + let method = method.unwrap(); + // HACK(eddyb) ignore self in the definition (see above). + let expected_arg_tys = self.expected_inputs_for_expected_output( + sp, + expected, + method.sig.output(), + &method.sig.inputs()[1..], + ); + self.check_argument_types( + sp, + expr, + &method.sig.inputs()[1..], + &expected_arg_tys[..], + args_no_rcvr, + method.sig.c_variadic, + tuple_arguments, + self.tcx.hir().span_if_local(method.def_id), + ); + method.sig.output() + } + + fn self_type_matches_expected_vid( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + expected_vid: ty::TyVid, + ) -> bool { + let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); + debug!( + "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})", + trait_ref, self_ty, expected_vid + ); + match *self_ty.kind() { + ty::Infer(ty::TyVar(found_vid)) => { + // FIXME: consider using `sub_root_var` here so we + // can see through subtyping. + let found_vid = self.root_var(found_vid); + debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid); + expected_vid == found_vid + } + _ => false, + } + } + + pub(super) fn obligations_for_self_ty<'b>( + &'b self, + self_ty: ty::TyVid, + ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)> + + Captures<'tcx> + + 'b { + // FIXME: consider using `sub_root_var` here so we + // can see through subtyping. + let ty_var_root = self.root_var(self_ty); + debug!( + "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}", + self_ty, + ty_var_root, + self.fulfillment_cx.borrow().pending_obligations() + ); + + self.fulfillment_cx + .borrow() + .pending_obligations() + .into_iter() + .filter_map(move |obligation| { + match obligation.predicate.skip_binders() { + ty::PredicateAtom::Projection(data) => { + Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation)) + } + ty::PredicateAtom::Trait(data, _) => { + Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation)) + } + ty::PredicateAtom::Subtype(..) => None, + ty::PredicateAtom::RegionOutlives(..) => None, + ty::PredicateAtom::TypeOutlives(..) => None, + ty::PredicateAtom::WellFormed(..) => None, + ty::PredicateAtom::ObjectSafe(..) => None, + ty::PredicateAtom::ConstEvaluatable(..) => None, + ty::PredicateAtom::ConstEquate(..) => None, + // N.B., this predicate is created by breaking down a + // `ClosureType: FnFoo()` predicate, where + // `ClosureType` represents some `Closure`. It can't + // possibly be referring to the current closure, + // because we haven't produced the `Closure` for + // this closure yet; this is exactly why the other + // code is looking for a self type of a unresolved + // inference variable. + ty::PredicateAtom::ClosureKind(..) => None, + ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, + } + }) + .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) + } + + pub(super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { + self.obligations_for_self_ty(self_ty) + .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait()) + } + + /// Generic function that factors out common logic from function calls, + /// method calls and overloaded operators. + pub(super) fn check_argument_types( + &self, + sp: Span, + expr: &'tcx hir::Expr<'tcx>, + fn_inputs: &[Ty<'tcx>], + expected_arg_tys: &[Ty<'tcx>], + args: &'tcx [hir::Expr<'tcx>], + c_variadic: bool, + tuple_arguments: TupleArgumentsFlag, + def_span: Option<Span>, + ) { + let tcx = self.tcx; + // Grab the argument types, supplying fresh type variables + // if the wrong number of arguments were supplied + let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 }; + + // All the input types from the fn signature must outlive the call + // so as to validate implied bounds. + for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) { + self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); + } + + let expected_arg_count = fn_inputs.len(); + + let param_count_error = |expected_count: usize, + arg_count: usize, + error_code: &str, + c_variadic: bool, + sugg_unit: bool| { + let (span, start_span, args) = match &expr.kind { + hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]), + hir::ExprKind::MethodCall(path_segment, span, args, _) => ( + *span, + // `sp` doesn't point at the whole `foo.bar()`, only at `bar`. + path_segment + .args + .and_then(|args| args.args.iter().last()) + // Account for `foo.bar::<T>()`. + .map(|arg| { + // Skip the closing `>`. + tcx.sess + .source_map() + .next_point(tcx.sess.source_map().next_point(arg.span())) + }) + .unwrap_or(*span), + &args[1..], // Skip the receiver. + ), + k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k), + }; + let arg_spans = if args.is_empty() { + // foo() + // ^^^-- supplied 0 arguments + // | + // expected 2 arguments + vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())] + } else { + // foo(1, 2, 3) + // ^^^ - - - supplied 3 arguments + // | + // expected 2 arguments + args.iter().map(|arg| arg.span).collect::<Vec<Span>>() + }; + + let mut err = tcx.sess.struct_span_err_with_code( + span, + &format!( + "this function takes {}{} but {} {} supplied", + if c_variadic { "at least " } else { "" }, + potentially_plural_count(expected_count, "argument"), + potentially_plural_count(arg_count, "argument"), + if arg_count == 1 { "was" } else { "were" } + ), + DiagnosticId::Error(error_code.to_owned()), + ); + let label = format!("supplied {}", potentially_plural_count(arg_count, "argument")); + for (i, span) in arg_spans.into_iter().enumerate() { + err.span_label( + span, + if arg_count == 0 || i + 1 == arg_count { &label } else { "" }, + ); + } + + if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) { + err.span_label(def_s, "defined here"); + } + if sugg_unit { + let sugg_span = tcx.sess.source_map().end_point(expr.span); + // remove closing `)` from the span + let sugg_span = sugg_span.shrink_to_lo(); + err.span_suggestion( + sugg_span, + "expected the unit value `()`; create it with empty parentheses", + String::from("()"), + Applicability::MachineApplicable, + ); + } else { + err.span_label( + span, + format!( + "expected {}{}", + if c_variadic { "at least " } else { "" }, + potentially_plural_count(expected_count, "argument") + ), + ); + } + err.emit(); + }; + + let mut expected_arg_tys = expected_arg_tys.to_vec(); + + let formal_tys = if tuple_arguments == TupleArguments { + let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]); + match tuple_type.kind() { + ty::Tuple(arg_types) if arg_types.len() != args.len() => { + param_count_error(arg_types.len(), args.len(), "E0057", false, false); + expected_arg_tys = vec![]; + self.err_args(args.len()) + } + ty::Tuple(arg_types) => { + expected_arg_tys = match expected_arg_tys.get(0) { + Some(&ty) => match ty.kind() { + ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(), + _ => vec![], + }, + None => vec![], + }; + arg_types.iter().map(|k| k.expect_ty()).collect() + } + _ => { + struct_span_err!( + tcx.sess, + sp, + E0059, + "cannot use call notation; the first type parameter \ + for the function trait is neither a tuple nor unit" + ) + .emit(); + expected_arg_tys = vec![]; + self.err_args(args.len()) + } + } + } else if expected_arg_count == supplied_arg_count { + fn_inputs.to_vec() + } else if c_variadic { + if supplied_arg_count >= expected_arg_count { + fn_inputs.to_vec() + } else { + param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false); + expected_arg_tys = vec![]; + self.err_args(supplied_arg_count) + } + } else { + // is the missing argument of type `()`? + let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 { + self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit() + } else if fn_inputs.len() == 1 && supplied_arg_count == 0 { + self.resolve_vars_if_possible(&fn_inputs[0]).is_unit() + } else { + false + }; + param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit); + + expected_arg_tys = vec![]; + self.err_args(supplied_arg_count) + }; + + debug!( + "check_argument_types: formal_tys={:?}", + formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::<Vec<String>>() + ); + + // If there is no expectation, expect formal_tys. + let expected_arg_tys = + if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() }; + + let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![]; + + // Check the arguments. + // We do this in a pretty awful way: first we type-check any arguments + // that are not closures, then we type-check the closures. This is so + // that we have more information about the types of arguments when we + // type-check the functions. This isn't really the right way to do this. + for &check_closures in &[false, true] { + debug!("check_closures={}", check_closures); + + // More awful hacks: before we check argument types, try to do + // an "opportunistic" trait resolution of any trait bounds on + // the call. This helps coercions. + if check_closures { + self.select_obligations_where_possible(false, |errors| { + 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[..], + sp, + &args, + ); + }) + } + + // For C-variadic functions, we don't have a declared type for all of + // the arguments hence we only do our usual type checking with + // the arguments who's types we do know. + let t = if c_variadic { + expected_arg_count + } else if tuple_arguments == TupleArguments { + args.len() + } else { + supplied_arg_count + }; + for (i, arg) in args.iter().take(t).enumerate() { + // Warn only for the first loop (the "no closures" one). + // Closure arguments themselves can't be diverging, but + // a previous argument can, e.g., `foo(panic!(), || {})`. + if !check_closures { + self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); + } + + let is_closure = match arg.kind { + ExprKind::Closure(..) => true, + _ => false, + }; + + if is_closure != check_closures { + continue; + } + + debug!("checking the argument"); + let formal_ty = formal_tys[i]; + + // The special-cased logic below has three functions: + // 1. Provide as good of an expected type as possible. + let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]); + + let checked_ty = self.check_expr_with_expectation(&arg, expected); + + // 2. Coerce to the most detailed type that could be coerced + // to, which is `expected_ty` if `rvalue_hint` returns an + // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. + let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty); + // 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. + self.demand_suptype(arg.span, formal_ty, coerce_ty); + } + } + + // We also need to make sure we at least write the ty of the other + // arguments which we skipped above. + if c_variadic { + fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { + use crate::structured_errors::{StructuredDiagnostic, VariadicError}; + VariadicError::new(s, span, t, cast_ty).diagnostic().emit(); + } + + for arg in args.iter().skip(expected_arg_count) { + let arg_ty = self.check_expr(&arg); + + // There are a few types which get autopromoted when passed via varargs + // in C but we just error out instead and require explicit casts. + let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); + match arg_ty.kind() { + ty::Float(ast::FloatTy::F32) => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); + } + ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); + } + ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); + } + ty::FnDef(..) => { + let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); + let ptr_ty = self.resolve_vars_if_possible(&ptr_ty); + variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); + } + _ => {} + } + } + } + } + + pub(super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> { + vec![self.tcx.ty_error(); len] + } + + /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk + /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s + /// reference a type argument. The reason to walk also the checked type is that the coerced type + /// can be not easily comparable with predicate type (because of coercion). If the types match + /// for either checked or coerced type, and there's only *one* argument that does, we point at + /// the corresponding argument's expression span instead of the `fn` call path span. + fn point_at_arg_instead_of_call_if_possible( + &self, + errors: &mut Vec<traits::FulfillmentError<'tcx>>, + final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)], + call_sp: Span, + args: &'tcx [hir::Expr<'tcx>], + ) { + // We *do not* do this for desugared call spans to keep good diagnostics when involving + // the `?` operator. + if call_sp.desugaring_kind().is_some() { + return; + } + + for error in errors { + // Only if the cause is somewhere inside the expression we want try to point at arg. + // Otherwise, it means that the cause is somewhere else and we should not change + // anything because we can break the correct span. + if !call_sp.contains(error.obligation.cause.span) { + continue; + } + + if let ty::PredicateAtom::Trait(predicate, _) = + error.obligation.predicate.skip_binders() + { + // 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().any(|arg| arg == predicate.self_ty().into()) { + Some(i) + } else { + None + } + }) + .collect::<Vec<usize>>(); + + // Both checked and coerced types could have matched, thus we need to remove + // duplicates. + + // 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()) { + // 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; + error.points_at_arg_span = true; + } + } + } + } + + /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the + /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s + /// were caused by them. If they were, we point at the corresponding type argument's span + /// instead of the `fn` call path span. + fn point_at_type_arg_instead_of_call_if_possible( + &self, + errors: &mut Vec<traits::FulfillmentError<'tcx>>, + 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::PredicateAtom::Trait(predicate, _) = + error.obligation.predicate.skip_binders() + { + // If any of the type arguments in this path segment caused the + // `FullfillmentError`, 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 = 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; + } + } + } + } + } + } + } + } + } + } + + // AST fragment checking + pub(super) fn check_lit(&self, lit: &hir::Lit, expected: Expectation<'tcx>) -> Ty<'tcx> { + let tcx = self.tcx; + + match lit.node { + ast::LitKind::Str(..) => tcx.mk_static_str(), + ast::LitKind::ByteStr(ref v) => { + tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64)) + } + ast::LitKind::Byte(_) => tcx.types.u8, + ast::LitKind::Char(_) => tcx.types.char, + ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t), + ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t), + ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { + let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { + ty::Int(_) | ty::Uint(_) => Some(ty), + ty::Char => Some(tcx.types.u8), + ty::RawPtr(..) => Some(tcx.types.usize), + ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), + _ => None, + }); + opt_ty.unwrap_or_else(|| self.next_int_var()) + } + ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t), + ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { + let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { + ty::Float(_) => Some(ty), + _ => None, + }); + opt_ty.unwrap_or_else(|| self.next_float_var()) + } + ast::LitKind::Bool(_) => tcx.types.bool, + ast::LitKind::Err(_) => tcx.ty_error(), + } + } + + /// Unifies the output type with the expected type early, for more coercions + /// and forward type information on the input expressions. + pub(super) fn expected_inputs_for_expected_output( + &self, + call_span: Span, + expected_ret: Expectation<'tcx>, + formal_ret: Ty<'tcx>, + formal_args: &[Ty<'tcx>], + ) -> Vec<Ty<'tcx>> { + let formal_ret = self.resolve_vars_with_obligations(formal_ret); + let ret_ty = match expected_ret.only_has_type(self) { + Some(ret) => ret, + None => return Vec::new(), + }; + let expect_args = self + .fudge_inference_if_ok(|| { + // Attempt to apply a subtyping relationship between the formal + // return type (likely containing type variables if the function + // is polymorphic) and the expected return type. + // No argument expectations are produced if unification fails. + let origin = self.misc(call_span); + let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret); + + // FIXME(#27336) can't use ? here, Try::from_error doesn't default + // to identity so the resulting type is not constrained. + match ures { + Ok(ok) => { + // Process any obligations locally as much as + // 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 mut fulfill = TraitEngine::new(self.tcx); + for obligation in ok.obligations { + fulfill.register_predicate_obligation(self, obligation); + } + fulfill.select_where_possible(self) + }) + .map_err(|_| ())?; + } + Err(_) => return Err(()), + } + + // Record all the argument types, with the substitutions + // produced from the above subtyping unification. + Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect()) + }) + .unwrap_or_default(); + debug!( + "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})", + formal_args, formal_ret, expect_args, expected_ret + ); + expect_args + } + + pub fn check_struct_path( + &self, + qpath: &QPath<'_>, + hir_id: hir::HirId, + ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { + let path_span = qpath.qself_span(); + let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); + let variant = match def { + Res::Err => { + self.set_tainted_by_errors(); + return None; + } + Res::Def(DefKind::Variant, _) => match ty.kind() { + ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)), + _ => bug!("unexpected type: {:?}", ty), + }, + Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) + | Res::SelfTy(..) => match ty.kind() { + ty::Adt(adt, substs) if !adt.is_enum() => { + Some((adt.non_enum_variant(), adt.did, substs)) + } + _ => None, + }, + _ => bug!("unexpected definition: {:?}", def), + }; + + if let Some((variant, did, substs)) = variant { + debug!("check_struct_path: did={:?} substs={:?}", did, substs); + 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); + + Some((variant, ty)) + } else { + struct_span_err!( + self.tcx.sess, + path_span, + E0071, + "expected struct, variant or union type, found {}", + ty.sort_string(self.tcx) + ) + .span_label(path_span, "not a struct") + .emit(); + None + } + } + + // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. + // The newly resolved definition is written into `type_dependent_defs`. + fn finish_resolving_struct_path( + &self, + qpath: &QPath<'_>, + path_span: Span, + hir_id: hir::HirId, + ) -> (Res, Ty<'tcx>) { + match *qpath { + QPath::Resolved(ref maybe_qself, ref path) => { + let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); + let ty = AstConv::res_to_ty(self, self_ty, path, true); + (path.res, ty) + } + QPath::TypeRelative(ref qself, ref segment) => { + let ty = self.to_ty(qself); + + let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind { + path.res + } else { + Res::Err + }; + let result = + AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true); + let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); + let result = result.map(|(_, kind, def_id)| (kind, def_id)); + + // Write back the new resolution. + self.write_resolution(hir_id, result); + + (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty) + } + QPath::LangItem(lang_item, span) => { + self.resolve_lang_item_path(lang_item, span, hir_id) + } + } + } + + pub(super) fn resolve_lang_item_path( + &self, + lang_item: hir::LangItem, + span: Span, + hir_id: hir::HirId, + ) -> (Res, Ty<'tcx>) { + let def_id = self.tcx.require_lang_item(lang_item, Some(span)); + let def_kind = self.tcx.def_kind(def_id); + + let item_ty = if let DefKind::Variant = def_kind { + self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent")) + } else { + self.tcx.type_of(def_id) + }; + let substs = self.infcx.fresh_substs_for_item(span, def_id); + let ty = item_ty.subst(self.tcx, substs); + + self.write_resolution(hir_id, Ok((def_kind, def_id))); + self.add_required_obligations(span, def_id, &substs); + (Res::Def(def_kind, def_id), ty) + } + + /// Resolves an associated value path into a base type and associated constant, or method + /// resolution. The newly resolved definition is written into `type_dependent_defs`. + pub fn resolve_ty_and_res_ufcs<'b>( + &self, + qpath: &'b QPath<'b>, + hir_id: hir::HirId, + span: Span, + ) -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]) { + debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span); + let (ty, qself, item_segment) = match *qpath { + QPath::Resolved(ref opt_qself, ref path) => { + return ( + path.res, + opt_qself.as_ref().map(|qself| self.to_ty(qself)), + &path.segments[..], + ); + } + QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment), + QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"), + }; + if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) + { + // Return directly on cache hit. This is useful to avoid doubly reporting + // errors with default match binding modes. See #44614. + let def = + cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err); + return (def, Some(ty), slice::from_ref(&**item_segment)); + } + let item_name = item_segment.ident; + let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| { + let result = match error { + method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), + _ => Err(ErrorReported), + }; + if item_name.name != kw::Invalid { + if let Some(mut e) = self.report_method_error( + span, + ty, + item_name, + SelfSource::QPath(qself), + error, + None, + ) { + e.emit(); + } + } + result + }); + + // Write back the new resolution. + self.write_resolution(hir_id, result); + ( + result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), + Some(ty), + slice::from_ref(&**item_segment), + ) + } + + pub fn check_decl_initializer( + &self, + local: &'tcx hir::Local<'tcx>, + init: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed + // for #42640 (default match binding modes). + // + // See #44848. + let ref_bindings = local.pat.contains_explicit_ref_binding(); + + let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty; + if let Some(m) = ref_bindings { + // Somewhat subtle: if we have a `ref` binding in the pattern, + // we want to avoid introducing coercions for the RHS. This is + // both because it helps preserve sanity and, in the case of + // ref mut, for soundness (issue #23116). In particular, in + // the latter case, we need to be clear that the type of the + // referent for the reference that results is *equal to* the + // type of the place it is referencing, and not some + // supertype thereof. + let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); + self.demand_eqtype(init.span, local_ty, init_ty); + init_ty + } else { + self.check_expr_coercable_to_type(init, local_ty, None) + } + } + + /// Type check a `let` statement. + pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + // Determine and write the type which we'll check the pattern against. + let ty = self.local_ty(local.span, local.hir_id).decl_ty; + self.write_ty(local.hir_id, ty); + + // Type check the initializer. + if let Some(ref init) = local.init { + let init_ty = self.check_decl_initializer(local, &init); + self.overwrite_local_ty_if_err(local, ty, init_ty); + } + + // Does the expected pattern type originate from an expression and what is the span? + let (origin_expr, ty_span) = match (local.ty, local.init) { + (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. + (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee. + _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. + }; + + // Type check the pattern. Override if necessary to avoid knock-on errors. + self.check_pat_top(&local.pat, ty, ty_span, origin_expr); + let pat_ty = self.node_ty(local.pat.hir_id); + self.overwrite_local_ty_if_err(local, ty, pat_ty); + } + + fn overwrite_local_ty_if_err( + &self, + local: &'tcx hir::Local<'tcx>, + decl_ty: Ty<'tcx>, + ty: Ty<'tcx>, + ) { + if ty.references_error() { + // Override the types everywhere with `err()` to avoid knock on errors. + self.write_ty(local.hir_id, ty); + self.write_ty(local.pat.hir_id, ty); + let local_ty = LocalTy { decl_ty, revealed_ty: ty }; + self.locals.borrow_mut().insert(local.hir_id, local_ty); + self.locals.borrow_mut().insert(local.pat.hir_id, local_ty); + } + } + + pub(super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) { + err.span_suggestion_short( + span.shrink_to_hi(), + "consider using a semicolon here", + ";".to_string(), + Applicability::MachineApplicable, + ); + } + + pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) { + // Don't do all the complex logic below for `DeclItem`. + match stmt.kind { + hir::StmtKind::Item(..) => return, + hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} + } + + self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); + + // Hide the outer diverging and `has_errors` flags. + let old_diverges = self.diverges.replace(Diverges::Maybe); + let old_has_errors = self.has_errors.replace(false); + + match stmt.kind { + hir::StmtKind::Local(ref l) => { + self.check_decl_local(&l); + } + // Ignore for now. + hir::StmtKind::Item(_) => {} + hir::StmtKind::Expr(ref expr) => { + // Check with expected type of `()`. + self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { + self.suggest_semicolon_at_end(expr.span, err); + }); + } + hir::StmtKind::Semi(ref expr) => { + self.check_expr(&expr); + } + } + + // Combine the diverging and `has_error` flags. + self.diverges.set(self.diverges.get() | old_diverges); + self.has_errors.set(self.has_errors.get() | old_has_errors); + } + + pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { + let unit = self.tcx.mk_unit(); + let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); + + // if the block produces a `!` value, that can always be + // (effectively) coerced to unit. + if !ty.is_never() { + self.demand_suptype(blk.span, unit, ty); + } + } + + /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail + /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors + /// when given code like the following: + /// ```text + /// if false { return 0i32; } else { 1u32 } + /// // ^^^^ point at this instead of the whole `if` expression + /// ``` + fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { + if let hir::ExprKind::Match(_, arms, _) = &expr.kind { + let arm_spans: Vec<Span> = arms + .iter() + .filter_map(|arm| { + self.in_progress_typeck_results + .and_then(|typeck_results| { + typeck_results.borrow().node_type_opt(arm.body.hir_id) + }) + .and_then(|arm_ty| { + if arm_ty.is_never() { + None + } else { + Some(match &arm.body.kind { + // Point at the tail expression when possible. + hir::ExprKind::Block(block, _) => { + block.expr.as_ref().map(|e| e.span).unwrap_or(block.span) + } + _ => arm.body.span, + }) + } + }) + }) + .collect(); + if arm_spans.len() == 1 { + return arm_spans[0]; + } + } + expr.span + } + + pub(super) fn check_block_with_expected( + &self, + blk: &'tcx hir::Block<'tcx>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let prev = { + let mut fcx_ps = self.ps.borrow_mut(); + let unsafety_state = fcx_ps.recurse(blk); + replace(&mut *fcx_ps, unsafety_state) + }; + + // In some cases, blocks have just one exit, but other blocks + // can be targeted by multiple breaks. This can happen both + // with labeled blocks as well as when we desugar + // a `try { ... }` expression. + // + // Example 1: + // + // 'a: { if true { break 'a Err(()); } Ok(()) } + // + // Here we would wind up with two coercions, one from + // `Err(())` and the other from the tail expression + // `Ok(())`. If the tail expression is omitted, that's a + // "forced unit" -- unless the block diverges, in which + // case we can ignore the tail expression (e.g., `'a: { + // break 'a 22; }` would not force the type of the block + // to be `()`). + let tail_expr = blk.expr.as_ref(); + let coerce_to_ty = expected.coercion_target_type(self, blk.span); + let coerce = if blk.targeted_by_break { + CoerceMany::new(coerce_to_ty) + } else { + let tail_expr: &[&hir::Expr<'_>] = match tail_expr { + Some(e) => slice::from_ref(e), + None => &[], + }; + CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) + }; + + let prev_diverges = self.diverges.get(); + let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; + + let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { + for s in blk.stmts { + self.check_stmt(s); + } + + // check the tail expression **without** holding the + // `enclosing_breakables` lock below. + let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); + + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + let ctxt = enclosing_breakables.find_breakable(blk.hir_id); + let coerce = ctxt.coerce.as_mut().unwrap(); + if let Some(tail_expr_ty) = tail_expr_ty { + let tail_expr = tail_expr.unwrap(); + let span = self.get_expr_coercion_span(tail_expr); + let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); + coerce.coerce(self, &cause, tail_expr, tail_expr_ty); + } else { + // Subtle: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + // + // #41425 -- label the implicit `()` as being the + // "found type" here, rather than the "expected type". + if !self.diverges.get().is_always() { + // #50009 -- Do not point at the entire fn block span, point at the return type + // span, as it is the cause of the requirement, and + // `consider_hint_about_removing_semicolon` will point at the last expression + // if it were a relevant part of the error. This improves usability in editors + // that highlight errors inline. + let mut sp = blk.span; + let mut fn_span = None; + if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { + let ret_sp = decl.output.span(); + if let Some(block_sp) = self.parent_item_span(blk.hir_id) { + // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the + // output would otherwise be incorrect and even misleading. Make sure + // the span we're aiming at correspond to a `fn` body. + if block_sp == blk.span { + sp = ret_sp; + fn_span = Some(ident.span); + } + } + } + coerce.coerce_forced_unit( + self, + &self.misc(sp), + &mut |err| { + if let Some(expected_ty) = expected.only_has_type(self) { + self.consider_hint_about_removing_semicolon(blk, expected_ty, err); + } + if let Some(fn_span) = fn_span { + err.span_label( + fn_span, + "implicitly returns `()` as its body has no tail or `return` \ + expression", + ); + } + }, + false, + ); + } + } + }); + + if ctxt.may_break { + // If we can break from the block, then the block's exit is always reachable + // (... as long as the entry is reachable) - regardless of the tail of the block. + self.diverges.set(prev_diverges); + } + + let mut ty = ctxt.coerce.unwrap().complete(self); + + if self.has_errors.get() || ty.references_error() { + ty = self.tcx.ty_error() + } + + self.write_ty(blk.hir_id, ty); + + *self.ps.borrow_mut() = prev; + ty + } + + fn parent_item_span(&self, id: hir::HirId) -> Option<Span> { + let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id)); + match node { + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { + let body = self.tcx.hir().body(body_id); + if let ExprKind::Block(block, _) = &body.value.kind { + return Some(block.span); + } + } + _ => {} + } + None + } + + /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. + fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { + let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id)); + self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) + } + + /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. + pub(super) fn get_node_fn_decl( + &self, + node: Node<'tcx>, + ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> { + match node { + Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => { + // This is less than ideal, it will not suggest a return type span on any + // method called `main`, regardless of whether it is actually the entry point, + // but it will still present it as the reason for the expected type. + Some((&sig.decl, ident, ident.name != sym::main)) + } + Node::TraitItem(&hir::TraitItem { + ident, + kind: hir::TraitItemKind::Fn(ref sig, ..), + .. + }) => Some((&sig.decl, ident, true)), + Node::ImplItem(&hir::ImplItem { + ident, + kind: hir::ImplItemKind::Fn(ref sig, ..), + .. + }) => Some((&sig.decl, ident, false)), + _ => None, + } + } + + /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a + /// suggestion can be made, `None` otherwise. + pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> { + // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or + // `while` before reaching it, as block tail returns are not available in them. + self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { + let parent = self.tcx.hir().get(blk_id); + self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) + }) + } + + /// On implicit return expressions with mismatched types, provides the following suggestions: + /// + /// - Points out the method's return type as the reason for the expected type. + /// - Possible missing semicolon. + /// - Possible missing return type if the return type is the default, and not `fn main()`. + pub fn suggest_mismatched_types_on_tail( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + cause_span: Span, + blk_id: hir::HirId, + ) -> bool { + let expr = expr.peel_drop_temps(); + self.suggest_missing_semicolon(err, expr, expected, cause_span); + let mut pointing_at_return_type = false; + if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { + pointing_at_return_type = + self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest); + } + pointing_at_return_type + } + + /// When encountering an fn-like ctor that needs to unify with a value, check whether calling + /// the ctor would successfully solve the type mismatch and if so, suggest it: + /// ``` + /// fn foo(x: usize) -> usize { x } + /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` + /// ``` + fn suggest_fn_call( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + let hir = self.tcx.hir(); + let (def_id, sig) = match *found.kind() { + ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)), + ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()), + _ => return false, + }; + + let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0; + let sig = self.normalize_associated_types_in(expr.span, &sig); + if self.can_coerce(sig.output(), expected) { + let (mut sugg_call, applicability) = if sig.inputs().is_empty() { + (String::new(), Applicability::MachineApplicable) + } else { + ("...".to_string(), Applicability::HasPlaceholders) + }; + let mut msg = "call this function"; + match hir.get_if_local(def_id) { + Some( + Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. }) + | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(_, body_id), .. + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), + .. + }), + ) => { + let body = hir.body(*body_id); + sugg_call = body + .params + .iter() + .map(|param| match ¶m.pat.kind { + hir::PatKind::Binding(_, _, ident, None) + if ident.name != kw::SelfLower => + { + ident.to_string() + } + _ => "_".to_string(), + }) + .collect::<Vec<_>>() + .join(", "); + } + Some(Node::Expr(hir::Expr { + kind: ExprKind::Closure(_, _, body_id, _, _), + span: full_closure_span, + .. + })) => { + if *full_closure_span == expr.span { + return false; + } + msg = "call this closure"; + let body = hir.body(*body_id); + sugg_call = body + .params + .iter() + .map(|param| match ¶m.pat.kind { + hir::PatKind::Binding(_, _, ident, None) + if ident.name != kw::SelfLower => + { + ident.to_string() + } + _ => "_".to_string(), + }) + .collect::<Vec<_>>() + .join(", "); + } + Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { + sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", "); + match def_id.as_local().map(|def_id| hir.def_kind(def_id)) { + Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { + msg = "instantiate this tuple variant"; + } + Some(DefKind::Ctor(CtorOf::Struct, _)) => { + msg = "instantiate this tuple struct"; + } + _ => {} + } + } + Some(Node::ForeignItem(hir::ForeignItem { + kind: hir::ForeignItemKind::Fn(_, idents, _), + .. + })) => { + sugg_call = idents + .iter() + .map(|ident| { + if ident.name != kw::SelfLower { + ident.to_string() + } else { + "_".to_string() + } + }) + .collect::<Vec<_>>() + .join(", ") + } + Some(Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)), + .. + })) => { + sugg_call = idents + .iter() + .map(|ident| { + if ident.name != kw::SelfLower { + ident.to_string() + } else { + "_".to_string() + } + }) + .collect::<Vec<_>>() + .join(", ") + } + _ => {} + } + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + &format!("use parentheses to {}", msg), + format!("({})", sugg_call), + applicability, + ); + return true; + } + false + } + + pub fn suggest_deref_ref_or_into( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + ) { + if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) { + err.span_suggestion(sp, msg, suggestion, applicability); + } else if let (ty::FnDef(def_id, ..), true) = + (&found.kind(), self.suggest_fn_call(err, expr, expected, found)) + { + if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { + let sp = self.sess().source_map().guess_head_span(sp); + err.span_label(sp, &format!("{} defined here", found)); + } + } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { + 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::repeat(&expr_text) + .zip(methods.iter()) + .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 sugg = if receiver.ends_with(".clone()") + && method_call_list.contains(&method_call.as_str()) + { + let max_len = receiver.rfind('.').unwrap(); + format!("{}{}", &receiver[..max_len], method_call) + } else { + if expr.precedence().order() < ExprPrecedence::MethodCall.order() { + format!("({}){}", receiver, method_call) + } else { + format!("{}{}", receiver, method_call) + } + }; + Some(if is_struct_pat_shorthand_field { + format!("{}: {}", receiver, sugg) + } else { + sugg + }) + } + }) + .peekable(); + if suggestions.peek().is_some() { + err.span_suggestions( + expr.span, + "try using a conversion method", + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + } + } + + /// When encountering the expected boxed value allocated in the stack, suggest allocating it + /// in the heap by calling `Box::new()`. + pub(super) fn suggest_boxing_when_appropriate( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if self.tcx.hir().is_inside_const_context(expr.hir_id) { + // Do not suggest `Box::new` in const context. + return; + } + if !expected.is_box() || found.is_box() { + return; + } + let boxed_found = self.tcx.mk_box(found); + if let (true, Ok(snippet)) = ( + self.can_coerce(boxed_found, expected), + self.sess().source_map().span_to_snippet(expr.span), + ) { + err.span_suggestion( + expr.span, + "store this in the heap by calling `Box::new`", + format!("Box::new({})", snippet), + Applicability::MachineApplicable, + ); + err.note( + "for more on the distinction between the stack and the heap, read \ + https://doc.rust-lang.org/book/ch15-01-box.html, \ + https://doc.rust-lang.org/rust-by-example/std/box.html, and \ + https://doc.rust-lang.org/std/boxed/index.html", + ); + } + } + + pub(super) fn note_internal_mutation_in_method( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if found != self.tcx.types.unit { + return; + } + if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind { + if self + .typeck_results + .borrow() + .expr_ty_adjusted_opt(rcvr) + .map_or(true, |ty| expected.peel_refs() != ty.peel_refs()) + { + return; + } + let mut sp = MultiSpan::from_span(path_segment.ident.span); + sp.push_span_label( + path_segment.ident.span, + format!( + "this call modifies {} in-place", + match rcvr.kind { + ExprKind::Path(QPath::Resolved( + None, + hir::Path { segments: [segment], .. }, + )) => format!("`{}`", segment.ident), + _ => "its receiver".to_string(), + } + ), + ); + sp.push_span_label( + rcvr.span, + "you probably want to use this value after calling the method...".to_string(), + ); + err.span_note( + sp, + &format!("method `{}` modifies its receiver in-place", path_segment.ident), + ); + err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); + } + } + + /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. + pub(super) fn suggest_calling_boxed_future_when_appropriate( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + // Handle #68197. + + if self.tcx.hir().is_inside_const_context(expr.hir_id) { + // Do not suggest `Box::new` in const context. + return false; + } + let pin_did = self.tcx.lang_items().pin_type(); + match expected.kind() { + ty::Adt(def, _) if Some(def.did) != pin_did => return false, + // This guards the `unwrap` and `mk_box` below. + _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false, + _ => {} + } + let boxed_found = self.tcx.mk_box(found); + let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap(); + if let (true, Ok(snippet)) = ( + self.can_coerce(new_found, expected), + self.sess().source_map().span_to_snippet(expr.span), + ) { + match found.kind() { + ty::Adt(def, _) if def.is_box() => { + err.help("use `Box::pin`"); + } + _ => { + err.span_suggestion( + expr.span, + "you need to pin and box this expression", + format!("Box::pin({})", snippet), + Applicability::MachineApplicable, + ); + } + } + true + } else { + false + } + } + + /// A common error is to forget to add a semicolon at the end of a block, e.g., + /// + /// ``` + /// fn foo() { + /// bar_that_returns_u32() + /// } + /// ``` + /// + /// This routine checks if the return expression in a block would make sense on its own as a + /// statement and the return type has been left as default or has been specified as `()`. If so, + /// it suggests adding a semicolon. + fn suggest_missing_semicolon( + &self, + err: &mut DiagnosticBuilder<'_>, + expression: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + cause_span: Span, + ) { + if expected.is_unit() { + // `BlockTailExpression` only relevant if the tail expr would be + // useful on its own. + match expression.kind { + ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Block(..) => { + err.span_suggestion( + cause_span.shrink_to_hi(), + "try adding a semicolon", + ";".to_string(), + Applicability::MachineApplicable, + ); + } + _ => (), + } + } + } + + /// A possible error is to forget to add a return type that is needed: + /// + /// ``` + /// fn foo() { + /// bar_that_returns_u32() + /// } + /// ``` + /// + /// This routine checks if the return type is left as default, the method is not part of an + /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return + /// type. + pub(super) fn suggest_missing_return_type( + &self, + err: &mut DiagnosticBuilder<'_>, + fn_decl: &hir::FnDecl<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + can_suggest: bool, + ) -> bool { + // Only suggest changing the return type for methods that + // haven't set a return type at all (and aren't `fn main()` or an impl). + match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) { + (&hir::FnRetTy::DefaultReturn(span), true, true, true) => { + err.span_suggestion( + span, + "try adding a return type", + format!("-> {} ", self.resolve_vars_with_obligations(found)), + Applicability::MachineApplicable, + ); + true + } + (&hir::FnRetTy::DefaultReturn(span), false, true, true) => { + err.span_label(span, "possibly return type missing here?"); + true + } + (&hir::FnRetTy::DefaultReturn(span), _, false, true) => { + // `fn main()` must return `()`, do not suggest changing return type + err.span_label(span, "expected `()` because of default return type"); + true + } + // expectation was caused by something else, not the default return + (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false, + (&hir::FnRetTy::Return(ref ty), _, _, _) => { + // Only point to return type if the expected type is the return type, as if they + // are not, the expectation must have been caused by something else. + debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); + let sp = ty.span; + let ty = AstConv::ast_ty_to_ty(self, ty); + debug!("suggest_missing_return_type: return type {:?}", ty); + debug!("suggest_missing_return_type: expected type {:?}", ty); + if ty.kind() == expected.kind() { + err.span_label(sp, format!("expected `{}` because of return type", expected)); + return true; + } + false + } + } + } + + /// A possible error is to forget to add `.await` when using futures: + /// + /// ``` + /// async fn make_u32() -> u32 { + /// 22 + /// } + /// + /// fn take_u32(x: u32) {} + /// + /// async fn foo() { + /// let x = make_u32(); + /// take_u32(x); + /// } + /// ``` + /// + /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the + /// expected type. If this is the case, and we are inside of an async body, it suggests adding + /// `.await` to the tail of the expression. + pub(super) fn suggest_missing_await( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found); + // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the + // body isn't `async`. + let item_id = self.tcx().hir().get_parent_node(self.body_id); + if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) { + let body = self.tcx().hir().body(body_id); + if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { + let sp = expr.span; + // Check for `Future` implementations by constructing a predicate to + // prove: `<T as Future>::Output == U` + let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp)); + let item_def_id = self + .tcx + .associated_items(future_trait) + .in_definition_order() + .next() + .unwrap() + .def_id; + // `<T as Future>::Output` + let projection_ty = ty::ProjectionTy { + // `T` + substs: self + .tcx + .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)), + // `Future::Output` + item_def_id, + }; + + let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate { + projection_ty, + ty: expected, + }) + .potentially_quantified(self.tcx, ty::PredicateKind::ForAll); + let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); + + debug!("suggest_missing_await: trying obligation {:?}", obligation); + + if self.infcx.predicate_may_hold(&obligation) { + debug!("suggest_missing_await: obligation held: {:?}", obligation); + if let Ok(code) = self.sess().source_map().span_to_snippet(sp) { + err.span_suggestion( + sp, + "consider using `.await` here", + format!("{}.await", code), + Applicability::MaybeIncorrect, + ); + } else { + debug!("suggest_missing_await: no snippet for {:?}", sp); + } + } else { + debug!("suggest_missing_await: obligation did not hold: {:?}", obligation) + } + } + } + } + + pub(super) fn suggest_missing_parentheses( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + ) { + let sp = self.tcx.sess.source_map().start_point(expr.span); + if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { + // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` + self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None); + } + } + + pub(super) fn note_need_for_fn_pointer( + &self, + err: &mut DiagnosticBuilder<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + let (sig, did, substs) = match (&expected.kind(), &found.kind()) { + (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { + let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); + let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); + if sig1 != sig2 { + return; + } + err.note( + "different `fn` items always have unique types, even if their signatures are \ + the same", + ); + (sig1, *did1, substs1) + } + (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { + let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs); + if sig1 != *sig2 { + return; + } + (sig1, *did, substs) + } + _ => return, + }; + err.help(&format!("change the expected type to be function pointer `{}`", sig)); + err.help(&format!( + "if the expected type is due to type inference, cast the expected `fn` to a function \ + pointer: `{} as {}`", + self.tcx.def_path_str_with_substs(did, substs), + sig + )); + } + + /// A common error is to add an extra semicolon: + /// + /// ``` + /// fn foo() -> usize { + /// 22; + /// } + /// ``` + /// + /// This routine checks if the final statement in a block is an + /// expression with an explicit semicolon whose type is compatible + /// with `expected_ty`. If so, it suggests removing the semicolon. + fn consider_hint_about_removing_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut DiagnosticBuilder<'_>, + ) { + if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) { + err.span_suggestion( + span_semi, + "consider removing this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + } + + pub(super) fn could_remove_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + ) -> Option<Span> { + // Be helpful when the user wrote `{... expr;}` and + // taking the `;` off is enough to fix the error. + let last_stmt = blk.stmts.last()?; + let last_expr = match last_stmt.kind { + hir::StmtKind::Semi(ref e) => e, + _ => return None, + }; + let last_expr_ty = self.node_ty(last_expr.hir_id); + if matches!(last_expr_ty.kind(), ty::Error(_)) + || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() + { + return None; + } + let original_span = original_sp(last_stmt.span, blk.span); + Some(original_span.with_lo(original_span.hi() - BytePos(1))) + } + + // Instantiates the given path, which must refer to an item with the given + // number of type parameters and type. + pub fn instantiate_value_path( + &self, + segments: &[hir::PathSegment<'_>], + self_ty: Option<Ty<'tcx>>, + res: Res, + span: Span, + hir_id: hir::HirId, + ) -> (Ty<'tcx>, Res) { + debug!( + "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})", + segments, self_ty, res, hir_id, + ); + + let tcx = self.tcx; + + let path_segs = match res { + Res::Local(_) | Res::SelfCtor(_) => vec![], + Res::Def(kind, def_id) => { + AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id) + } + _ => bug!("instantiate_value_path on {:?}", res), + }; + + let mut user_self_ty = None; + let mut is_alias_variant_ctor = false; + match res { + Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => { + if let Some(self_ty) = self_ty { + let adt_def = self_ty.ty_adt_def().unwrap(); + user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty }); + is_alias_variant_ctor = true; + } + } + Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { + let container = tcx.associated_item(def_id).container; + debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container); + match container { + ty::TraitContainer(trait_did) => { + callee::check_legal_trait_for_method_call(tcx, span, None, trait_did) + } + ty::ImplContainer(impl_def_id) => { + if segments.len() == 1 { + // `<T>::assoc` will end up here, and so + // can `T::assoc`. It this came from an + // inherent impl, we need to record the + // `T` for posterity (see `UserSelfTy` for + // details). + let self_ty = self_ty.expect("UFCS sugared assoc missing Self"); + user_self_ty = Some(UserSelfTy { impl_def_id, self_ty }); + } + } + } + } + _ => {} + } + + // Now that we have categorized what space the parameters for each + // segment belong to, let's sort out the parameters that the user + // provided (if any) into their appropriate spaces. We'll also report + // errors if type parameters are provided in an inappropriate place. + + let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); + let generics_has_err = AstConv::prohibit_generics( + self, + segments.iter().enumerate().filter_map(|(index, seg)| { + if !generic_segs.contains(&index) || is_alias_variant_ctor { + Some(seg) + } else { + None + } + }), + ); + + if let Res::Local(hid) = res { + let ty = self.local_ty(span, hid).decl_ty; + let ty = self.normalize_associated_types_in(span, &ty); + self.write_ty(hir_id, ty); + return (ty, res); + } + + if generics_has_err { + // Don't try to infer type parameters when prohibited generic arguments were given. + user_self_ty = None; + } + + // Now we have to compare the types that the user *actually* + // provided against the types that were *expected*. If the user + // did not provide any types, then we want to substitute inference + // variables. If the user provided some types, we may still need + // to add defaults. If the user provided *too many* types, that's + // a problem. + + let mut infer_args_for_err = FxHashSet::default(); + for &PathSeg(def_id, index) in &path_segs { + let seg = &segments[index]; + let generics = tcx.generics_of(def_id); + // Argument-position `impl Trait` is treated as a normal generic + // parameter internally, but we don't allow users to specify the + // parameter's value explicitly, so we have to do some error- + // checking here. + if let GenericArgCountResult { + correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }), + .. + } = AstConv::check_generic_arg_count_for_call( + tcx, span, &generics, &seg, false, // `is_method_call` + ) { + infer_args_for_err.insert(index); + self.set_tainted_by_errors(); // See issue #53251. + } + } + + let has_self = path_segs + .last() + .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self) + .unwrap_or(false); + + let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { + let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id)); + match *ty.kind() { + ty::Adt(adt_def, substs) if adt_def.has_ctor() => { + let variant = adt_def.non_enum_variant(); + let ctor_def_id = variant.ctor_def_id.unwrap(); + ( + Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id), + Some(substs), + ) + } + _ => { + let mut err = tcx.sess.struct_span_err( + span, + "the `Self` constructor can only be used with tuple or unit structs", + ); + if let Some(adt_def) = ty.ty_adt_def() { + match adt_def.adt_kind() { + AdtKind::Enum => { + err.help("did you mean to use one of the enum's variants?"); + } + AdtKind::Struct | AdtKind::Union => { + err.span_suggestion( + span, + "use curly brackets", + String::from("Self { /* fields */ }"), + Applicability::HasPlaceholders, + ); + } + } + } + err.emit(); + + return (tcx.ty_error(), res); + } + } + } else { + (res, None) + }; + let def_id = res.def_id(); + + // The things we are substituting into the type should not contain + // escaping late-bound regions, and nor should the base type scheme. + let ty = tcx.type_of(def_id); + + let arg_count = GenericArgCountResult { + explicit_late_bound: ExplicitLateBound::No, + correct: if infer_args_for_err.is_empty() { + Ok(()) + } else { + Err(GenericArgCountMismatch::default()) + }, + }; + + let substs = self_ctor_substs.unwrap_or_else(|| { + AstConv::create_substs_for_generic_args( + tcx, + def_id, + &[][..], + has_self, + self_ty, + arg_count, + // Provide the generic args, and whether types should be inferred. + |def_id| { + if let Some(&PathSeg(_, index)) = + path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) + { + // If we've encountered an `impl Trait`-related error, we're just + // going to infer the arguments for better error messages. + if !infer_args_for_err.contains(&index) { + // Check whether the user has provided generic arguments. + if let Some(ref data) = segments[index].args { + return (Some(data), segments[index].infer_args); + } + } + return (None, segments[index].infer_args); + } + + (None, true) + }, + // Provide substitutions for parameters for which (valid) arguments have been provided. + |param, arg| match (¶m.kind, arg) { + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { + AstConv::ast_region_to_region(self, lt, Some(param)).into() + } + (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { + self.to_ty(ty).into() + } + (GenericParamDefKind::Const, GenericArg::Const(ct)) => { + self.const_arg_to_const(&ct.value, param.def_id).into() + } + _ => unreachable!(), + }, + // Provide substitutions for parameters for which arguments are inferred. + |substs, param, infer_args| { + match param.kind { + GenericParamDefKind::Lifetime => { + self.re_infer(Some(param), span).unwrap().into() + } + GenericParamDefKind::Type { has_default, .. } => { + if !infer_args && has_default { + // If we have a default, then we it doesn't matter that we're not + // inferring the type arguments: we provide the default where any + // is missing. + let default = tcx.type_of(param.def_id); + self.normalize_ty( + span, + default.subst_spanned(tcx, substs.unwrap(), Some(span)), + ) + .into() + } else { + // If no type arguments were provided, we have to infer them. + // This case also occurs as a result of some malformed input, e.g. + // a lifetime argument being given instead of a type parameter. + // Using inference instead of `Error` gives better error messages. + self.var_for_def(span, param) + } + } + GenericParamDefKind::Const => { + // FIXME(const_generics:defaults) + // No const parameters were provided, we have to infer them. + self.var_for_def(span, param) + } + } + }, + ) + }); + assert!(!substs.has_escaping_bound_vars()); + assert!(!ty.has_escaping_bound_vars()); + + // First, store the "user substs" for later. + self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); + + self.add_required_obligations(span, def_id, &substs); + + // Substitute the values for the type parameters into the type of + // the referenced item. + let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty); + + if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { + // In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method` + // is inherent, there is no `Self` parameter; instead, the impl needs + // type parameters, which we can infer by unifying the provided `Self` + // with the substituted impl type. + // This also occurs for an enum variant on a type alias. + let ty = tcx.type_of(impl_def_id); + + let impl_ty = self.instantiate_type_scheme(span, &substs, &ty); + match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) { + Ok(ok) => self.register_infer_ok_obligations(ok), + Err(_) => { + self.tcx.sess.delay_span_bug( + span, + &format!( + "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?", + self_ty, + impl_ty, + ), + ); + } + } + } + + self.check_rustc_args_require_const(def_id, hir_id, span); + + debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted); + self.write_substs(hir_id, substs); + + (ty_substituted, res) + } + + /// Add all the obligations that are required, substituting and normalized appropriately. + fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) { + let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs); + + for (i, mut 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); + } + } + + fn check_rustc_args_require_const(&self, def_id: DefId, hir_id: hir::HirId, span: Span) { + // We're only interested in functions tagged with + // #[rustc_args_required_const], so ignore anything that's not. + if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) { + return; + } + + // If our calling expression is indeed the function itself, we're good! + // If not, generate an error that this can only be called directly. + if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) { + if let ExprKind::Call(ref callee, ..) = expr.kind { + if callee.hir_id == hir_id { + return; + } + } + } + + self.tcx.sess.span_err( + span, + "this function can only be invoked directly, not through a function pointer", + ); + } + + /// Resolves `typ` by a single level if `typ` is a type variable. + /// If no resolution is possible, then an error is reported. + /// Numeric inference variables may be left unresolved. + pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + let ty = self.resolve_vars_with_obligations(ty); + if !ty.is_ty_var() { + ty + } else { + if !self.is_tainted_by_errors() { + self.need_type_info_err((**self).body_id, sp, ty, E0282) + .note("type must be known at this point") + .emit(); + } + let err = self.tcx.ty_error(); + self.demand_suptype(sp, err, ty); + err + } + } + + pub(super) fn with_breakable_ctxt<F: FnOnce() -> R, R>( + &self, + id: hir::HirId, + ctxt: BreakableCtxt<'tcx>, + f: F, + ) -> (BreakableCtxt<'tcx>, R) { + let index; + { + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + index = enclosing_breakables.stack.len(); + enclosing_breakables.by_id.insert(id, index); + enclosing_breakables.stack.push(ctxt); + } + let result = f(); + let ctxt = { + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + debug_assert!(enclosing_breakables.stack.len() == index + 1); + enclosing_breakables.by_id.remove(&id).expect("missing breakable context"); + enclosing_breakables.stack.pop().expect("missing breakable context") + }; + (ctxt, result) + } + + /// Instantiate a QueryResponse in a probe context, without a + /// good ObligationCause. + pub(super) fn probe_instantiate_query_response( + &self, + span: Span, + original_values: &OriginalQueryValues<'tcx>, + query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, + ) -> InferResult<'tcx, Ty<'tcx>> { + self.instantiate_query_response_and_region_obligations( + &traits::ObligationCause::misc(span, self.body_id), + self.param_env, + original_values, + query_result, + ) + } + + /// Returns `true` if an expression is contained inside the LHS of an assignment expression. + pub(super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool { + let mut contained_in_place = false; + + while let hir::Node::Expr(parent_expr) = + self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id)) + { + match &parent_expr.kind { + hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => { + if lhs.hir_id == expr_id { + contained_in_place = true; + break; + } + } + _ => (), + } + expr_id = parent_expr.hir_id; + } + + contained_in_place + } +} +impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { + type Target = Inherited<'a, 'tcx>; + fn deref(&self) -> &Self::Target { + &self.inh + } +} + +impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn item_def_id(&self) -> Option<DefId> { + None + } + + fn default_constness_for_trait_bounds(&self) -> hir::Constness { + // FIXME: refactor this into a method + let node = self.tcx.hir().get(self.body_id); + if let Some(fn_like) = FnLikeNode::from_node(node) { + fn_like.constness() + } else { + hir::Constness::NotConst + } + } + + fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> { + let tcx = self.tcx; + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let item_id = tcx.hir().ty_param_owner(hir_id); + let item_def_id = tcx.hir().local_def_id(item_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + ty::GenericPredicates { + parent: None, + predicates: tcx.arena.alloc_from_iter( + self.param_env.caller_bounds().iter().filter_map(|predicate| { + match predicate.skip_binders() { + ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => { + // HACK(eddyb) should get the original `Span`. + let span = tcx.def_span(def_id); + Some((predicate, span)) + } + _ => None, + } + }), + ), + } + } + + fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> { + let v = match def { + Some(def) => infer::EarlyBoundRegion(span, def.name), + None => infer::MiscVariable(span), + }; + Some(self.next_region_var(v)) + } + + fn allow_ty_infer(&self) -> bool { + true + } + + fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { + if let Some(param) = param { + if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() { + return ty; + } + unreachable!() + } else { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }) + } + } + + fn ct_infer( + &self, + ty: Ty<'tcx>, + param: Option<&ty::GenericParamDef>, + span: Span, + ) -> &'tcx Const<'tcx> { + if let Some(param) = param { + if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { + return ct; + } + unreachable!() + } else { + self.next_const_var( + ty, + ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, + ) + } + } + + fn projected_ty_from_poly_trait_ref( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'_>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Ty<'tcx> { + let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars( + span, + infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), + &poly_trait_ref, + ); + + let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item( + self, + self.tcx, + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + + self.tcx().mk_projection(item_def_id, item_substs) + } + + fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + if ty.has_escaping_bound_vars() { + ty // FIXME: normalization and escaping regions + } else { + self.normalize_associated_types_in(span, &ty) + } + } + + fn set_tainted_by_errors(&self) { + self.infcx.set_tainted_by_errors() + } + + fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { + self.write_ty(hir_id, ty) + } +} diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs new file mode 100644 index 00000000000..1d505cfa698 --- /dev/null +++ b/compiler/rustc_typeck/src/check/gather_locals.rs @@ -0,0 +1,120 @@ +use crate::check::{FnCtxt, LocalTy, UserType}; +use rustc_hir as hir; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::PatKind; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::ty::Ty; +use rustc_span::Span; +use rustc_trait_selection::traits; + +pub(super) struct GatherLocalsVisitor<'a, 'tcx> { + fcx: &'a FnCtxt<'a, 'tcx>, + parent_id: hir::HirId, +} + +impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { + pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>, parent_id: hir::HirId) -> Self { + Self { fcx, parent_id } + } + + fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> { + match ty_opt { + None => { + // Infer the variable's type. + let var_ty = self.fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }); + self.fcx + .locals + .borrow_mut() + .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty }); + var_ty + } + Some(typ) => { + // Take type that the user specified. + self.fcx.locals.borrow_mut().insert(nid, typ); + typ.revealed_ty + } + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { + type Map = intravisit::ErasedMap<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } + + // Add explicitly-declared locals. + fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + let local_ty = match local.ty { + Some(ref ty) => { + let o_ty = self.fcx.to_ty(&ty); + + let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings { + self.fcx.instantiate_opaque_types_from_value(self.parent_id, &o_ty, ty.span) + } else { + o_ty + }; + + let c_ty = self + .fcx + .inh + .infcx + .canonicalize_user_type_annotation(&UserType::Ty(revealed_ty)); + debug!( + "visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}", + ty.hir_id, o_ty, revealed_ty, c_ty + ); + self.fcx + .typeck_results + .borrow_mut() + .user_provided_types_mut() + .insert(ty.hir_id, c_ty); + + Some(LocalTy { decl_ty: o_ty, revealed_ty }) + } + None => None, + }; + self.assign(local.span, local.hir_id, local_ty); + + debug!( + "local variable {:?} is assigned type {}", + local.pat, + self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&local.hir_id).unwrap().decl_ty) + ); + intravisit::walk_local(self, local); + } + + // Add pattern bindings. + fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { + if let PatKind::Binding(_, _, ident, _) = p.kind { + let var_ty = self.assign(p.span, p.hir_id, None); + + if !self.fcx.tcx.features().unsized_locals { + self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id)); + } + + debug!( + "pattern binding {} is assigned to {} with type {:?}", + ident, + self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty), + var_ty + ); + } + intravisit::walk_pat(self, p); + } + + // Don't descend into the bodies of nested closures. + fn visit_fn( + &mut self, + _: intravisit::FnKind<'tcx>, + _: &'tcx hir::FnDecl<'tcx>, + _: hir::BodyId, + _: Span, + _: hir::HirId, + ) { + } +} diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs new file mode 100644 index 00000000000..7e580485c3d --- /dev/null +++ b/compiler/rustc_typeck/src/check/inherited.rs @@ -0,0 +1,167 @@ +use super::callee::DeferredCallResolution; +use super::MaybeInProgressTables; + +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_hir::def_id::{DefIdMap, LocalDefId}; +use rustc_hir::HirIdMap; +use rustc_infer::infer; +use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::{self, Span}; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::opaque_types::OpaqueTypeDecl; +use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt}; + +use std::cell::RefCell; +use std::ops::Deref; + +/// Closures defined within the function. For example: +/// +/// fn foo() { +/// bar(move|| { ... }) +/// } +/// +/// Here, the function `foo()` and the closure passed to +/// `bar()` will each have their own `FnCtxt`, but they will +/// share the inherited fields. +pub struct Inherited<'a, 'tcx> { + pub(super) infcx: InferCtxt<'a, 'tcx>, + + pub(super) typeck_results: super::MaybeInProgressTables<'a, 'tcx>, + + pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>, + + pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>, + + // Some additional `Sized` obligations badly affect type inference. + // These obligations are added in a later stage of typeck. + pub(super) deferred_sized_obligations: + RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>, + + // When we process a call like `c()` where `c` is a closure type, + // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or + // `FnOnce` closure. In that case, we defer full resolution of the + // call until upvar inference can kick in and make the + // decision. We keep these deferred resolutions grouped by the + // def-id of the closure, so that once we decide, we can easily go + // back and process them. + pub(super) deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolution<'tcx>>>>, + + pub(super) deferred_cast_checks: RefCell<Vec<super::cast::CastCheck<'tcx>>>, + + pub(super) deferred_generator_interiors: + RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>, + + // Opaque types found in explicit return types and their + // associated fresh inference variable. Writeback resolves these + // variables to get the concrete type, which can be used to + // 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions. + pub(super) opaque_types: RefCell<DefIdMap<OpaqueTypeDecl<'tcx>>>, + + /// A map from inference variables created from opaque + /// type instantiations (`ty::Infer`) to the actual opaque + /// type (`ty::Opaque`). Used during fallback to map unconstrained + /// opaque type inference variables to their corresponding + /// opaque type. + pub(super) opaque_types_vars: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>, + + pub(super) body_id: Option<hir::BodyId>, +} + +impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> { + type Target = InferCtxt<'a, 'tcx>; + fn deref(&self) -> &Self::Target { + &self.infcx + } +} + +/// Helper type of a temporary returned by `Inherited::build(...)`. +/// Necessary because we can't write the following bound: +/// `F: for<'b, 'tcx> where 'tcx FnOnce(Inherited<'b, 'tcx>)`. +pub struct InheritedBuilder<'tcx> { + infcx: infer::InferCtxtBuilder<'tcx>, + def_id: LocalDefId, +} + +impl Inherited<'_, 'tcx> { + pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { + let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; + + InheritedBuilder { + infcx: tcx.infer_ctxt().with_fresh_in_progress_typeck_results(hir_owner), + def_id, + } + } +} + +impl<'tcx> InheritedBuilder<'tcx> { + pub fn enter<F, R>(&mut self, f: F) -> R + where + F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R, + { + let def_id = self.def_id; + self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id))) + } +} + +impl Inherited<'a, 'tcx> { + pub(super) fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { + let tcx = infcx.tcx; + let item_id = tcx.hir().local_def_id_to_hir_id(def_id); + let body_id = tcx.hir().maybe_body_owned_by(item_id); + + Inherited { + typeck_results: MaybeInProgressTables { + maybe_typeck_results: infcx.in_progress_typeck_results, + }, + infcx, + fulfillment_cx: RefCell::new(TraitEngine::new(tcx)), + locals: RefCell::new(Default::default()), + deferred_sized_obligations: RefCell::new(Vec::new()), + deferred_call_resolutions: RefCell::new(Default::default()), + deferred_cast_checks: RefCell::new(Vec::new()), + deferred_generator_interiors: RefCell::new(Vec::new()), + opaque_types: RefCell::new(Default::default()), + opaque_types_vars: RefCell::new(Default::default()), + body_id, + } + } + + pub(super) fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) { + debug!("register_predicate({:?})", obligation); + if obligation.has_escaping_bound_vars() { + span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation); + } + self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation); + } + + pub(super) fn register_predicates<I>(&self, obligations: I) + where + I: IntoIterator<Item = traits::PredicateObligation<'tcx>>, + { + for obligation in obligations { + self.register_predicate(obligation); + } + } + + pub(super) fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T { + self.register_predicates(infer_ok.obligations); + infer_ok.value + } + + pub(super) fn normalize_associated_types_in<T>( + &self, + span: Span, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + value: &T, + ) -> T + where + T: TypeFoldable<'tcx>, + { + let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value); + self.register_infer_ok_obligations(ok) + } +} diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 47cea8649ef..b8230f52444 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -1,6 +1,10 @@ //! Type-checking for the rust-intrinsic and platform-intrinsic //! intrinsics that the compiler exposes. +use crate::errors::{ + SimdShuffleMissingLength, UnrecognizedAtomicOperation, UnrecognizedIntrinsicFunction, + WrongNumberOfTypeArgumentsToInstrinsic, +}; use crate::require_same_types; use rustc_errors::struct_span_err; @@ -41,17 +45,11 @@ fn equate_intrinsic_type<'tcx>( _ => bug!(), }; - struct_span_err!( - tcx.sess, + tcx.sess.emit_err(WrongNumberOfTypeArgumentsToInstrinsic { span, - E0094, - "intrinsic has wrong number of type \ - parameters: found {}, expected {}", - i_n_tps, - n_tps - ) - .span_label(span, format!("expected {} type parameter", n_tps)) - .emit(); + found: i_n_tps, + expected: n_tps, + }); return; } @@ -146,15 +144,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)), "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()), op => { - struct_span_err!( - tcx.sess, - it.span, - E0092, - "unrecognized atomic operation function: `{}`", - op - ) - .span_label(it.span, "unrecognized atomic operation") - .emit(); + tcx.sess.emit_err(UnrecognizedAtomicOperation { span: it.span, op }); return; } }; @@ -380,15 +370,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), other => { - struct_span_err!( - tcx.sess, - it.span, - E0093, - "unrecognized intrinsic function: `{}`", - other, - ) - .span_label(it.span, "unrecognized intrinsic") - .emit(); + tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other }); return; } }; @@ -468,14 +450,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) (2, params, param(1)) } Err(_) => { - struct_span_err!( - tcx.sess, - it.span, - E0439, - "invalid `simd_shuffle`, needs length: `{}`", - name - ) - .emit(); + tcx.sess.emit_err(SimdShuffleMissingLength { span: it.span, name }); return; } } diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index 41e37ee9752..fd2700b85e2 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -137,7 +137,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> Ty<'tcx> { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. - let mut autoderef = self.autoderef(self.span, unadjusted_self_ty); + let mut autoderef = + self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span); let (_, n) = match autoderef.nth(pick.autoderefs) { Some(n) => n, None => { @@ -269,7 +270,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.fcx .autoderef(self.span, self_ty) .include_raw_pointers() - .find_map(|(ty, _)| match ty.kind { + .find_map(|(ty, _)| match ty.kind() { ty::Dynamic(ref data, ..) => Some(closure( self, ty, @@ -464,7 +465,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } _ => None, }) - .find_map(|(trait_pred, span)| match trait_pred.self_ty().kind { + .find_map(|(trait_pred, span)| match trait_pred.self_ty().kind() { ty::Dynamic(..) => Some(span), _ => None, }) diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index c9a4df0317a..84bc3979e12 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -207,7 +207,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(span) = result.illegal_sized_bound { let mut needs_mut = false; - if let ty::Ref(region, t_type, mutability) = self_ty.kind { + if let ty::Ref(region, t_type, mutability) = self_ty.kind() { let trait_type = self .tcx .mk_ref(region, ty::TypeAndMut { ty: t_type, mutbl: mutability.invert() }); @@ -424,7 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; // Check if we have an enum variant. - if let ty::Adt(adt_def, _) = self_ty.kind { + if let ty::Adt(adt_def, _) = self_ty.kind() { if adt_def.is_enum() { let variant_def = adt_def .variants diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 7ac6681be1a..8a62031ec88 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -4,6 +4,7 @@ use super::NoMatchData; use super::{CandidateSource, ImplSource, TraitSource}; use crate::check::FnCtxt; +use crate::errors::MethodCallOnUnknownType; use crate::hir::def::DefKind; use crate::hir::def_id::DefId; @@ -11,7 +12,6 @@ use rustc_ast as ast; use rustc_ast::util::lev_distance::{find_best_match_for_name, lev_distance}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_infer::infer::canonical::OriginalQueryValues; @@ -376,14 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) if self.tcx.sess.rust_2018() { - struct_span_err!( - self.tcx.sess, - span, - E0699, - "the type of this value must be known to call a method on a raw pointer on \ - it" - ) - .emit(); + self.tcx.sess.emit_err(MethodCallOnUnknownType { span }); } else { self.tcx.struct_span_lint_hir( lint::builtin::TYVAR_BEHIND_RAW_POINTER, @@ -401,7 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .probe_instantiate_query_response(span, &orig_values, ty) .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); let ty = self.structurally_resolved_type(span, ty.value); - assert!(matches!(ty.kind, ty::Error(_))); + assert!(matches!(ty.kind(), ty::Error(_))); return Err(MethodError::NoMatch(NoMatchData::new( Vec::new(), Vec::new(), @@ -453,9 +446,10 @@ fn method_autoderef_steps<'tcx>( tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| { let ParamEnvAnd { param_env, value: self_ty } = goal; - let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty) - .include_raw_pointers() - .silence_errors(); + let mut autoderef = + Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) + .include_raw_pointers() + .silence_errors(); let mut reached_raw_pointer = false; let mut steps: Vec<_> = autoderef .by_ref() @@ -469,7 +463,7 @@ fn method_autoderef_steps<'tcx>( from_unsafe_deref: reached_raw_pointer, unsize: false, }; - if let ty::RawPtr(_) = ty.kind { + if let ty::RawPtr(_) = ty.kind() { // all the subsequent steps will be from_unsafe_deref reached_raw_pointer = true; } @@ -478,7 +472,7 @@ fn method_autoderef_steps<'tcx>( .collect(); let final_ty = autoderef.final_ty(true); - let opt_bad_ty = match final_ty.kind { + let opt_bad_ty = match final_ty.kind() { ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, ty: infcx @@ -587,7 +581,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("assemble_probe: self_ty={:?}", self_ty); let lang_items = self.tcx.lang_items(); - match self_ty.value.value.kind { + match *self_ty.value.value.kind() { ty::Dynamic(ref data, ..) => { if let Some(p) = data.principal() { // Subtle: we can't use `instantiate_query_response` here: using it will @@ -759,7 +753,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) { debug!("assemble_inherent_candidates_from_object(self_ty={:?})", self_ty); - let principal = match self_ty.kind { + let principal = match self_ty.kind() { ty::Dynamic(ref data, ..) => Some(data), _ => None, } @@ -806,7 +800,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.param_env.caller_bounds().iter().map(ty::Predicate::skip_binders).filter_map( |predicate| match predicate { ty::PredicateAtom::Trait(trait_predicate, _) => { - match trait_predicate.trait_ref.self_ty().kind { + match trait_predicate.trait_ref.self_ty().kind() { ty::Param(ref p) if *p == param_ty => { Some(ty::Binder::bind(trait_predicate.trait_ref)) } @@ -821,7 +815,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::PredicateAtom::ClosureKind(..) | ty::PredicateAtom::TypeOutlives(..) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => None, + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, }, ); @@ -1125,7 +1120,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pick.autoderefs = step.autoderefs; // Insert a `&*` or `&mut *` if this is a reference type: - if let ty::Ref(_, _, mutbl) = step.self_ty.value.value.kind { + if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() { pick.autoderefs += 1; pick.autoref = Some(mutbl); } diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 5cae66bc5da..e33a4e98c59 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -31,7 +31,7 @@ use super::{CandidateSource, MethodError, NoMatchData}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { let tcx = self.tcx; - match ty.kind { + match ty.kind() { // Not all of these (e.g., unsafe fns) implement `FnOnce`, // so we look for these beforehand. ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true, @@ -413,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } - if let ty::RawPtr(_) = &actual.kind { + if let ty::RawPtr(_) = &actual.kind() { err.note( "try using `<*const T>::as_ref()` to get a reference to the \ type behind the pointer: https://doc.rust-lang.org/std/\ @@ -450,7 +450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { let field_receiver = - self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind { + self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { ty::Adt(def, substs) if !def.is_enum() => { let variant = &def.non_enum_variant(); self.tcx.find_field_index(item_name, variant).map(|index| { @@ -545,7 +545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // original type that has the associated function for accurate suggestions. // (#61411) let ty = tcx.at(span).type_of(*impl_did); - match (&ty.peel_refs().kind, &actual.peel_refs().kind) { + match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) { (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => { // Use `actual` as it will have more `substs` filled in. self.ty_to_value_string(actual.peel_refs()) @@ -583,9 +583,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |self_ty: Ty<'tcx>, parent_pred: &ty::Predicate<'tcx>, obligation: &str| { // We don't care about regions here, so it's fine to skip the binder here. if let (ty::Param(_), ty::PredicateAtom::Trait(p, _)) = - (&self_ty.kind, parent_pred.skip_binders()) + (self_ty.kind(), parent_pred.skip_binders()) { - if let ty::Adt(def, _) = p.trait_ref.self_ty().kind { + if let ty::Adt(def, _) = p.trait_ref.self_ty().kind() { let node = def.did.as_local().map(|def_id| { self.tcx .hir() @@ -615,7 +615,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "doesn't satisfy `{}`", if obligation.len() > 50 { quiet } else { obligation } ); - match &self_ty.kind { + match &self_ty.kind() { // Point at the type that couldn't satisfy the bound. ty::Adt(def, _) => bound_spans.push((def_span(def.did), msg)), // Point at the trait object that couldn't satisfy the bound. @@ -837,7 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); self.suggest_use_candidates(&mut err, help, candidates); } - if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind { + if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { if needs_mut { let trait_type = self.tcx.mk_ref( region, @@ -856,7 +856,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Print out the type for use in value namespace. fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { - match ty.kind { + match ty.kind() { ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did, substs)), _ => self.ty_to_string(ty), } @@ -870,7 +870,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call: &hir::Expr<'_>, span: Span, ) { - if let ty::Opaque(def_id, _) = ty.kind { + if let ty::Opaque(def_id, _) = *ty.kind() { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); // Future::Output let item_def_id = self @@ -897,7 +897,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!( "suggest_await_before_method: normalized_ty={:?}, ty_kind={:?}", self.resolve_vars_if_possible(&normalized_ty), - normalized_ty.kind, + normalized_ty.kind(), ); let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true); debug!("suggest_await_before_method: is_method_exist={}", method_exists); @@ -1089,9 +1089,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { candidates.sort_by(|a, b| a.cmp(b).reverse()); candidates.dedup(); - let param_type = match rcvr_ty.kind { + let param_type = match rcvr_ty.kind() { ty::Param(param) => Some(param), - ty::Ref(_, ty, _) => match ty.kind { + ty::Ref(_, ty, _) => match ty.kind() { ty::Param(param) => Some(param), _ => None, }, @@ -1243,7 +1243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// autoderefs of `rcvr_ty`. fn type_derefs_to_local(&self, span: Span, rcvr_ty: Ty<'tcx>, source: SelfSource<'_>) -> bool { fn is_local(ty: Ty<'_>) -> bool { - match ty.kind { + match ty.kind() { ty::Adt(def, _) => def.did.is_local(), ty::Foreign(did) => did.is_local(), diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 031d48f8a60..97172d391ba 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -1,5 +1,3 @@ -// ignore-tidy-filelength - /*! # typeck: check phase @@ -68,13 +66,19 @@ pub mod _match; mod autoderef; mod callee; pub mod cast; +mod check; mod closure; pub mod coercion; mod compare_method; pub mod demand; +mod diverges; pub mod dropck; +mod expectation; mod expr; +mod fn_ctxt; +mod gather_locals; mod generator_interior; +mod inherited; pub mod intrinsic; pub mod method; mod op; @@ -85,81 +89,53 @@ mod upvar; mod wfcheck; pub mod writeback; -use crate::astconv::{ - AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg, +use check::{ + check_abi, check_fn, check_impl_item_well_formed, check_item_well_formed, check_mod_item_types, + check_trait_item_well_formed, }; -use rustc_ast as ast; -use rustc_ast::util::parser::ExprPrecedence; -use rustc_attr as attr; -use rustc_data_structures::captures::Captures; +pub use check::{check_item_type, check_wf_new}; +pub use diverges::Diverges; +pub use expectation::Expectation; +pub use fn_ctxt::FnCtxt; +pub use inherited::{Inherited, InheritedBuilder}; + +use crate::astconv::AstConv; +use crate::check::gather_locals::GatherLocalsVisitor; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::ErrorReported; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_errors::{pluralize, struct_span_err, Applicability}; use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::def::Res; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; +use rustc_hir::intravisit::Visitor; use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, GenericArg, HirIdMap, ItemKind, Node, PatKind, QPath}; +use rustc_hir::{HirIdMap, Node}; use rustc_index::bit_set::BitSet; use rustc_index::vec::Idx; -use rustc_infer::infer; -use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; -use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_infer::infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TyCtxtInferExt}; -use rustc_middle::hir::map::blocks::FnLikeNode; -use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, -}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::subst::{GenericArgKind, UserSelfTy, UserSubsts}; -use rustc_middle::ty::util::{Discr, IntTypeExt, Representability}; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::WithConstness; -use rustc_middle::ty::{self, AdtKind, CanonicalUserType, Const, DefIdTree, GenericParamDefKind}; -use rustc_middle::ty::{RegionKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, UserType}; -use rustc_session::config::{self, EntryFnType}; -use rustc_session::lint; +use rustc_middle::ty::{self, RegionKind, Ty, TyCtxt, UserType}; +use rustc_session::config; use rustc_session::parse::feature_err; use rustc_session::Session; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::source_map::{original_sp, DUMMY_SP}; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::source_map::DUMMY_SP; +use rustc_span::symbol::{kw, Ident}; use rustc_span::{self, BytePos, MultiSpan, Span}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::opaque_types::{InferCtxtExt as _, OpaqueTypeDecl}; +use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, -}; -use std::cell::{Cell, Ref, RefCell, RefMut}; -use std::cmp; -use std::collections::hash_map::Entry; -use std::iter; -use std::mem::replace; -use std::ops::{self, Deref}; -use std::slice; +use std::cell::{Ref, RefCell, RefMut}; use crate::require_c_abi_if_c_variadic; use crate::util::common::indenter; -use self::callee::DeferredCallResolution; -use self::coercion::{CoerceMany, DynamicCoerceMany}; -use self::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; -use self::method::{MethodCallee, SelfSource}; +use self::coercion::DynamicCoerceMany; pub use self::Expectation::*; -use self::TupleArgumentsFlag::*; #[macro_export] macro_rules! type_error_struct { @@ -179,201 +155,6 @@ pub struct LocalTy<'tcx> { revealed_ty: Ty<'tcx>, } -/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field. -#[derive(Copy, Clone)] -struct MaybeInProgressTables<'a, 'tcx> { - maybe_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>, -} - -impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> { - fn borrow(self) -> Ref<'a, ty::TypeckResults<'tcx>> { - match self.maybe_typeck_results { - Some(typeck_results) => typeck_results.borrow(), - None => bug!( - "MaybeInProgressTables: inh/fcx.typeck_results.borrow() with no typeck results" - ), - } - } - - fn borrow_mut(self) -> RefMut<'a, ty::TypeckResults<'tcx>> { - match self.maybe_typeck_results { - Some(typeck_results) => typeck_results.borrow_mut(), - None => bug!( - "MaybeInProgressTables: inh/fcx.typeck_results.borrow_mut() with no typeck results" - ), - } - } -} - -/// Closures defined within the function. For example: -/// -/// fn foo() { -/// bar(move|| { ... }) -/// } -/// -/// Here, the function `foo()` and the closure passed to -/// `bar()` will each have their own `FnCtxt`, but they will -/// share the inherited fields. -pub struct Inherited<'a, 'tcx> { - infcx: InferCtxt<'a, 'tcx>, - - typeck_results: MaybeInProgressTables<'a, 'tcx>, - - locals: RefCell<HirIdMap<LocalTy<'tcx>>>, - - fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>, - - // Some additional `Sized` obligations badly affect type inference. - // These obligations are added in a later stage of typeck. - deferred_sized_obligations: RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>, - - // When we process a call like `c()` where `c` is a closure type, - // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or - // `FnOnce` closure. In that case, we defer full resolution of the - // call until upvar inference can kick in and make the - // decision. We keep these deferred resolutions grouped by the - // def-id of the closure, so that once we decide, we can easily go - // back and process them. - deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolution<'tcx>>>>, - - deferred_cast_checks: RefCell<Vec<cast::CastCheck<'tcx>>>, - - deferred_generator_interiors: RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>, - - // Opaque types found in explicit return types and their - // associated fresh inference variable. Writeback resolves these - // variables to get the concrete type, which can be used to - // 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions. - opaque_types: RefCell<DefIdMap<OpaqueTypeDecl<'tcx>>>, - - /// A map from inference variables created from opaque - /// type instantiations (`ty::Infer`) to the actual opaque - /// type (`ty::Opaque`). Used during fallback to map unconstrained - /// opaque type inference variables to their corresponding - /// opaque type. - opaque_types_vars: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>, - - body_id: Option<hir::BodyId>, -} - -impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> { - type Target = InferCtxt<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.infcx - } -} - -/// When type-checking an expression, we propagate downward -/// whatever type hint we are able in the form of an `Expectation`. -#[derive(Copy, Clone, Debug)] -pub enum Expectation<'tcx> { - /// We know nothing about what type this expression should have. - NoExpectation, - - /// This expression should have the type given (or some subtype). - ExpectHasType(Ty<'tcx>), - - /// This expression will be cast to the `Ty`. - ExpectCastableToType(Ty<'tcx>), - - /// This rvalue expression will be wrapped in `&` or `Box` and coerced - /// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`. - ExpectRvalueLikeUnsized(Ty<'tcx>), -} - -impl<'a, 'tcx> Expectation<'tcx> { - // Disregard "castable to" expectations because they - // can lead us astray. Consider for example `if cond - // {22} else {c} as u8` -- if we propagate the - // "castable to u8" constraint to 22, it will pick the - // type 22u8, which is overly constrained (c might not - // be a u8). In effect, the problem is that the - // "castable to" expectation is not the tightest thing - // we can say, so we want to drop it in this case. - // The tightest thing we can say is "must unify with - // else branch". Note that in the case of a "has type" - // constraint, this limitation does not hold. - - // If the expected type is just a type variable, then don't use - // an expected type. Otherwise, we might write parts of the type - // when checking the 'then' block which are incompatible with the - // 'else' branch. - fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { - match *self { - ExpectHasType(ety) => { - let ety = fcx.shallow_resolve(ety); - if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation } - } - ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety), - _ => NoExpectation, - } - } - - /// Provides an expectation for an rvalue expression given an *optional* - /// hint, which is not required for type safety (the resulting type might - /// be checked higher up, as is the case with `&expr` and `box expr`), but - /// is useful in determining the concrete type. - /// - /// The primary use case is where the expected type is a fat pointer, - /// like `&[isize]`. For example, consider the following statement: - /// - /// let x: &[isize] = &[1, 2, 3]; - /// - /// In this case, the expected type for the `&[1, 2, 3]` expression is - /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the - /// expectation `ExpectHasType([isize])`, that would be too strong -- - /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`. - /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced - /// to the type `&[isize]`. Therefore, we propagate this more limited hint, - /// which still is useful, because it informs integer literals and the like. - /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 - /// for examples of where this comes up,. - fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> { - match fcx.tcx.struct_tail_without_normalization(ty).kind { - ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty), - _ => ExpectHasType(ty), - } - } - - // Resolves `expected` by a single level if it is a variable. If - // there is no expected type or resolution is not possible (e.g., - // no constraints yet present), just returns `None`. - fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { - match self { - NoExpectation => NoExpectation, - ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(&t)), - ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(&t)), - ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(&t)), - } - } - - fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> { - match self.resolve(fcx) { - NoExpectation => None, - ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty), - } - } - - /// It sometimes happens that we want to turn an expectation into - /// a **hard constraint** (i.e., something that must be satisfied - /// for the program to type-check). `only_has_type` will return - /// such a constraint, if it exists. - fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> { - match self.resolve(fcx) { - ExpectHasType(ty) => Some(ty), - NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None, - } - } - - /// Like `only_has_type`, but instead of returning `None` if no - /// hard constraint exists, creates a fresh type variable. - fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> { - self.only_has_type(fcx).unwrap_or_else(|| { - fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }) - }) - } -} - #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Needs { MutPlace, @@ -436,81 +217,6 @@ pub enum PlaceOp { Index, } -/// Tracks whether executing a node may exit normally (versus -/// return/break/panic, which "diverge", leaving dead code in their -/// wake). Tracked semi-automatically (through type variables marked -/// as diverging), with some manual adjustments for control-flow -/// primitives (approximating a CFG). -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Diverges { - /// Potentially unknown, some cases converge, - /// others require a CFG to determine them. - Maybe, - - /// Definitely known to diverge and therefore - /// not reach the next sibling or its parent. - Always { - /// The `Span` points to the expression - /// that caused us to diverge - /// (e.g. `return`, `break`, etc). - span: Span, - /// In some cases (e.g. a `match` expression - /// where all arms diverge), we may be - /// able to provide a more informative - /// message to the user. - /// If this is `None`, a default message - /// will be generated, which is suitable - /// for most cases. - custom_note: Option<&'static str>, - }, - - /// Same as `Always` but with a reachability - /// warning already emitted. - WarnedAlways, -} - -// Convenience impls for combining `Diverges`. - -impl ops::BitAnd for Diverges { - type Output = Self; - fn bitand(self, other: Self) -> Self { - cmp::min(self, other) - } -} - -impl ops::BitOr for Diverges { - type Output = Self; - fn bitor(self, other: Self) -> Self { - cmp::max(self, other) - } -} - -impl ops::BitAndAssign for Diverges { - fn bitand_assign(&mut self, other: Self) { - *self = *self & other; - } -} - -impl ops::BitOrAssign for Diverges { - fn bitor_assign(&mut self, other: Self) { - *self = *self | other; - } -} - -impl Diverges { - /// Creates a `Diverges::Always` with the provided `span` and the default note message. - fn always(span: Span) -> Diverges { - Diverges::Always { span, custom_note: None } - } - - fn is_always(self) -> bool { - // Enum comparison ignores the - // contents of fields, so we just - // fill them in with garbage here. - self >= Diverges::Always { span: DUMMY_SP, custom_note: None } - } -} - pub struct BreakableCtxt<'tcx> { may_break: bool, @@ -539,221 +245,6 @@ impl<'tcx> EnclosingBreakables<'tcx> { } } -pub struct FnCtxt<'a, 'tcx> { - body_id: hir::HirId, - - /// The parameter environment used for proving trait obligations - /// in this function. This can change when we descend into - /// closures (as they bring new things into scope), hence it is - /// not part of `Inherited` (as of the time of this writing, - /// closures do not yet change the environment, but they will - /// eventually). - param_env: ty::ParamEnv<'tcx>, - - /// Number of errors that had been reported when we started - /// checking this function. On exit, if we find that *more* errors - /// have been reported, we will skip regionck and other work that - /// expects the types within the function to be consistent. - // FIXME(matthewjasper) This should not exist, and it's not correct - // if type checking is run in parallel. - err_count_on_creation: usize, - - /// If `Some`, this stores coercion information for returned - /// expressions. If `None`, this is in a context where return is - /// inappropriate, such as a const expression. - /// - /// This is a `RefCell<DynamicCoerceMany>`, which means that we - /// can track all the return expressions and then use them to - /// compute a useful coercion from the set, similar to a match - /// expression or other branching context. You can use methods - /// like `expected_ty` to access the declared return type (if - /// any). - ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>, - - /// First span of a return site that we find. Used in error messages. - ret_coercion_span: RefCell<Option<Span>>, - - resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, - - ps: RefCell<UnsafetyState>, - - /// Whether the last checked node generates a divergence (e.g., - /// `return` will set this to `Always`). In general, when entering - /// an expression or other node in the tree, the initial value - /// indicates whether prior parts of the containing expression may - /// have diverged. It is then typically set to `Maybe` (and the - /// old value remembered) for processing the subparts of the - /// current expression. As each subpart is processed, they may set - /// the flag to `Always`, etc. Finally, at the end, we take the - /// result and "union" it with the original value, so that when we - /// return the flag indicates if any subpart of the parent - /// expression (up to and including this part) has diverged. So, - /// if you read it after evaluating a subexpression `X`, the value - /// you get indicates whether any subexpression that was - /// evaluating up to and including `X` diverged. - /// - /// We currently use this flag only for diagnostic purposes: - /// - /// - To warn about unreachable code: if, after processing a - /// sub-expression but before we have applied the effects of the - /// current node, we see that the flag is set to `Always`, we - /// can issue a warning. This corresponds to something like - /// `foo(return)`; we warn on the `foo()` expression. (We then - /// update the flag to `WarnedAlways` to suppress duplicate - /// reports.) Similarly, if we traverse to a fresh statement (or - /// tail expression) from a `Always` setting, we will issue a - /// warning. This corresponds to something like `{return; - /// foo();}` or `{return; 22}`, where we would warn on the - /// `foo()` or `22`. - /// - /// An expression represents dead code if, after checking it, - /// the diverges flag is set to something other than `Maybe`. - diverges: Cell<Diverges>, - - /// Whether any child nodes have any type errors. - has_errors: Cell<bool>, - - enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>, - - inh: &'a Inherited<'a, 'tcx>, -} - -impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { - type Target = Inherited<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.inh - } -} - -/// Helper type of a temporary returned by `Inherited::build(...)`. -/// Necessary because we can't write the following bound: -/// `F: for<'b, 'tcx> where 'tcx FnOnce(Inherited<'b, 'tcx>)`. -pub struct InheritedBuilder<'tcx> { - infcx: infer::InferCtxtBuilder<'tcx>, - def_id: LocalDefId, -} - -impl Inherited<'_, 'tcx> { - pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { - let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; - - InheritedBuilder { - infcx: tcx.infer_ctxt().with_fresh_in_progress_typeck_results(hir_owner), - def_id, - } - } -} - -impl<'tcx> InheritedBuilder<'tcx> { - pub fn enter<F, R>(&mut self, f: F) -> R - where - F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R, - { - let def_id = self.def_id; - self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id))) - } -} - -impl Inherited<'a, 'tcx> { - fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { - let tcx = infcx.tcx; - let item_id = tcx.hir().local_def_id_to_hir_id(def_id); - let body_id = tcx.hir().maybe_body_owned_by(item_id); - - Inherited { - typeck_results: MaybeInProgressTables { - maybe_typeck_results: infcx.in_progress_typeck_results, - }, - infcx, - fulfillment_cx: RefCell::new(TraitEngine::new(tcx)), - locals: RefCell::new(Default::default()), - deferred_sized_obligations: RefCell::new(Vec::new()), - deferred_call_resolutions: RefCell::new(Default::default()), - deferred_cast_checks: RefCell::new(Vec::new()), - deferred_generator_interiors: RefCell::new(Vec::new()), - opaque_types: RefCell::new(Default::default()), - opaque_types_vars: RefCell::new(Default::default()), - body_id, - } - } - - fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) { - debug!("register_predicate({:?})", obligation); - if obligation.has_escaping_bound_vars() { - span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation); - } - self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation); - } - - fn register_predicates<I>(&self, obligations: I) - where - I: IntoIterator<Item = traits::PredicateObligation<'tcx>>, - { - for obligation in obligations { - self.register_predicate(obligation); - } - } - - fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T { - self.register_predicates(infer_ok.obligations); - infer_ok.value - } - - fn normalize_associated_types_in<T>( - &self, - span: Span, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value: &T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value); - self.register_infer_ok_obligations(ok) - } -} - -struct CheckItemTypesVisitor<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'tcx> { - fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) { - check_item_type(self.tcx, i); - } - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} -} - -pub fn check_wf_new(tcx: TyCtxt<'_>) { - let visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx); - tcx.hir().krate().par_visit_all_item_likes(&visit); -} - -fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx }); -} - -fn typeck_item_bodies(tcx: TyCtxt<'_>, crate_num: CrateNum) { - debug_assert!(crate_num == LOCAL_CRATE); - tcx.par_body_owners(|body_owner_def_id| { - tcx.ensure().typeck(body_owner_def_id); - }); -} - -fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - wfcheck::check_item_well_formed(tcx, def_id); -} - -fn check_trait_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - wfcheck::check_trait_item(tcx, def_id); -} - -fn check_impl_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - wfcheck::check_impl_item(tcx, def_id); -} - pub fn provide(providers: &mut Providers) { method::provide(providers); *providers = Providers { @@ -884,7 +375,7 @@ where } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { + match *ty.kind() { ty::Opaque(def_id, substs) => { debug!("fixup_opaque_types: found type {:?}", ty); // Here, we replace any inference variables that occur within @@ -900,7 +391,7 @@ where let old_param = substs[param.index as usize]; match old_param.unpack() { GenericArgKind::Type(old_ty) => { - if let ty::Infer(_) = old_ty.kind { + if let ty::Infer(_) = old_ty.kind() { // Replace inference type with a generic parameter self.tcx.mk_param_from_def(param) } else { @@ -1048,7 +539,7 @@ fn typeck_with_fallback<'tcx>( }; // Gather locals in statics (because of block expressions). - GatherLocalsVisitor { fcx: &fcx, parent_id: id }.visit_body(body); + GatherLocalsVisitor::new(&fcx, id).visit_body(body); fcx.check_expr_coercable_to_type(&body.value, revealed_ty, None); @@ -1135,127 +626,6 @@ fn typeck_with_fallback<'tcx>( typeck_results } -fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) { - if !tcx.sess.target.target.is_abi_supported(abi) { - struct_span_err!( - tcx.sess, - span, - E0570, - "The ABI `{}` is not supported for the current target", - abi - ) - .emit() - } -} - -struct GatherLocalsVisitor<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - parent_id: hir::HirId, -} - -impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { - fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> { - match ty_opt { - None => { - // Infer the variable's type. - let var_ty = self.fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }); - self.fcx - .locals - .borrow_mut() - .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty }); - var_ty - } - Some(typ) => { - // Take type that the user specified. - self.fcx.locals.borrow_mut().insert(nid, typ); - typ.revealed_ty - } - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } - - // Add explicitly-declared locals. - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { - let local_ty = match local.ty { - Some(ref ty) => { - let o_ty = self.fcx.to_ty(&ty); - - let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings { - self.fcx.instantiate_opaque_types_from_value(self.parent_id, &o_ty, ty.span) - } else { - o_ty - }; - - let c_ty = self - .fcx - .inh - .infcx - .canonicalize_user_type_annotation(&UserType::Ty(revealed_ty)); - debug!( - "visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}", - ty.hir_id, o_ty, revealed_ty, c_ty - ); - self.fcx - .typeck_results - .borrow_mut() - .user_provided_types_mut() - .insert(ty.hir_id, c_ty); - - Some(LocalTy { decl_ty: o_ty, revealed_ty }) - } - None => None, - }; - self.assign(local.span, local.hir_id, local_ty); - - debug!( - "local variable {:?} is assigned type {}", - local.pat, - self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&local.hir_id).unwrap().decl_ty) - ); - intravisit::walk_local(self, local); - } - - // Add pattern bindings. - fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { - if let PatKind::Binding(_, _, ident, _) = p.kind { - let var_ty = self.assign(p.span, p.hir_id, None); - - if !self.fcx.tcx.features().unsized_locals { - self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id)); - } - - debug!( - "pattern binding {} is assigned to {} with type {:?}", - ident, - self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty), - var_ty - ); - } - intravisit::walk_pat(self, p); - } - - // Don't descend into the bodies of nested closures. - fn visit_fn( - &mut self, - _: intravisit::FnKind<'tcx>, - _: &'tcx hir::FnDecl<'tcx>, - _: hir::BodyId, - _: Span, - _: hir::HirId, - ) { - } -} - /// When `check_fn` is invoked on a generator (i.e., a body that /// includes yield), it returns back some information about the yield /// points. @@ -1273,453 +643,6 @@ struct GeneratorTypes<'tcx> { movability: hir::Movability, } -/// Helper used for fns and closures. Does the grungy work of checking a function -/// body and returns the function context used for that purpose, since in the case of a fn item -/// there is still a bit more to do. -/// -/// * ... -/// * inherited: other fields inherited from the enclosing fn (if any) -fn check_fn<'a, 'tcx>( - inherited: &'a Inherited<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - fn_sig: ty::FnSig<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - fn_id: hir::HirId, - body: &'tcx hir::Body<'tcx>, - can_be_generator: Option<hir::Movability>, -) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) { - let mut fn_sig = fn_sig; - - debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env); - - // Create the function context. This is either derived from scratch or, - // in the case of closures, based on the outer context. - let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); - *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id); - - let tcx = fcx.tcx; - let sess = tcx.sess; - let hir = tcx.hir(); - - let declared_ret_ty = fn_sig.output(); - let revealed_ret_ty = - fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty, decl.output.span()); - debug!("check_fn: declared_ret_ty: {}, revealed_ret_ty: {}", declared_ret_ty, revealed_ret_ty); - fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty))); - fn_sig = tcx.mk_fn_sig( - fn_sig.inputs().iter().cloned(), - revealed_ret_ty, - fn_sig.c_variadic, - fn_sig.unsafety, - fn_sig.abi, - ); - - let span = body.value.span; - - fn_maybe_err(tcx, span, fn_sig.abi); - - if body.generator_kind.is_some() && can_be_generator.is_some() { - let yield_ty = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); - fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); - - // Resume type defaults to `()` if the generator has no argument. - let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit()); - - fcx.resume_yield_tys = Some((resume_ty, yield_ty)); - } - - let outer_def_id = tcx.closure_base_def_id(hir.local_def_id(fn_id).to_def_id()).expect_local(); - let outer_hir_id = hir.local_def_id_to_hir_id(outer_def_id); - GatherLocalsVisitor { fcx: &fcx, parent_id: outer_hir_id }.visit_body(body); - - // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` - // (as it's created inside the body itself, not passed in from outside). - let maybe_va_list = if fn_sig.c_variadic { - let span = body.params.last().unwrap().span; - let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span)); - let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); - - Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()])) - } else { - None - }; - - // Add formal parameters. - let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); - let inputs_fn = fn_sig.inputs().iter().copied(); - for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { - // Check the pattern. - let ty_span = try { inputs_hir?.get(idx)?.span }; - fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); - - // Check that argument is Sized. - // The check for a non-trivial pattern is a hack to avoid duplicate warnings - // for simple cases like `fn foo(x: Trait)`, - // where we would error once on the parameter as a whole, and once on the binding `x`. - if param.pat.simple_ident().is_none() && !tcx.features().unsized_locals { - fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); - } - - fcx.write_ty(param.hir_id, param_ty); - } - - inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); - - if let ty::Dynamic(..) = declared_ret_ty.kind { - // FIXME: We need to verify that the return type is `Sized` after the return expression has - // been evaluated so that we have types available for all the nodes being returned, but that - // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this - // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, - // while keeping the current ordering we will ignore the tail expression's type because we - // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` - // because we will trigger "unreachable expression" lints unconditionally. - // Because of all of this, we perform a crude check to know whether the simplest `!Sized` - // case that a newcomer might make, returning a bare trait, and in that case we populate - // the tail expression's type so that the suggestion will be correct, but ignore all other - // possible cases. - fcx.check_expr(&body.value); - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - tcx.sess.delay_span_bug(decl.output.span(), "`!Sized` return type"); - } else { - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - fcx.check_return_expr(&body.value); - } - - // We insert the deferred_generator_interiors entry after visiting the body. - // This ensures that all nested generators appear before the entry of this generator. - // resolve_generator_interiors relies on this property. - let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) { - let interior = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }); - fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind)); - - let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); - Some(GeneratorTypes { - resume_ty, - yield_ty, - interior, - movability: can_be_generator.unwrap(), - }) - } else { - None - }; - - // Finalize the return check by taking the LUB of the return types - // we saw and assigning it to the expected return type. This isn't - // really expected to fail, since the coercions would have failed - // earlier when trying to find a LUB. - // - // However, the behavior around `!` is sort of complex. In the - // event that the `actual_return_ty` comes back as `!`, that - // indicates that the fn either does not return or "returns" only - // values of type `!`. In this case, if there is an expected - // return type that is *not* `!`, that should be ok. But if the - // return type is being inferred, we want to "fallback" to `!`: - // - // let x = move || panic!(); - // - // To allow for that, I am creating a type variable with diverging - // fallback. This was deemed ever so slightly better than unifying - // the return value with `!` because it allows for the caller to - // make more assumptions about the return type (e.g., they could do - // - // let y: Option<u32> = Some(x()); - // - // which would then cause this return type to become `u32`, not - // `!`). - let coercion = fcx.ret_coercion.take().unwrap().into_inner(); - let mut actual_return_ty = coercion.complete(&fcx); - if actual_return_ty.is_never() { - actual_return_ty = fcx.next_diverging_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::DivergingFn, - span, - }); - } - fcx.demand_suptype(span, revealed_ret_ty, actual_return_ty); - - // Check that the main return type implements the termination trait. - if let Some(term_id) = tcx.lang_items().termination() { - if let Some((def_id, EntryFnType::Main)) = tcx.entry_fn(LOCAL_CRATE) { - let main_id = hir.local_def_id_to_hir_id(def_id); - if main_id == fn_id { - let substs = tcx.mk_substs_trait(declared_ret_ty, &[]); - let trait_ref = ty::TraitRef::new(term_id, substs); - let return_ty_span = decl.output.span(); - let cause = traits::ObligationCause::new( - return_ty_span, - fn_id, - ObligationCauseCode::MainFunctionType, - ); - - inherited.register_predicate(traits::Obligation::new( - cause, - param_env, - trait_ref.without_const().to_predicate(tcx), - )); - } - } - } - - // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` - if let Some(panic_impl_did) = tcx.lang_items().panic_impl() { - if panic_impl_did == hir.local_def_id(fn_id).to_def_id() { - if let Some(panic_info_did) = tcx.lang_items().panic_info() { - if declared_ret_ty.kind != ty::Never { - sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - let span = hir.span(fn_id); - if inputs.len() == 1 { - let arg_is_panic_info = match inputs[0].kind { - ty::Ref(region, ty, mutbl) => match ty.kind { - ty::Adt(ref adt, _) => { - adt.did == panic_info_did - && mutbl == hir::Mutability::Not - && *region != RegionKind::ReStatic - } - _ => false, - }, - _ => false, - }; - - if !arg_is_panic_info { - sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`"); - } - - if let Node::Item(item) = hir.get(fn_id) { - if let ItemKind::Fn(_, ref generics, _) = item.kind { - if !generics.params.is_empty() { - sess.span_err(span, "should have no type parameters"); - } - } - } - } else { - let span = sess.source_map().guess_head_span(span); - sess.span_err(span, "function should have one argument"); - } - } else { - sess.err("language item required, but not found: `panic_info`"); - } - } - } - - // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !` - if let Some(alloc_error_handler_did) = tcx.lang_items().oom() { - if alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() { - if let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() { - if declared_ret_ty.kind != ty::Never { - sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - let span = hir.span(fn_id); - if inputs.len() == 1 { - let arg_is_alloc_layout = match inputs[0].kind { - ty::Adt(ref adt, _) => adt.did == alloc_layout_did, - _ => false, - }; - - if !arg_is_alloc_layout { - sess.span_err(decl.inputs[0].span, "argument should be `Layout`"); - } - - if let Node::Item(item) = hir.get(fn_id) { - if let ItemKind::Fn(_, ref generics, _) = item.kind { - if !generics.params.is_empty() { - sess.span_err( - span, - "`#[alloc_error_handler]` function should have no type \ - parameters", - ); - } - } - } - } else { - let span = sess.source_map().guess_head_span(span); - sess.span_err(span, "function should have one argument"); - } - } else { - sess.err("language item required, but not found: `alloc_layout`"); - } - } - } - - (fcx, gen_ty) -} - -fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { - let def_id = tcx.hir().local_def_id(id); - let def = tcx.adt_def(def_id); - def.destructor(tcx); // force the destructor to be evaluated - check_representable(tcx, span, def_id); - - if def.repr.simd() { - check_simd(tcx, span, def_id); - } - - check_transparent(tcx, span, def); - check_packed(tcx, span, def); -} - -fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { - let def_id = tcx.hir().local_def_id(id); - let def = tcx.adt_def(def_id); - def.destructor(tcx); // force the destructor to be evaluated - check_representable(tcx, span, def_id); - check_transparent(tcx, span, def); - check_union_fields(tcx, span, def_id); - check_packed(tcx, span, def); -} - -/// When the `#![feature(untagged_unions)]` gate is active, -/// check that the fields of the `union` does not contain fields that need dropping. -fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { - let item_type = tcx.type_of(item_def_id); - if let ty::Adt(def, substs) = item_type.kind { - assert!(def.is_union()); - let fields = &def.non_enum_variant().fields; - 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) { - 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") - .emit(); - return false; - } - } - } else { - span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind); - } - true -} - -/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` -/// projections that would result in "inheriting lifetimes". -fn check_opaque<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - span: Span, - origin: &hir::OpaqueTyOrigin, -) { - check_opaque_for_inheriting_lifetimes(tcx, def_id, span); - check_opaque_for_cycles(tcx, def_id, substs, span, origin); -} - -/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result -/// in "inheriting lifetimes". -fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { - let item = tcx.hir().expect_item(tcx.hir().local_def_id_to_hir_id(def_id)); - debug!( - "check_opaque_for_inheriting_lifetimes: def_id={:?} span={:?} item={:?}", - def_id, span, item - ); - - #[derive(Debug)] - struct ProhibitOpaqueVisitor<'tcx> { - opaque_identity_ty: Ty<'tcx>, - generics: &'tcx ty::Generics, - ty: Option<Ty<'tcx>>, - }; - - impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t); - if t != self.opaque_identity_ty && t.super_visit_with(self) { - self.ty = Some(t); - return true; - } - false - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - debug!("check_opaque_for_inheriting_lifetimes: (visit_region) r={:?}", r); - if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r { - return *index < self.generics.parent_count as u32; - } - - r.super_visit_with(self) - } - - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { - if let ty::ConstKind::Unevaluated(..) = c.val { - // FIXME(#72219) We currenctly don't detect lifetimes within substs - // which would violate this check. Even though the particular substitution is not used - // within the const, this should still be fixed. - return false; - } - c.super_visit_with(self) - } - } - - if let ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn, - .. - }) = item.kind - { - let mut visitor = ProhibitOpaqueVisitor { - opaque_identity_ty: tcx.mk_opaque( - def_id.to_def_id(), - InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), - ), - generics: tcx.generics_of(def_id), - ty: None, - }; - let prohibit_opaque = tcx - .predicates_of(def_id) - .predicates - .iter() - .any(|(predicate, _)| predicate.visit_with(&mut visitor)); - debug!( - "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor={:?}", - prohibit_opaque, visitor - ); - - if prohibit_opaque { - let is_async = match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin { - hir::OpaqueTyOrigin::AsyncFn => true, - _ => false, - }, - _ => unreachable!(), - }; - - let mut err = struct_span_err!( - tcx.sess, - span, - E0760, - "`{}` return type cannot contain a projection or `Self` that references lifetimes from \ - a parent scope", - if is_async { "async fn" } else { "impl Trait" }, - ); - - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) { - if snippet == "Self" { - if let Some(ty) = visitor.ty { - err.span_suggestion( - span, - "consider spelling out the type instead", - format!("{:?}", ty), - Applicability::MaybeIncorrect, - ); - } - } - } - err.emit(); - } - } -} - /// Given a `DefId` for an opaque type in return position, find its parent item's return /// expressions. fn get_owner_return_paths( @@ -1740,86 +663,6 @@ fn get_owner_return_paths( }) } -/// Emit an error for recursive opaque types. -/// -/// If this is a return `impl Trait`, find the item's return expressions and point at them. For -/// direct recursion this is enough, but for indirect recursion also point at the last intermediary -/// `impl Trait`. -/// -/// If all the return expressions evaluate to `!`, then we explain that the error will go away -/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. -fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { - let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); - - let mut label = false; - if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) { - let typeck_results = tcx.typeck(tcx.hir().local_def_id(hir_id)); - if visitor - .returns - .iter() - .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id)) - .all(|ty| matches!(ty.kind, ty::Never)) - { - let spans = visitor - .returns - .iter() - .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some()) - .map(|expr| expr.span) - .collect::<Vec<Span>>(); - let span_len = spans.len(); - if span_len == 1 { - err.span_label(spans[0], "this returned value is of `!` type"); - } else { - let mut multispan: MultiSpan = spans.clone().into(); - for span in spans { - multispan - .push_span_label(span, "this returned value is of `!` type".to_string()); - } - err.span_note(multispan, "these returned values have a concrete \"never\" type"); - } - err.help("this error will resolve once the item's body returns a concrete type"); - } else { - let mut seen = FxHashSet::default(); - seen.insert(span); - err.span_label(span, "recursive opaque type"); - label = true; - for (sp, ty) in visitor - .returns - .iter() - .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t))) - .filter(|(_, ty)| !matches!(ty.kind, ty::Never)) - { - struct VisitTypes(Vec<DefId>); - impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - match t.kind { - ty::Opaque(def, _) => { - self.0.push(def); - false - } - _ => t.super_visit_with(self), - } - } - } - let mut visitor = VisitTypes(vec![]); - ty.visit_with(&mut visitor); - for def_id in visitor.0 { - let ty_span = tcx.def_span(def_id); - if !seen.contains(&ty_span) { - err.span_label(ty_span, &format!("returning this opaque type `{}`", ty)); - seen.insert(ty_span); - } - err.span_label(sp, &format!("returning here with type `{}`", ty)); - } - } - } - } - if !label { - err.span_label(span, "cannot resolve opaque type"); - } - err.emit(); -} - /// Emit an error for recursive opaque types in a `let` binding. fn binding_opaque_type_cycle_error( tcx: TyCtxt<'tcx>, @@ -1829,7 +672,7 @@ fn binding_opaque_type_cycle_error( ) { let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); err.span_label(span, "cannot resolve opaque type"); - // Find the the owner that declared this `impl Trait` type. + // Find the owner that declared this `impl Trait` type. let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let mut prev_hir_id = hir_id; let mut hir_id = tcx.hir().get_parent_node(hir_id); @@ -1880,33 +723,6 @@ fn binding_opaque_type_cycle_error( err.emit(); } -fn async_opaque_type_cycle_error(tcx: TyCtxt<'tcx>, span: Span) { - struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing") - .span_label(span, "recursive `async fn`") - .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") - .emit(); -} - -/// Checks that an opaque type does not contain cycles. -fn check_opaque_for_cycles<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - span: Span, - origin: &hir::OpaqueTyOrigin, -) { - if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs) - { - match origin { - hir::OpaqueTyOrigin::AsyncFn => async_opaque_type_cycle_error(tcx, span), - hir::OpaqueTyOrigin::Binding => { - binding_opaque_type_cycle_error(tcx, def_id, span, partially_expanded_type) - } - _ => opaque_type_cycle_error(tcx, def_id, span), - } - } -} - // Forbid defining intrinsics in Rust code, // as they must always be defined by the compiler. fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) { @@ -1915,126 +731,6 @@ fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) { } } -pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { - debug!( - "check_item_type(it.hir_id={}, it.name={})", - it.hir_id, - tcx.def_path_str(tcx.hir().local_def_id(it.hir_id).to_def_id()) - ); - let _indenter = indenter(); - match it.kind { - // Consts can play a role in type-checking, so they are included here. - hir::ItemKind::Static(..) => { - let def_id = tcx.hir().local_def_id(it.hir_id); - tcx.ensure().typeck(def_id); - maybe_check_static_with_link_section(tcx, def_id, it.span); - } - hir::ItemKind::Const(..) => { - tcx.ensure().typeck(tcx.hir().local_def_id(it.hir_id)); - } - hir::ItemKind::Enum(ref enum_definition, _) => { - check_enum(tcx, it.span, &enum_definition.variants, it.hir_id); - } - hir::ItemKind::Fn(..) => {} // entirely within check_item_body - hir::ItemKind::Impl { ref items, .. } => { - debug!("ItemKind::Impl {} with id {}", it.ident, it.hir_id); - let impl_def_id = tcx.hir().local_def_id(it.hir_id); - if let Some(impl_trait_ref) = tcx.impl_trait_ref(impl_def_id) { - check_impl_items_against_trait(tcx, it.span, impl_def_id, impl_trait_ref, items); - let trait_def_id = impl_trait_ref.def_id; - check_on_unimplemented(tcx, trait_def_id, it); - } - } - hir::ItemKind::Trait(_, _, _, _, ref items) => { - let def_id = tcx.hir().local_def_id(it.hir_id); - check_on_unimplemented(tcx, def_id.to_def_id(), it); - - for item in items.iter() { - let item = tcx.hir().trait_item(item.id); - if let hir::TraitItemKind::Fn(sig, _) = &item.kind { - let abi = sig.header.abi; - fn_maybe_err(tcx, item.ident.span, abi); - } - } - } - hir::ItemKind::Struct(..) => { - check_struct(tcx, it.hir_id, it.span); - } - hir::ItemKind::Union(..) => { - check_union(tcx, it.hir_id, it.span); - } - hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { - // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting - // `async-std` (and `pub async fn` in general). - // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! - // See https://github.com/rust-lang/rust/issues/75100 - if !tcx.sess.opts.actually_rustdoc { - let def_id = tcx.hir().local_def_id(it.hir_id); - - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - check_opaque(tcx, def_id, substs, it.span, &origin); - } - } - hir::ItemKind::TyAlias(..) => { - let def_id = tcx.hir().local_def_id(it.hir_id); - let pty_ty = tcx.type_of(def_id); - let generics = tcx.generics_of(def_id); - check_type_params_are_used(tcx, &generics, pty_ty); - } - hir::ItemKind::ForeignMod(ref m) => { - check_abi(tcx, it.span, m.abi); - - if m.abi == Abi::RustIntrinsic { - for item in m.items { - intrinsic::check_intrinsic_type(tcx, item); - } - } else if m.abi == Abi::PlatformIntrinsic { - for item in m.items { - intrinsic::check_platform_intrinsic_type(tcx, item); - } - } else { - for item in m.items { - let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id)); - let own_counts = generics.own_counts(); - if generics.params.len() - own_counts.lifetimes != 0 { - let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) { - (_, 0) => ("type", "types", Some("u32")), - // We don't specify an example value, because we can't generate - // a valid value for any type. - (0, _) => ("const", "consts", None), - _ => ("type or const", "types or consts", None), - }; - struct_span_err!( - tcx.sess, - item.span, - E0044, - "foreign items may not have {} parameters", - kinds, - ) - .span_label(item.span, &format!("can't have {} parameters", kinds)) - .help( - // FIXME: once we start storing spans for type arguments, turn this - // into a suggestion. - &format!( - "replace the {} parameters with concrete {}{}", - kinds, - kinds_pl, - egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(), - ), - ) - .emit(); - } - - if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.kind { - require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span); - } - } - } - } - _ => { /* nothing to do */ } - } -} - 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") { @@ -2055,8 +751,8 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is // the consumer's responsibility to ensure all bytes that have been read // have defined values. - match tcx.const_eval_poly(id.to_def_id()) { - Ok(ConstValue::ByRef { alloc, .. }) => { + match tcx.eval_static_initializer(id.to_def_id()) { + Ok(alloc) => { if alloc.relocations().len() != 0 { let msg = "statics with a custom `#[link_section]` must be a \ simple list of bytes on the wasm target with no \ @@ -2064,17 +760,10 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S tcx.sess.span_err(span, msg); } } - Ok(_) => bug!("Matching on non-ByRef static"), Err(_) => {} } } -fn check_on_unimplemented(tcx: TyCtxt<'_>, trait_def_id: DefId, item: &hir::Item<'_>) { - let item_def_id = tcx.hir().local_def_id(item.hir_id); - // an error would be reported if this fails. - let _ = traits::OnUnimplementedDirective::of_item(tcx, trait_def_id, item_def_id.to_def_id()); -} - fn report_forbidden_specialization( tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>, @@ -2106,248 +795,6 @@ fn report_forbidden_specialization( err.emit(); } -fn check_specialization_validity<'tcx>( - tcx: TyCtxt<'tcx>, - trait_def: &ty::TraitDef, - trait_item: &ty::AssocItem, - impl_id: DefId, - impl_item: &hir::ImplItem<'_>, -) { - let kind = match impl_item.kind { - hir::ImplItemKind::Const(..) => ty::AssocKind::Const, - hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, - hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type, - }; - - let ancestors = match trait_def.ancestors(tcx, impl_id) { - Ok(ancestors) => ancestors, - Err(_) => return, - }; - let mut ancestor_impls = ancestors - .skip(1) - .filter_map(|parent| { - if parent.is_from_trait() { - None - } else { - Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id))) - } - }) - .peekable(); - - if ancestor_impls.peek().is_none() { - // No parent, nothing to specialize. - return; - } - - let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| { - match parent_item { - // Parent impl exists, and contains the parent item we're trying to specialize, but - // doesn't mark it `default`. - Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => { - Some(Err(parent_impl.def_id())) - } - - // Parent impl contains item and makes it specializable. - Some(_) => Some(Ok(())), - - // Parent impl doesn't mention the item. This means it's inherited from the - // grandparent. In that case, if parent is a `default impl`, inherited items use the - // "defaultness" from the grandparent, else they are final. - None => { - if tcx.impl_defaultness(parent_impl.def_id()).is_default() { - None - } else { - Some(Err(parent_impl.def_id())) - } - } - } - }); - - // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the - // item. This is allowed, the item isn't actually getting specialized here. - let result = opt_result.unwrap_or(Ok(())); - - if let Err(parent_impl) = result { - report_forbidden_specialization(tcx, impl_item, parent_impl); - } -} - -fn check_impl_items_against_trait<'tcx>( - tcx: TyCtxt<'tcx>, - full_impl_span: Span, - impl_id: LocalDefId, - impl_trait_ref: ty::TraitRef<'tcx>, - impl_item_refs: &[hir::ImplItemRef<'_>], -) { - let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); - - // If the trait reference itself is erroneous (so the compilation is going - // to fail), skip checking the items here -- the `impl_item` table in `tcx` - // isn't populated for such impls. - if impl_trait_ref.references_error() { - return; - } - - // Negative impls are not expected to have any items - match tcx.impl_polarity(impl_id) { - ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {} - ty::ImplPolarity::Negative => { - if let [first_item_ref, ..] = impl_item_refs { - let first_item_span = tcx.hir().impl_item(first_item_ref.id).span; - struct_span_err!( - tcx.sess, - first_item_span, - E0749, - "negative impls cannot have any items" - ) - .emit(); - } - return; - } - } - - // Locate trait definition and items - let trait_def = tcx.trait_def(impl_trait_ref.def_id); - - let impl_items = || impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id)); - - // Check existing impl methods to see if they are both present in trait - // and compatible with trait signature - for impl_item in impl_items() { - let namespace = impl_item.kind.namespace(); - let ty_impl_item = tcx.associated_item(tcx.hir().local_def_id(impl_item.hir_id)); - let ty_trait_item = tcx - .associated_items(impl_trait_ref.def_id) - .find_by_name_and_namespace(tcx, ty_impl_item.ident, namespace, impl_trait_ref.def_id) - .or_else(|| { - // Not compatible, but needed for the error message - tcx.associated_items(impl_trait_ref.def_id) - .filter_by_name(tcx, ty_impl_item.ident, impl_trait_ref.def_id) - .next() - }); - - // Check that impl definition matches trait definition - if let Some(ty_trait_item) = ty_trait_item { - match impl_item.kind { - hir::ImplItemKind::Const(..) => { - // Find associated const definition. - if ty_trait_item.kind == ty::AssocKind::Const { - compare_const_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - ); - } else { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0323, - "item `{}` is an associated const, \ - which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ); - err.span_label(impl_item.span, "does not match trait"); - // We can only get the spans from local trait definition - // Same for E0324 and E0325 - if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) { - err.span_label(trait_span, "item in trait"); - } - err.emit() - } - } - hir::ImplItemKind::Fn(..) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - if ty_trait_item.kind == ty::AssocKind::Fn { - compare_impl_method( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } else { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0324, - "item `{}` is an associated method, \ - which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ); - err.span_label(impl_item.span, "does not match trait"); - if let Some(trait_span) = opt_trait_span { - err.span_label(trait_span, "item in trait"); - } - err.emit() - } - } - hir::ImplItemKind::TyAlias(_) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - if ty_trait_item.kind == ty::AssocKind::Type { - compare_ty_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } else { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0325, - "item `{}` is an associated type, \ - which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ); - err.span_label(impl_item.span, "does not match trait"); - if let Some(trait_span) = opt_trait_span { - err.span_label(trait_span, "item in trait"); - } - err.emit() - } - } - } - - check_specialization_validity( - tcx, - trait_def, - &ty_trait_item, - impl_id.to_def_id(), - impl_item, - ); - } - } - - // Check for missing items from trait - let mut missing_items = Vec::new(); - if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { - for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { - let is_implemented = ancestors - .leaf_def(tcx, trait_item.ident, trait_item.kind) - .map(|node_item| !node_item.defining_node.is_from_trait()) - .unwrap_or(false); - - if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { - if !trait_item.defaultness.has_value() { - missing_items.push(*trait_item); - } - } - } - } - - if !missing_items.is_empty() { - missing_items_err(tcx, impl_span, &missing_items, full_impl_span); - } -} - fn missing_items_err( tcx: TyCtxt<'_>, impl_span: Span, @@ -2426,7 +873,7 @@ fn bounds_from_generic_predicates<'tcx>( "<{}>", types .keys() - .filter_map(|t| match t.kind { + .filter_map(|t| match t.kind() { ty::Param(_) => Some(t.to_string()), // Avoid suggesting the following: // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {} @@ -2471,7 +918,7 @@ fn fn_sig_suggestion<'tcx>( .iter() .enumerate() .map(|(i, ty)| { - Some(match ty.kind { + Some(match ty.kind() { ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(), ty::Ref(reg, ref_ty, mutability) if i == 0 => { let reg = match &format!("{}", reg)[..] { @@ -2479,7 +926,7 @@ fn fn_sig_suggestion<'tcx>( reg => format!("{} ", reg), }; if assoc.fn_has_self_parameter { - match ref_ty.kind { + match ref_ty.kind() { ty::Param(param) if param.name == kw::SelfUpper => { format!("&{}{}self", reg, mutability.prefix_str()) } @@ -2487,14 +934,14 @@ fn fn_sig_suggestion<'tcx>( _ => format!("self: {}", ty), } } else { - format!("_: {:?}", ty) + format!("_: {}", ty) } } _ => { if assoc.fn_has_self_parameter && i == 0 { - format!("self: {:?}", ty) + format!("self: {}", ty) } else { - format!("_: {:?}", ty) + format!("_: {}", ty) } } }) @@ -2504,7 +951,7 @@ fn fn_sig_suggestion<'tcx>( .collect::<Vec<String>>() .join(", "); let output = sig.output(); - let output = if !output.is_unit() { format!(" -> {:?}", output) } else { String::new() }; + let output = if !output.is_unit() { format!(" -> {}", output) } else { String::new() }; let unsafety = sig.unsafety.prefix_str(); let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates); @@ -2542,164 +989,9 @@ fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { ty::AssocKind::Const => { let ty = tcx.type_of(assoc.def_id); let val = expr::ty_kind_suggestion(ty).unwrap_or("value"); - format!("const {}: {:?} = {};", assoc.ident, ty, val) - } - } -} - -/// Checks whether a type can be represented in memory. In particular, it -/// identifies types that contain themselves without indirection through a -/// pointer, which would mean their size is unbounded. -fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool { - let rty = tcx.type_of(item_def_id); - - // Check that it is possible to represent this type. This call identifies - // (1) types that contain themselves and (2) types that contain a different - // recursive type. It is only necessary to throw an error on those that - // contain themselves. For case 2, there must be an inner type that will be - // caught by case 1. - match rty.is_representable(tcx, sp) { - Representability::SelfRecursive(spans) => { - recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans); - return false; + format!("const {}: {} = {};", assoc.ident, ty, val) } - Representability::Representable | Representability::ContainsRecursive => (), } - true -} - -pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { - let t = tcx.type_of(def_id); - if let ty::Adt(def, substs) = t.kind { - if def.is_struct() { - let fields = &def.non_enum_variant().fields; - if fields.is_empty() { - struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit(); - return; - } - let e = fields[0].ty(tcx, substs); - if !fields.iter().all(|f| f.ty(tcx, substs) == e) { - struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous") - .span_label(sp, "SIMD elements must have the same type") - .emit(); - return; - } - match e.kind { - ty::Param(_) => { /* struct<T>(T, T, T, T) is ok */ } - _ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ } - _ => { - struct_span_err!( - tcx.sess, - sp, - E0077, - "SIMD vector element type should be machine type" - ) - .emit(); - return; - } - } - } - } -} - -fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: &ty::AdtDef) { - let repr = def.repr; - if repr.packed() { - for attr in tcx.get_attrs(def.did).iter() { - for r in attr::find_repr_attrs(&tcx.sess, attr) { - if let attr::ReprPacked(pack) = r { - if let Some(repr_pack) = repr.pack { - if pack as u64 != repr_pack.bytes() { - struct_span_err!( - tcx.sess, - sp, - E0634, - "type has conflicting packed representation hints" - ) - .emit(); - } - } - } - } - } - if repr.align.is_some() { - struct_span_err!( - tcx.sess, - sp, - E0587, - "type has conflicting packed and align representation hints" - ) - .emit(); - } else { - if let Some(def_spans) = check_packed_inner(tcx, def.did, &mut vec![]) { - let mut err = struct_span_err!( - tcx.sess, - sp, - E0588, - "packed type cannot transitively contain a `#[repr(align)]` type" - ); - - err.span_note( - tcx.def_span(def_spans[0].0), - &format!( - "`{}` has a `#[repr(align)]` attribute", - tcx.item_name(def_spans[0].0) - ), - ); - - if def_spans.len() > 2 { - let mut first = true; - for (adt_def, span) in def_spans.iter().skip(1).rev() { - let ident = tcx.item_name(*adt_def); - err.span_note( - *span, - &if first { - format!( - "`{}` contains a field of type `{}`", - tcx.type_of(def.did), - ident - ) - } else { - format!("...which contains a field of type `{}`", ident) - }, - ); - first = false; - } - } - - err.emit(); - } - } - } -} - -fn check_packed_inner( - tcx: TyCtxt<'_>, - def_id: DefId, - stack: &mut Vec<DefId>, -) -> Option<Vec<(DefId, Span)>> { - if let ty::Adt(def, substs) = tcx.type_of(def_id).kind { - if def.is_struct() || def.is_union() { - if def.repr.align.is_some() { - return Some(vec![(def.did, DUMMY_SP)]); - } - - stack.push(def_id); - for field in &def.non_enum_variant().fields { - if let ty::Adt(def, _) = field.ty(tcx, substs).kind { - if !stack.contains(&def.did) { - if let Some(mut defs) = check_packed_inner(tcx, def.did, stack) { - defs.push((def.did, field.ident.span)); - return Some(defs); - } - } - } - } - stack.pop(); - } - } - - None } /// Emit an error when encountering more or less than one variant in a transparent enum. @@ -2747,158 +1039,6 @@ fn bad_non_zero_sized_fields<'tcx>( err.emit(); } -fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: &'tcx ty::AdtDef) { - if !adt.repr.transparent() { - return; - } - let sp = tcx.sess.source_map().guess_head_span(sp); - - if adt.is_union() && !tcx.features().transparent_unions { - feature_err( - &tcx.sess.parse_sess, - sym::transparent_unions, - sp, - "transparent unions are unstable", - ) - .emit(); - } - - if adt.variants.len() != 1 { - bad_variant_count(tcx, adt, sp, adt.did); - if adt.variants.is_empty() { - // Don't bother checking the fields. No variants (and thus no fields) exist. - return; - } - } - - // For each field, figure out if it's known to be a ZST and align(1) - let field_infos = adt.all_fields().map(|field| { - let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did)); - let param_env = tcx.param_env(field.did); - let layout = tcx.layout_of(param_env.and(ty)); - // We are currently checking the type this field came from, so it must be local - let span = tcx.hir().span_if_local(field.did).unwrap(); - let zst = layout.map(|layout| layout.is_zst()).unwrap_or(false); - let align1 = layout.map(|layout| layout.align.abi.bytes() == 1).unwrap_or(false); - (span, zst, align1) - }); - - let non_zst_fields = - field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None }); - let non_zst_count = non_zst_fields.clone().count(); - if non_zst_count != 1 { - bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp); - } - for (span, zst, align1) in field_infos { - if zst && !align1 { - struct_span_err!( - tcx.sess, - span, - E0691, - "zero-sized field in transparent {} has alignment larger than 1", - adt.descr(), - ) - .span_label(span, "has alignment larger than 1") - .emit(); - } - } -} - -#[allow(trivial_numeric_casts)] -pub fn check_enum<'tcx>( - tcx: TyCtxt<'tcx>, - sp: Span, - vs: &'tcx [hir::Variant<'tcx>], - id: hir::HirId, -) { - let def_id = tcx.hir().local_def_id(id); - let def = tcx.adt_def(def_id); - def.destructor(tcx); // force the destructor to be evaluated - - if vs.is_empty() { - let attributes = tcx.get_attrs(def_id.to_def_id()); - if let Some(attr) = tcx.sess.find_by_name(&attributes, sym::repr) { - struct_span_err!( - tcx.sess, - attr.span, - E0084, - "unsupported representation for zero-variant enum" - ) - .span_label(sp, "zero-variant enum") - .emit(); - } - } - - let repr_type_ty = def.repr.discr_type().to_ty(tcx); - if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 { - if !tcx.features().repr128 { - feature_err( - &tcx.sess.parse_sess, - sym::repr128, - sp, - "repr with 128-bit type is unstable", - ) - .emit(); - } - } - - for v in vs { - if let Some(ref e) = v.disr_expr { - tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id)); - } - } - - if tcx.adt_def(def_id).repr.int.is_none() && tcx.features().arbitrary_enum_discriminant { - let is_unit = |var: &hir::Variant<'_>| match var.data { - hir::VariantData::Unit(..) => true, - _ => false, - }; - - let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); - let has_non_units = vs.iter().any(|var| !is_unit(var)); - let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var)); - let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var)); - - if disr_non_unit || (disr_units && has_non_units) { - let mut err = - struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified"); - err.emit(); - } - } - - let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len()); - for ((_, discr), v) in def.discriminants(tcx).zip(vs) { - // Check for duplicate discriminant values - if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) { - let variant_did = def.variants[VariantIdx::new(i)].def_id; - let variant_i_hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.expect_local()); - let variant_i = tcx.hir().expect_variant(variant_i_hir_id); - let i_span = match variant_i.disr_expr { - Some(ref expr) => tcx.hir().span(expr.hir_id), - None => tcx.hir().span(variant_i_hir_id), - }; - let span = match v.disr_expr { - Some(ref expr) => tcx.hir().span(expr.hir_id), - None => v.span, - }; - struct_span_err!( - tcx.sess, - span, - E0081, - "discriminant value `{}` already exists", - disr_vals[i] - ) - .span_label(i_span, format!("first use of `{}`", disr_vals[i])) - .span_label(span, format!("enum already has `{}`", disr_vals[i])) - .emit(); - } - disr_vals.push(discr); - } - - check_representable(tcx, sp, def_id); - check_transparent(tcx, sp, def); -} - fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) { struct_span_err!( tcx.sess, @@ -2911,136 +1051,6 @@ fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) { .emit(); } -impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - - fn item_def_id(&self) -> Option<DefId> { - None - } - - fn default_constness_for_trait_bounds(&self) -> hir::Constness { - // FIXME: refactor this into a method - let node = self.tcx.hir().get(self.body_id); - if let Some(fn_like) = FnLikeNode::from_node(node) { - fn_like.constness() - } else { - hir::Constness::NotConst - } - } - - fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> { - let tcx = self.tcx; - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let item_id = tcx.hir().ty_param_owner(hir_id); - let item_def_id = tcx.hir().local_def_id(item_id); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; - ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter( - self.param_env.caller_bounds().iter().filter_map(|predicate| { - match predicate.skip_binders() { - ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => { - // HACK(eddyb) should get the original `Span`. - let span = tcx.def_span(def_id); - Some((predicate, span)) - } - _ => None, - } - }), - ), - } - } - - fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> { - let v = match def { - Some(def) => infer::EarlyBoundRegion(span, def.name), - None => infer::MiscVariable(span), - }; - Some(self.next_region_var(v)) - } - - fn allow_ty_infer(&self) -> bool { - true - } - - fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() { - return ty; - } - unreachable!() - } else { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }) - } - } - - fn ct_infer( - &self, - ty: Ty<'tcx>, - param: Option<&ty::GenericParamDef>, - span: Span, - ) -> &'tcx Const<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { - return ct; - } - unreachable!() - } else { - self.next_const_var( - ty, - ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, - ) - } - } - - fn projected_ty_from_poly_trait_ref( - &self, - span: Span, - item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Ty<'tcx> { - let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars( - span, - infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), - &poly_trait_ref, - ); - - let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item( - self, - self.tcx, - span, - item_def_id, - item_segment, - trait_ref.substs, - ); - - self.tcx().mk_projection(item_def_id, item_substs) - } - - fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_escaping_bound_vars() { - ty // FIXME: normalization and escaping regions - } else { - self.normalize_associated_types_in(span, &ty) - } - } - - fn set_tainted_by_errors(&self) { - self.infcx.set_tainted_by_errors() - } - - fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { - self.write_ty(hir_id, ty) - } -} - /// Controls whether the arguments are tupled. This is used for the call /// operator. /// @@ -3073,2948 +1083,49 @@ enum FallbackMode { All, } -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn new( - inh: &'a Inherited<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - ) -> FnCtxt<'a, 'tcx> { - FnCtxt { - body_id, - param_env, - err_count_on_creation: inh.tcx.sess.err_count(), - ret_coercion: None, - ret_coercion_span: RefCell::new(None), - resume_yield_tys: None, - ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), - diverges: Cell::new(Diverges::Maybe), - has_errors: Cell::new(false), - enclosing_breakables: RefCell::new(EnclosingBreakables { - stack: Vec::new(), - by_id: Default::default(), - }), - inh, - } - } - - pub fn sess(&self) -> &Session { - &self.tcx.sess - } - - pub fn errors_reported_since_creation(&self) -> bool { - self.tcx.sess.err_count() > self.err_count_on_creation - } - - /// Produces warning on the given node, if the current point in the - /// function is unreachable, and there hasn't been another warning. - fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) { - // FIXME: Combine these two 'if' expressions into one once - // let chains are implemented - if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() { - // If span arose from a desugaring of `if` or `while`, then it is the condition itself, - // which diverges, that we are about to lint on. This gives suboptimal diagnostics. - // Instead, stop here so that the `if`- or `while`-expression's block is linted instead. - if !span.is_desugaring(DesugaringKind::CondTemporary) - && !span.is_desugaring(DesugaringKind::Async) - && !orig_span.is_desugaring(DesugaringKind::Await) - { - self.diverges.set(Diverges::WarnedAlways); - - debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - - self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - let msg = format!("unreachable {}", kind); - lint.build(&msg) - .span_label(span, &msg) - .span_label( - orig_span, - custom_note - .unwrap_or("any code following this expression is unreachable"), - ) - .emit(); - }) - } - } - } - - pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { - ObligationCause::new(span, self.body_id, code) - } - - pub fn misc(&self, span: Span) -> ObligationCause<'tcx> { - self.cause(span, ObligationCauseCode::MiscObligation) - } - - /// Resolves type and const variables in `ty` if possible. Unlike the infcx - /// version (resolve_vars_if_possible), this version will - /// also select obligations if it seems useful, in an effort - /// to get more type information. - fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { - debug!("resolve_vars_with_obligations(ty={:?})", ty); - - // No Infer()? Nothing needs doing. - if !ty.has_infer_types_or_consts() { - debug!("resolve_vars_with_obligations: ty={:?}", ty); - return ty; - } - - // If `ty` is a type variable, see whether we already know what it is. - ty = self.resolve_vars_if_possible(&ty); - if !ty.has_infer_types_or_consts() { - debug!("resolve_vars_with_obligations: ty={:?}", ty); - return ty; - } - - // If not, try resolving pending obligations as much as - // possible. This can help substantially when there are - // indirect dependencies that don't seem worth tracking - // precisely. - self.select_obligations_where_possible(false, |_| {}); - ty = self.resolve_vars_if_possible(&ty); - - debug!("resolve_vars_with_obligations: ty={:?}", ty); - ty - } - - fn record_deferred_call_resolution( - &self, - closure_def_id: DefId, - r: DeferredCallResolution<'tcx>, - ) { - let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); - deferred_call_resolutions.entry(closure_def_id).or_default().push(r); - } - - fn remove_deferred_call_resolutions( - &self, - closure_def_id: DefId, - ) -> Vec<DeferredCallResolution<'tcx>> { - let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); - deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![]) - } - - pub fn tag(&self) -> String { - format!("{:p}", self) - } - - pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> { - self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| { - span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid)) - }) - } - - #[inline] - pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) { - debug!( - "write_ty({:?}, {:?}) in fcx {}", - id, - self.resolve_vars_if_possible(&ty), - self.tag() - ); - self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); - - if ty.references_error() { - self.has_errors.set(true); - self.set_tainted_by_errors(); - } - } - - pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) { - self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index); - } - - fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), ErrorReported>) { - self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); - } - - pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) { - debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method); - self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); - self.write_substs(hir_id, method.substs); - - // When the method is confirmed, the `method.substs` includes - // parameters from not just the method, but also the impl of - // the method -- in particular, the `Self` type will be fully - // resolved. However, those are not something that the "user - // specified" -- i.e., those types come from the inferred type - // of the receiver, not something the user wrote. So when we - // create the user-substs, we want to replace those earlier - // types with just the types that the user actually wrote -- - // that is, those that appear on the *method itself*. - // - // As an example, if the user wrote something like - // `foo.bar::<u32>(...)` -- the `Self` type here will be the - // type of `foo` (possibly adjusted), but we don't want to - // include that. We want just the `[_, u32]` part. - if !method.substs.is_noop() { - let method_generics = self.tcx.generics_of(method.def_id); - if !method_generics.params.is_empty() { - let user_type_annotation = self.infcx.probe(|_| { - let user_substs = UserSubsts { - substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| { - let i = param.index as usize; - if i < method_generics.parent_count { - self.infcx.var_for_def(DUMMY_SP, param) - } else { - method.substs[i] - } - }), - user_self_ty: None, // not relevant here - }; - - self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( - method.def_id, - user_substs, - )) - }); - - debug!("write_method_call: user_type_annotation={:?}", user_type_annotation); - self.write_user_type_annotation(hir_id, user_type_annotation); - } - } - } - - pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) { - if !substs.is_noop() { - debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag()); - - self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs); - } - } - - /// Given the substs that we just converted from the HIR, try to - /// canonicalize them and store them as user-given substitutions - /// (i.e., substitutions that must be respected by the NLL check). - /// - /// This should be invoked **before any unifications have - /// occurred**, so that annotations like `Vec<_>` are preserved - /// properly. - pub fn write_user_type_annotation_from_substs( - &self, - hir_id: hir::HirId, - def_id: DefId, - substs: SubstsRef<'tcx>, - user_self_ty: Option<UserSelfTy<'tcx>>, - ) { - debug!( - "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \ - user_self_ty={:?} in fcx {}", - hir_id, - def_id, - substs, - user_self_ty, - self.tag(), - ); - - if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { - let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( - def_id, - UserSubsts { substs, user_self_ty }, - )); - debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized); - self.write_user_type_annotation(hir_id, canonicalized); - } - } - - pub fn write_user_type_annotation( - &self, - hir_id: hir::HirId, - canonical_user_type_annotation: CanonicalUserType<'tcx>, - ) { - debug!( - "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}", - hir_id, - canonical_user_type_annotation, - self.tag(), - ); - - if !canonical_user_type_annotation.is_identity() { - self.typeck_results - .borrow_mut() - .user_provided_types_mut() - .insert(hir_id, canonical_user_type_annotation); - } else { - debug!("write_user_type_annotation: skipping identity substs"); - } - } - - pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) { - debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj); - - if adj.is_empty() { - return; - } - - let autoborrow_mut = adj.iter().any(|adj| { - matches!(adj, &Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), - .. - }) - }); - - match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) { - Entry::Vacant(entry) => { - entry.insert(adj); - } - Entry::Occupied(mut entry) => { - debug!(" - composing on top of {:?}", entry.get()); - match (&entry.get()[..], &adj[..]) { - // Applying any adjustment on top of a NeverToAny - // is a valid NeverToAny adjustment, because it can't - // be reached. - (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return, - (&[ - Adjustment { kind: Adjust::Deref(_), .. }, - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, - ], &[ - Adjustment { kind: Adjust::Deref(_), .. }, - .. // Any following adjustments are allowed. - ]) => { - // A reborrow has no effect before a dereference. - } - // FIXME: currently we never try to compose autoderefs - // and ReifyFnPointer/UnsafeFnPointer, but we could. - _ => - bug!("while adjusting {:?}, can't compose {:?} and {:?}", - expr, entry.get(), adj) - }; - *entry.get_mut() = adj; - } - } - - // If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`. - // In this case implicit use of `Deref` and `Index` within `<expr>` should - // instead be `DerefMut` and `IndexMut`, so fix those up. - if autoborrow_mut { - self.convert_place_derefs_to_mutable(expr); - } - } - - /// Basically whenever we are converting from a type scheme into - /// the fn body space, we always want to normalize associated - /// types as well. This function combines the two. - fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T - where - T: TypeFoldable<'tcx>, - { - let value = value.subst(self.tcx, substs); - let result = self.normalize_associated_types_in(span, &value); - debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result); - result - } - - /// As `instantiate_type_scheme`, but for the bounds found in a - /// generic type scheme. - fn instantiate_bounds( - &self, - span: Span, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) { - let bounds = self.tcx.predicates_of(def_id); - let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect(); - let result = bounds.instantiate(self.tcx, substs); - let result = self.normalize_associated_types_in(span, &result); - debug!( - "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", - bounds, substs, result, spans, - ); - (result, spans) - } - - /// Replaces the opaque types from the given value with type variables, - /// and records the `OpaqueTypeMap` for later use during writeback. See - /// `InferCtxt::instantiate_opaque_types` for more details. - fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>( - &self, - parent_id: hir::HirId, - value: &T, - value_span: Span, - ) -> T { - let parent_def_id = self.tcx.hir().local_def_id(parent_id); - debug!( - "instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})", - parent_def_id, value - ); - - let (value, opaque_type_map) = - self.register_infer_ok_obligations(self.instantiate_opaque_types( - parent_def_id, - self.body_id, - self.param_env, - value, - value_span, - )); - - let mut opaque_types = self.opaque_types.borrow_mut(); - let mut opaque_types_vars = self.opaque_types_vars.borrow_mut(); - for (ty, decl) in opaque_type_map { - let _ = opaque_types.insert(ty, decl); - let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type); - } - - value - } - - fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T - where - T: TypeFoldable<'tcx>, - { - self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value) - } - - fn normalize_associated_types_in_as_infer_ok<T>( - &self, - span: Span, - value: &T, - ) -> InferOk<'tcx, T> - where - T: TypeFoldable<'tcx>, - { - self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value) - } - - pub fn require_type_meets( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - def_id: DefId, - ) { - self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code)); - } - - pub fn require_type_is_sized( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - if !ty.references_error() { - let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); - self.require_type_meets(ty, span, code, lang_item); - } - } - - pub fn require_type_is_sized_deferred( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - if !ty.references_error() { - self.deferred_sized_obligations.borrow_mut().push((ty, span, code)); - } - } - - pub fn register_bound( - &self, - ty: Ty<'tcx>, - def_id: DefId, - cause: traits::ObligationCause<'tcx>, - ) { - if !ty.references_error() { - self.fulfillment_cx.borrow_mut().register_bound( - self, - self.param_env, - ty, - def_id, - cause, - ); - } - } - - pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { - let t = AstConv::ast_ty_to_ty(self, ast_t); - self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation); - t - } - - pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { - let ty = self.to_ty(ast_ty); - debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); - - if Self::can_contain_user_lifetime_bounds(ty) { - let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty)); - debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); - self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); - } - - ty - } - - pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> { - let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); - let c = ty::Const::from_anon_const(self.tcx, const_def_id); - self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::MiscObligation, - ); - c - } - - pub fn const_arg_to_const( - &self, - ast_c: &hir::AnonConst, - param_def_id: DefId, - ) -> &'tcx ty::Const<'tcx> { - let const_def = ty::WithOptConstParam { - did: self.tcx.hir().local_def_id(ast_c.hir_id), - const_param_did: Some(param_def_id), - }; - let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def); - self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::MiscObligation, - ); - c - } - - // If the type given by the user has free regions, save it for later, since - // NLL would like to enforce those. Also pass in types that involve - // projections, since those can resolve to `'static` bounds (modulo #54940, - // which hopefully will be fixed by the time you see this comment, dear - // reader, although I have my doubts). Also pass in types with inference - // types, because they may be repeated. Other sorts of things are already - // sufficiently enforced with erased regions. =) - fn can_contain_user_lifetime_bounds<T>(t: T) -> bool - where - T: TypeFoldable<'tcx>, - { - t.has_free_regions() || t.has_projections() || t.has_infer_types() - } - - pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { - match self.typeck_results.borrow().node_types().get(id) { - Some(&t) => t, - None if self.is_tainted_by_errors() => self.tcx.ty_error(), - None => { - bug!( - "no type for node {}: {} in fcx {}", - id, - self.tcx.hir().node_to_string(id), - self.tag() - ); - } - } - } - - /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. - pub fn register_wf_obligation( - &self, - arg: subst::GenericArg<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - // WF obligations never themselves fail, so no real need to give a detailed cause: - let cause = traits::ObligationCause::new(span, self.body_id, code); - self.register_predicate(traits::Obligation::new( - cause, - self.param_env, - ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx), - )); - } - - /// Registers obligations that all `substs` are well-formed. - pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) { - for arg in substs.iter().filter(|arg| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) { - self.register_wf_obligation(arg, expr.span, traits::MiscObligation); - } - } - - /// 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 - pub fn field_ty( - &self, - span: Span, - field: &'tcx ty::FieldDef, - substs: SubstsRef<'tcx>, - ) -> Ty<'tcx> { - self.normalize_associated_types_in(span, &field.ty(self.tcx, substs)) - } - - fn check_casts(&self) { - let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - for cast in deferred_cast_checks.drain(..) { - cast.check(self); - } - } - - fn resolve_generator_interiors(&self, def_id: DefId) { - let mut generators = self.deferred_generator_interiors.borrow_mut(); - for (body_id, interior, kind) in generators.drain(..) { - self.select_obligations_where_possible(false, |_| {}); - generator_interior::resolve_interior(self, def_id, body_id, interior, kind); - } - } - - // Tries to apply a fallback to `ty` if it is an unsolved variable. - // - // - Unconstrained ints are replaced with `i32`. - // - // - Unconstrained floats are replaced with with `f64`. - // - // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]` - // is enabled. Otherwise, they are replaced with `()`. - // - // Fallback becomes very dubious if we have encountered type-checking errors. - // In that case, fallback to Error. - // The return value indicates whether fallback has occurred. - fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool { - use rustc_middle::ty::error::UnconstrainedNumeric::Neither; - use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; - - assert!(ty.is_ty_infer()); - let fallback = match self.type_is_unconstrained_numeric(ty) { - _ if self.is_tainted_by_errors() => self.tcx().ty_error(), - UnconstrainedInt => self.tcx.types.i32, - UnconstrainedFloat => self.tcx.types.f64, - Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(), - Neither => { - // This type variable was created from the instantiation of an opaque - // type. The fact that we're attempting to perform fallback for it - // means that the function neither constrained it to a concrete - // type, nor to the opaque type itself. - // - // For example, in this code: - // - //``` - // type MyType = impl Copy; - // fn defining_use() -> MyType { true } - // fn other_use() -> MyType { defining_use() } - // ``` - // - // `defining_use` will constrain the instantiated inference - // variable to `bool`, while `other_use` will constrain - // the instantiated inference variable to `MyType`. - // - // When we process opaque types during writeback, we - // will handle cases like `other_use`, and not count - // them as defining usages - // - // However, we also need to handle cases like this: - // - // ```rust - // pub type Foo = impl Copy; - // fn produce() -> Option<Foo> { - // None - // } - // ``` - // - // In the above snippet, the inference variable created by - // instantiating `Option<Foo>` will be completely unconstrained. - // We treat this as a non-defining use by making the inference - // variable fall back to the opaque type itself. - if let FallbackMode::All = mode { - if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) { - debug!( - "fallback_if_possible: falling back opaque type var {:?} to {:?}", - ty, opaque_ty - ); - *opaque_ty - } else { - return false; - } - } else { - return false; - } - } - }; - debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback); - self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback); - true - } - - fn select_all_obligations_or_error(&self) { - debug!("select_all_obligations_or_error"); - if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) { - self.report_fulfillment_errors(&errors, self.inh.body_id, false); - } - } - - /// Select as many obligations as we can at present. - fn select_obligations_where_possible( - &self, - fallback_has_occurred: bool, - mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>), - ) { - let result = self.fulfillment_cx.borrow_mut().select_where_possible(self); - if let Err(mut errors) = result { - mutate_fullfillment_errors(&mut errors); - self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred); - } - } - - /// For the overloaded place expressions (`*x`, `x[3]`), the trait - /// returns a type of `&T`, but the actual type we assign to the - /// *expression* is `T`. So this function just peels off the return - /// type by one layer to yield `T`. - fn make_overloaded_place_return_type( - &self, - method: MethodCallee<'tcx>, - ) -> ty::TypeAndMut<'tcx> { - // extract method return type, which will be &T; - let ret_ty = method.sig.output(); - - // method returns &T, but the type as visible to user is T, so deref - ret_ty.builtin_deref(true).unwrap() - } - - fn check_method_argument_types( - &self, - sp: Span, - expr: &'tcx hir::Expr<'tcx>, - method: Result<MethodCallee<'tcx>, ()>, - args_no_rcvr: &'tcx [hir::Expr<'tcx>], - tuple_arguments: TupleArgumentsFlag, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let has_error = match method { - Ok(method) => method.substs.references_error() || method.sig.references_error(), - Err(_) => true, - }; - if has_error { - let err_inputs = self.err_args(args_no_rcvr.len()); - - let err_inputs = match tuple_arguments { - DontTupleArguments => err_inputs, - TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])], - }; - - self.check_argument_types( - sp, - expr, - &err_inputs[..], - &[], - args_no_rcvr, - false, - tuple_arguments, - None, - ); - return self.tcx.ty_error(); - } - - let method = method.unwrap(); - // HACK(eddyb) ignore self in the definition (see above). - let expected_arg_tys = self.expected_inputs_for_expected_output( - sp, - expected, - method.sig.output(), - &method.sig.inputs()[1..], - ); - self.check_argument_types( - sp, - expr, - &method.sig.inputs()[1..], - &expected_arg_tys[..], - args_no_rcvr, - method.sig.c_variadic, - tuple_arguments, - self.tcx.hir().span_if_local(method.def_id), - ); - method.sig.output() - } - - fn self_type_matches_expected_vid( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - expected_vid: ty::TyVid, - ) -> bool { - let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); - debug!( - "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})", - trait_ref, self_ty, expected_vid - ); - match self_ty.kind { - ty::Infer(ty::TyVar(found_vid)) => { - // FIXME: consider using `sub_root_var` here so we - // can see through subtyping. - let found_vid = self.root_var(found_vid); - debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid); - expected_vid == found_vid - } - _ => false, - } - } - - fn obligations_for_self_ty<'b>( - &'b self, - self_ty: ty::TyVid, - ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)> - + Captures<'tcx> - + 'b { - // FIXME: consider using `sub_root_var` here so we - // can see through subtyping. - let ty_var_root = self.root_var(self_ty); - debug!( - "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}", - self_ty, - ty_var_root, - self.fulfillment_cx.borrow().pending_obligations() - ); - - self.fulfillment_cx - .borrow() - .pending_obligations() - .into_iter() - .filter_map(move |obligation| { - match obligation.predicate.skip_binders() { - ty::PredicateAtom::Projection(data) => { - Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation)) - } - ty::PredicateAtom::Trait(data, _) => { - Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation)) - } - ty::PredicateAtom::Subtype(..) => None, - ty::PredicateAtom::RegionOutlives(..) => None, - ty::PredicateAtom::TypeOutlives(..) => None, - ty::PredicateAtom::WellFormed(..) => None, - ty::PredicateAtom::ObjectSafe(..) => None, - ty::PredicateAtom::ConstEvaluatable(..) => None, - ty::PredicateAtom::ConstEquate(..) => None, - // N.B., this predicate is created by breaking down a - // `ClosureType: FnFoo()` predicate, where - // `ClosureType` represents some `Closure`. It can't - // possibly be referring to the current closure, - // because we haven't produced the `Closure` for - // this closure yet; this is exactly why the other - // code is looking for a self type of a unresolved - // inference variable. - ty::PredicateAtom::ClosureKind(..) => None, - } - }) - .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) - } - - fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { - self.obligations_for_self_ty(self_ty) - .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait()) - } - - /// Generic function that factors out common logic from function calls, - /// method calls and overloaded operators. - fn check_argument_types( - &self, - sp: Span, - expr: &'tcx hir::Expr<'tcx>, - fn_inputs: &[Ty<'tcx>], - expected_arg_tys: &[Ty<'tcx>], - args: &'tcx [hir::Expr<'tcx>], - c_variadic: bool, - tuple_arguments: TupleArgumentsFlag, - def_span: Option<Span>, - ) { - let tcx = self.tcx; - // Grab the argument types, supplying fresh type variables - // if the wrong number of arguments were supplied - let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 }; - - // All the input types from the fn signature must outlive the call - // so as to validate implied bounds. - for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) { - self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); - } - - let expected_arg_count = fn_inputs.len(); - - let param_count_error = |expected_count: usize, - arg_count: usize, - error_code: &str, - c_variadic: bool, - sugg_unit: bool| { - let (span, start_span, args) = match &expr.kind { - hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]), - hir::ExprKind::MethodCall(path_segment, span, args, _) => ( - *span, - // `sp` doesn't point at the whole `foo.bar()`, only at `bar`. - path_segment - .args - .and_then(|args| args.args.iter().last()) - // Account for `foo.bar::<T>()`. - .map(|arg| { - // Skip the closing `>`. - tcx.sess - .source_map() - .next_point(tcx.sess.source_map().next_point(arg.span())) - }) - .unwrap_or(*span), - &args[1..], // Skip the receiver. - ), - k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k), - }; - let arg_spans = if args.is_empty() { - // foo() - // ^^^-- supplied 0 arguments - // | - // expected 2 arguments - vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())] - } else { - // foo(1, 2, 3) - // ^^^ - - - supplied 3 arguments - // | - // expected 2 arguments - args.iter().map(|arg| arg.span).collect::<Vec<Span>>() - }; - - let mut err = tcx.sess.struct_span_err_with_code( - span, - &format!( - "this function takes {}{} but {} {} supplied", - if c_variadic { "at least " } else { "" }, - potentially_plural_count(expected_count, "argument"), - potentially_plural_count(arg_count, "argument"), - if arg_count == 1 { "was" } else { "were" } - ), - DiagnosticId::Error(error_code.to_owned()), - ); - let label = format!("supplied {}", potentially_plural_count(arg_count, "argument")); - for (i, span) in arg_spans.into_iter().enumerate() { - err.span_label( - span, - if arg_count == 0 || i + 1 == arg_count { &label } else { "" }, - ); - } - - if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) { - err.span_label(def_s, "defined here"); - } - if sugg_unit { - let sugg_span = tcx.sess.source_map().end_point(expr.span); - // remove closing `)` from the span - let sugg_span = sugg_span.shrink_to_lo(); - err.span_suggestion( - sugg_span, - "expected the unit value `()`; create it with empty parentheses", - String::from("()"), - Applicability::MachineApplicable, - ); - } else { - err.span_label( - span, - format!( - "expected {}{}", - if c_variadic { "at least " } else { "" }, - potentially_plural_count(expected_count, "argument") - ), - ); - } - err.emit(); - }; - - let mut expected_arg_tys = expected_arg_tys.to_vec(); - - let formal_tys = if tuple_arguments == TupleArguments { - let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]); - match tuple_type.kind { - ty::Tuple(arg_types) if arg_types.len() != args.len() => { - param_count_error(arg_types.len(), args.len(), "E0057", false, false); - expected_arg_tys = vec![]; - self.err_args(args.len()) - } - ty::Tuple(arg_types) => { - expected_arg_tys = match expected_arg_tys.get(0) { - Some(&ty) => match ty.kind { - ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(), - _ => vec![], - }, - None => vec![], - }; - arg_types.iter().map(|k| k.expect_ty()).collect() - } - _ => { - struct_span_err!( - tcx.sess, - sp, - E0059, - "cannot use call notation; the first type parameter \ - for the function trait is neither a tuple nor unit" - ) - .emit(); - expected_arg_tys = vec![]; - self.err_args(args.len()) - } - } - } else if expected_arg_count == supplied_arg_count { - fn_inputs.to_vec() - } else if c_variadic { - if supplied_arg_count >= expected_arg_count { - fn_inputs.to_vec() - } else { - param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false); - expected_arg_tys = vec![]; - self.err_args(supplied_arg_count) - } - } else { - // is the missing argument of type `()`? - let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 { - self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit() - } else if fn_inputs.len() == 1 && supplied_arg_count == 0 { - self.resolve_vars_if_possible(&fn_inputs[0]).is_unit() - } else { - false - }; - param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit); - - expected_arg_tys = vec![]; - self.err_args(supplied_arg_count) - }; - - debug!( - "check_argument_types: formal_tys={:?}", - formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::<Vec<String>>() - ); - - // If there is no expectation, expect formal_tys. - let expected_arg_tys = - if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() }; - - let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![]; - - // Check the arguments. - // We do this in a pretty awful way: first we type-check any arguments - // that are not closures, then we type-check the closures. This is so - // that we have more information about the types of arguments when we - // type-check the functions. This isn't really the right way to do this. - for &check_closures in &[false, true] { - debug!("check_closures={}", check_closures); - - // More awful hacks: before we check argument types, try to do - // an "opportunistic" trait resolution of any trait bounds on - // the call. This helps coercions. - if check_closures { - self.select_obligations_where_possible(false, |errors| { - 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[..], - sp, - &args, - ); - }) - } - - // For C-variadic functions, we don't have a declared type for all of - // the arguments hence we only do our usual type checking with - // the arguments who's types we do know. - let t = if c_variadic { - expected_arg_count - } else if tuple_arguments == TupleArguments { - args.len() - } else { - supplied_arg_count - }; - for (i, arg) in args.iter().take(t).enumerate() { - // Warn only for the first loop (the "no closures" one). - // Closure arguments themselves can't be diverging, but - // a previous argument can, e.g., `foo(panic!(), || {})`. - if !check_closures { - self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); - } - - let is_closure = match arg.kind { - ExprKind::Closure(..) => true, - _ => false, - }; - - if is_closure != check_closures { - continue; - } - - debug!("checking the argument"); - let formal_ty = formal_tys[i]; - - // The special-cased logic below has three functions: - // 1. Provide as good of an expected type as possible. - let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]); - - let checked_ty = self.check_expr_with_expectation(&arg, expected); - - // 2. Coerce to the most detailed type that could be coerced - // to, which is `expected_ty` if `rvalue_hint` returns an - // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. - let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty); - // 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. - self.demand_suptype(arg.span, formal_ty, coerce_ty); - } - } - - // We also need to make sure we at least write the ty of the other - // arguments which we skipped above. - if c_variadic { - fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { - use crate::structured_errors::{StructuredDiagnostic, VariadicError}; - VariadicError::new(s, span, t, cast_ty).diagnostic().emit(); - } - - for arg in args.iter().skip(expected_arg_count) { - let arg_ty = self.check_expr(&arg); - - // There are a few types which get autopromoted when passed via varargs - // in C but we just error out instead and require explicit casts. - let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); - match arg_ty.kind { - ty::Float(ast::FloatTy::F32) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); - } - ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); - } - ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); - } - ty::FnDef(..) => { - let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); - let ptr_ty = self.resolve_vars_if_possible(&ptr_ty); - variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); - } - _ => {} - } - } - } - } - - fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> { - vec![self.tcx.ty_error(); len] - } - - /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk - /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s - /// reference a type argument. The reason to walk also the checked type is that the coerced type - /// can be not easily comparable with predicate type (because of coercion). If the types match - /// for either checked or coerced type, and there's only *one* argument that does, we point at - /// the corresponding argument's expression span instead of the `fn` call path span. - fn point_at_arg_instead_of_call_if_possible( - &self, - errors: &mut Vec<traits::FulfillmentError<'tcx>>, - final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)], - call_sp: Span, - args: &'tcx [hir::Expr<'tcx>], - ) { - // We *do not* do this for desugared call spans to keep good diagnostics when involving - // the `?` operator. - if call_sp.desugaring_kind().is_some() { - return; - } - - for error in errors { - // Only if the cause is somewhere inside the expression we want try to point at arg. - // Otherwise, it means that the cause is somewhere else and we should not change - // anything because we can break the correct span. - if !call_sp.contains(error.obligation.cause.span) { - continue; - } - - if let ty::PredicateAtom::Trait(predicate, _) = - error.obligation.predicate.skip_binders() - { - // 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().any(|arg| arg == predicate.self_ty().into()) { - Some(i) - } else { - None - } - }) - .collect::<Vec<_>>(); - - // Both checked and coerced types could have matched, thus we need to remove - // duplicates. - referenced_in.sort(); - referenced_in.dedup(); - - 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; - error.points_at_arg_span = true; - } - } - } - } - - /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the - /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s - /// were caused by them. If they were, we point at the corresponding type argument's span - /// instead of the `fn` call path span. - fn point_at_type_arg_instead_of_call_if_possible( - &self, - errors: &mut Vec<traits::FulfillmentError<'tcx>>, - 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::PredicateAtom::Trait(predicate, _) = - error.obligation.predicate.skip_binders() - { - // If any of the type arguments in this path segment caused the - // `FullfillmentError`, 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 = 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; - } - } - } - } - } - } - } - } - } - } - - // AST fragment checking - fn check_lit(&self, lit: &hir::Lit, expected: Expectation<'tcx>) -> Ty<'tcx> { - let tcx = self.tcx; - - match lit.node { - ast::LitKind::Str(..) => tcx.mk_static_str(), - ast::LitKind::ByteStr(ref v) => { - tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64)) - } - ast::LitKind::Byte(_) => tcx.types.u8, - ast::LitKind::Char(_) => tcx.types.char, - ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t), - ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t), - ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind { - ty::Int(_) | ty::Uint(_) => Some(ty), - ty::Char => Some(tcx.types.u8), - ty::RawPtr(..) => Some(tcx.types.usize), - ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_int_var()) - } - ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t), - ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind { - ty::Float(_) => Some(ty), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_float_var()) - } - ast::LitKind::Bool(_) => tcx.types.bool, - ast::LitKind::Err(_) => tcx.ty_error(), - } - } - - /// Unifies the output type with the expected type early, for more coercions - /// and forward type information on the input expressions. - fn expected_inputs_for_expected_output( - &self, - call_span: Span, - expected_ret: Expectation<'tcx>, - formal_ret: Ty<'tcx>, - formal_args: &[Ty<'tcx>], - ) -> Vec<Ty<'tcx>> { - let formal_ret = self.resolve_vars_with_obligations(formal_ret); - let ret_ty = match expected_ret.only_has_type(self) { - Some(ret) => ret, - None => return Vec::new(), - }; - let expect_args = self - .fudge_inference_if_ok(|| { - // Attempt to apply a subtyping relationship between the formal - // return type (likely containing type variables if the function - // is polymorphic) and the expected return type. - // No argument expectations are produced if unification fails. - let origin = self.misc(call_span); - let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret); - - // FIXME(#27336) can't use ? here, Try::from_error doesn't default - // to identity so the resulting type is not constrained. - match ures { - Ok(ok) => { - // Process any obligations locally as much as - // 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 mut fulfill = TraitEngine::new(self.tcx); - for obligation in ok.obligations { - fulfill.register_predicate_obligation(self, obligation); - } - fulfill.select_where_possible(self) - }) - .map_err(|_| ())?; - } - Err(_) => return Err(()), - } - - // Record all the argument types, with the substitutions - // produced from the above subtyping unification. - Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect()) - }) - .unwrap_or_default(); - debug!( - "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})", - formal_args, formal_ret, expect_args, expected_ret - ); - expect_args - } - - pub fn check_struct_path( - &self, - qpath: &QPath<'_>, - hir_id: hir::HirId, - ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { - let path_span = qpath.qself_span(); - let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); - let variant = match def { - Res::Err => { - self.set_tainted_by_errors(); - return None; - } - Res::Def(DefKind::Variant, _) => match ty.kind { - ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)), - _ => bug!("unexpected type: {:?}", ty), - }, - Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) - | Res::SelfTy(..) => match ty.kind { - ty::Adt(adt, substs) if !adt.is_enum() => { - Some((adt.non_enum_variant(), adt.did, substs)) - } - _ => None, - }, - _ => bug!("unexpected definition: {:?}", def), - }; - - if let Some((variant, did, substs)) = variant { - debug!("check_struct_path: did={:?} substs={:?}", did, substs); - 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); - - Some((variant, ty)) - } else { - struct_span_err!( - self.tcx.sess, - path_span, - E0071, - "expected struct, variant or union type, found {}", - ty.sort_string(self.tcx) - ) - .span_label(path_span, "not a struct") - .emit(); - None - } - } - - // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. - // The newly resolved definition is written into `type_dependent_defs`. - fn finish_resolving_struct_path( - &self, - qpath: &QPath<'_>, - path_span: Span, - hir_id: hir::HirId, - ) -> (Res, Ty<'tcx>) { - match *qpath { - QPath::Resolved(ref maybe_qself, ref path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); - let ty = AstConv::res_to_ty(self, self_ty, path, true); - (path.res, ty) - } - QPath::TypeRelative(ref qself, ref segment) => { - let ty = self.to_ty(qself); - - let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind { - path.res - } else { - Res::Err - }; - let result = - AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true); - let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); - let result = result.map(|(_, kind, def_id)| (kind, def_id)); - - // Write back the new resolution. - self.write_resolution(hir_id, result); - - (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty) - } - QPath::LangItem(lang_item, span) => { - self.resolve_lang_item_path(lang_item, span, hir_id) - } - } - } - - fn resolve_lang_item_path( - &self, - lang_item: hir::LangItem, - span: Span, - hir_id: hir::HirId, - ) -> (Res, Ty<'tcx>) { - let def_id = self.tcx.require_lang_item(lang_item, Some(span)); - let def_kind = self.tcx.def_kind(def_id); - - let item_ty = if let DefKind::Variant = def_kind { - self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent")) - } else { - self.tcx.type_of(def_id) - }; - let substs = self.infcx.fresh_substs_for_item(span, def_id); - let ty = item_ty.subst(self.tcx, substs); - - self.write_resolution(hir_id, Ok((def_kind, def_id))); - self.add_required_obligations(span, def_id, &substs); - (Res::Def(def_kind, def_id), ty) - } - - /// Resolves an associated value path into a base type and associated constant, or method - /// resolution. The newly resolved definition is written into `type_dependent_defs`. - pub fn resolve_ty_and_res_ufcs<'b>( - &self, - qpath: &'b QPath<'b>, - hir_id: hir::HirId, - span: Span, - ) -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]) { - debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span); - let (ty, qself, item_segment) = match *qpath { - QPath::Resolved(ref opt_qself, ref path) => { - return ( - path.res, - opt_qself.as_ref().map(|qself| self.to_ty(qself)), - &path.segments[..], - ); - } - QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment), - QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"), - }; - if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) - { - // Return directly on cache hit. This is useful to avoid doubly reporting - // errors with default match binding modes. See #44614. - let def = - cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err); - return (def, Some(ty), slice::from_ref(&**item_segment)); - } - let item_name = item_segment.ident; - let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| { - let result = match error { - method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), - _ => Err(ErrorReported), - }; - if item_name.name != kw::Invalid { - if let Some(mut e) = self.report_method_error( - span, - ty, - item_name, - SelfSource::QPath(qself), - error, - None, - ) { - e.emit(); - } - } - result - }); - - // Write back the new resolution. - self.write_resolution(hir_id, result); - ( - result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), - Some(ty), - slice::from_ref(&**item_segment), - ) - } - - pub fn check_decl_initializer( - &self, - local: &'tcx hir::Local<'tcx>, - init: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed - // for #42640 (default match binding modes). - // - // See #44848. - let ref_bindings = local.pat.contains_explicit_ref_binding(); - - let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty; - if let Some(m) = ref_bindings { - // Somewhat subtle: if we have a `ref` binding in the pattern, - // we want to avoid introducing coercions for the RHS. This is - // both because it helps preserve sanity and, in the case of - // ref mut, for soundness (issue #23116). In particular, in - // the latter case, we need to be clear that the type of the - // referent for the reference that results is *equal to* the - // type of the place it is referencing, and not some - // supertype thereof. - let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); - self.demand_eqtype(init.span, local_ty, init_ty); - init_ty - } else { - self.check_expr_coercable_to_type(init, local_ty, None) - } - } - - /// Type check a `let` statement. - pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { - // Determine and write the type which we'll check the pattern against. - let ty = self.local_ty(local.span, local.hir_id).decl_ty; - self.write_ty(local.hir_id, ty); - - // Type check the initializer. - if let Some(ref init) = local.init { - let init_ty = self.check_decl_initializer(local, &init); - self.overwrite_local_ty_if_err(local, ty, init_ty); - } - - // Does the expected pattern type originate from an expression and what is the span? - let (origin_expr, ty_span) = match (local.ty, local.init) { - (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. - (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee. - _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. - }; - - // Type check the pattern. Override if necessary to avoid knock-on errors. - self.check_pat_top(&local.pat, ty, ty_span, origin_expr); - let pat_ty = self.node_ty(local.pat.hir_id); - self.overwrite_local_ty_if_err(local, ty, pat_ty); - } - - fn overwrite_local_ty_if_err( - &self, - local: &'tcx hir::Local<'tcx>, - decl_ty: Ty<'tcx>, - ty: Ty<'tcx>, - ) { - if ty.references_error() { - // Override the types everywhere with `err()` to avoid knock on errors. - self.write_ty(local.hir_id, ty); - self.write_ty(local.pat.hir_id, ty); - let local_ty = LocalTy { decl_ty, revealed_ty: ty }; - self.locals.borrow_mut().insert(local.hir_id, local_ty); - self.locals.borrow_mut().insert(local.pat.hir_id, local_ty); - } - } - - fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) { - err.span_suggestion_short( - span.shrink_to_hi(), - "consider using a semicolon here", - ";".to_string(), - Applicability::MachineApplicable, - ); - } - - pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) { - // Don't do all the complex logic below for `DeclItem`. - match stmt.kind { - hir::StmtKind::Item(..) => return, - hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} - } - - self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); - - // Hide the outer diverging and `has_errors` flags. - let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); - - match stmt.kind { - hir::StmtKind::Local(ref l) => { - self.check_decl_local(&l); - } - // Ignore for now. - hir::StmtKind::Item(_) => {} - hir::StmtKind::Expr(ref expr) => { - // Check with expected type of `()`. - self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { - self.suggest_semicolon_at_end(expr.span, err); - }); - } - hir::StmtKind::Semi(ref expr) => { - self.check_expr(&expr); - } - } - - // Combine the diverging and `has_error` flags. - self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); - } - - pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { - let unit = self.tcx.mk_unit(); - let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); - - // if the block produces a `!` value, that can always be - // (effectively) coerced to unit. - if !ty.is_never() { - self.demand_suptype(blk.span, unit, ty); - } - } - - /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail - /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors - /// when given code like the following: - /// ```text - /// if false { return 0i32; } else { 1u32 } - /// // ^^^^ point at this instead of the whole `if` expression - /// ``` - fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { - if let hir::ExprKind::Match(_, arms, _) = &expr.kind { - let arm_spans: Vec<Span> = arms - .iter() - .filter_map(|arm| { - self.in_progress_typeck_results - .and_then(|typeck_results| { - typeck_results.borrow().node_type_opt(arm.body.hir_id) - }) - .and_then(|arm_ty| { - if arm_ty.is_never() { - None - } else { - Some(match &arm.body.kind { - // Point at the tail expression when possible. - hir::ExprKind::Block(block, _) => { - block.expr.as_ref().map(|e| e.span).unwrap_or(block.span) - } - _ => arm.body.span, - }) - } - }) - }) - .collect(); - if arm_spans.len() == 1 { - return arm_spans[0]; - } - } - expr.span - } - - fn check_block_with_expected( - &self, - blk: &'tcx hir::Block<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let prev = { - let mut fcx_ps = self.ps.borrow_mut(); - let unsafety_state = fcx_ps.recurse(blk); - replace(&mut *fcx_ps, unsafety_state) - }; - - // In some cases, blocks have just one exit, but other blocks - // can be targeted by multiple breaks. This can happen both - // with labeled blocks as well as when we desugar - // a `try { ... }` expression. - // - // Example 1: - // - // 'a: { if true { break 'a Err(()); } Ok(()) } - // - // Here we would wind up with two coercions, one from - // `Err(())` and the other from the tail expression - // `Ok(())`. If the tail expression is omitted, that's a - // "forced unit" -- unless the block diverges, in which - // case we can ignore the tail expression (e.g., `'a: { - // break 'a 22; }` would not force the type of the block - // to be `()`). - let tail_expr = blk.expr.as_ref(); - let coerce_to_ty = expected.coercion_target_type(self, blk.span); - let coerce = if blk.targeted_by_break { - CoerceMany::new(coerce_to_ty) - } else { - let tail_expr: &[&hir::Expr<'_>] = match tail_expr { - Some(e) => slice::from_ref(e), - None => &[], - }; - CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) - }; - - let prev_diverges = self.diverges.get(); - let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; - - let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { - for s in blk.stmts { - self.check_stmt(s); - } - - // check the tail expression **without** holding the - // `enclosing_breakables` lock below. - let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); - - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let ctxt = enclosing_breakables.find_breakable(blk.hir_id); - let coerce = ctxt.coerce.as_mut().unwrap(); - if let Some(tail_expr_ty) = tail_expr_ty { - let tail_expr = tail_expr.unwrap(); - let span = self.get_expr_coercion_span(tail_expr); - let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); - coerce.coerce(self, &cause, tail_expr, tail_expr_ty); - } else { - // Subtle: if there is no explicit tail expression, - // that is typically equivalent to a tail expression - // of `()` -- except if the block diverges. In that - // case, there is no value supplied from the tail - // expression (assuming there are no other breaks, - // this implies that the type of the block will be - // `!`). - // - // #41425 -- label the implicit `()` as being the - // "found type" here, rather than the "expected type". - if !self.diverges.get().is_always() { - // #50009 -- Do not point at the entire fn block span, point at the return type - // span, as it is the cause of the requirement, and - // `consider_hint_about_removing_semicolon` will point at the last expression - // if it were a relevant part of the error. This improves usability in editors - // that highlight errors inline. - let mut sp = blk.span; - let mut fn_span = None; - if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { - let ret_sp = decl.output.span(); - if let Some(block_sp) = self.parent_item_span(blk.hir_id) { - // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the - // output would otherwise be incorrect and even misleading. Make sure - // the span we're aiming at correspond to a `fn` body. - if block_sp == blk.span { - sp = ret_sp; - fn_span = Some(ident.span); - } - } - } - coerce.coerce_forced_unit( - self, - &self.misc(sp), - &mut |err| { - if let Some(expected_ty) = expected.only_has_type(self) { - self.consider_hint_about_removing_semicolon(blk, expected_ty, err); - } - if let Some(fn_span) = fn_span { - err.span_label( - fn_span, - "implicitly returns `()` as its body has no tail or `return` \ - expression", - ); - } - }, - false, - ); - } - } - }); - - if ctxt.may_break { - // If we can break from the block, then the block's exit is always reachable - // (... as long as the entry is reachable) - regardless of the tail of the block. - self.diverges.set(prev_diverges); - } - - let mut ty = ctxt.coerce.unwrap().complete(self); - - if self.has_errors.get() || ty.references_error() { - ty = self.tcx.ty_error() - } - - self.write_ty(blk.hir_id, ty); - - *self.ps.borrow_mut() = prev; - ty - } - - fn parent_item_span(&self, id: hir::HirId) -> Option<Span> { - let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id)); - match node { - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { - let body = self.tcx.hir().body(body_id); - if let ExprKind::Block(block, _) = &body.value.kind { - return Some(block.span); - } - } - _ => {} - } - None - } - - /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. - fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { - let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id)); - self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) - } - - /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. - fn get_node_fn_decl(&self, node: Node<'tcx>) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> { - match node { - Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => { - // This is less than ideal, it will not suggest a return type span on any - // method called `main`, regardless of whether it is actually the entry point, - // but it will still present it as the reason for the expected type. - Some((&sig.decl, ident, ident.name != sym::main)) - } - Node::TraitItem(&hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(ref sig, ..), - .. - }) => Some((&sig.decl, ident, true)), - Node::ImplItem(&hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(ref sig, ..), - .. - }) => Some((&sig.decl, ident, false)), - _ => None, - } - } - - /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a - /// suggestion can be made, `None` otherwise. - pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> { - // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or - // `while` before reaching it, as block tail returns are not available in them. - self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { - let parent = self.tcx.hir().get(blk_id); - self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) - }) - } - - /// On implicit return expressions with mismatched types, provides the following suggestions: - /// - /// - Points out the method's return type as the reason for the expected type. - /// - Possible missing semicolon. - /// - Possible missing return type if the return type is the default, and not `fn main()`. - pub fn suggest_mismatched_types_on_tail( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - cause_span: Span, - blk_id: hir::HirId, - ) -> bool { - let expr = expr.peel_drop_temps(); - self.suggest_missing_semicolon(err, expr, expected, cause_span); - let mut pointing_at_return_type = false; - if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { - pointing_at_return_type = - self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest); - } - pointing_at_return_type - } - - /// When encountering an fn-like ctor that needs to unify with a value, check whether calling - /// the ctor would successfully solve the type mismatch and if so, suggest it: - /// ``` - /// fn foo(x: usize) -> usize { x } - /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` - /// ``` - fn suggest_fn_call( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) -> bool { - let hir = self.tcx.hir(); - let (def_id, sig) = match found.kind { - ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)), - ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()), - _ => return false, - }; - - let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0; - let sig = self.normalize_associated_types_in(expr.span, &sig); - if self.can_coerce(sig.output(), expected) { - let (mut sugg_call, applicability) = if sig.inputs().is_empty() { - (String::new(), Applicability::MachineApplicable) - } else { - ("...".to_string(), Applicability::HasPlaceholders) - }; - let mut msg = "call this function"; - match hir.get_if_local(def_id) { - Some( - Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. }) - | Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(_, body_id), .. - }) - | Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), - .. - }), - ) => { - let body = hir.body(*body_id); - sugg_call = body - .params - .iter() - .map(|param| match ¶m.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => - { - ident.to_string() - } - _ => "_".to_string(), - }) - .collect::<Vec<_>>() - .join(", "); - } - Some(Node::Expr(hir::Expr { - kind: ExprKind::Closure(_, _, body_id, _, _), - span: full_closure_span, - .. - })) => { - if *full_closure_span == expr.span { - return false; - } - msg = "call this closure"; - let body = hir.body(*body_id); - sugg_call = body - .params - .iter() - .map(|param| match ¶m.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => - { - ident.to_string() - } - _ => "_".to_string(), - }) - .collect::<Vec<_>>() - .join(", "); - } - Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { - sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", "); - match def_id.as_local().map(|def_id| hir.def_kind(def_id)) { - Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { - msg = "instantiate this tuple variant"; - } - Some(DefKind::Ctor(CtorOf::Struct, _)) => { - msg = "instantiate this tuple struct"; - } - _ => {} - } - } - Some(Node::ForeignItem(hir::ForeignItem { - kind: hir::ForeignItemKind::Fn(_, idents, _), - .. - })) => { - sugg_call = idents - .iter() - .map(|ident| { - if ident.name != kw::SelfLower { - ident.to_string() - } else { - "_".to_string() - } - }) - .collect::<Vec<_>>() - .join(", ") - } - Some(Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)), - .. - })) => { - sugg_call = idents - .iter() - .map(|ident| { - if ident.name != kw::SelfLower { - ident.to_string() - } else { - "_".to_string() - } - }) - .collect::<Vec<_>>() - .join(", ") - } - _ => {} - } - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - &format!("use parentheses to {}", msg), - format!("({})", sugg_call), - applicability, - ); - return true; - } - false - } - - pub fn suggest_deref_ref_or_into( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) { - if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) { - err.span_suggestion(sp, msg, suggestion, applicability); - } else if let (ty::FnDef(def_id, ..), true) = - (&found.kind, self.suggest_fn_call(err, expr, expected, found)) - { - if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { - let sp = self.sess().source_map().guess_head_span(sp); - err.span_label(sp, &format!("{} defined here", found)); - } - } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { - 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::repeat(&expr_text) - .zip(methods.iter()) - .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 sugg = if receiver.ends_with(".clone()") - && method_call_list.contains(&method_call.as_str()) - { - let max_len = receiver.rfind('.').unwrap(); - format!("{}{}", &receiver[..max_len], method_call) - } else { - if expr.precedence().order() < ExprPrecedence::MethodCall.order() { - format!("({}){}", receiver, method_call) - } else { - format!("{}{}", receiver, method_call) - } - }; - Some(if is_struct_pat_shorthand_field { - format!("{}: {}", receiver, sugg) - } else { - sugg - }) - } - }) - .peekable(); - if suggestions.peek().is_some() { - err.span_suggestions( - expr.span, - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); - } - } - } - } - - /// When encountering the expected boxed value allocated in the stack, suggest allocating it - /// in the heap by calling `Box::new()`. - fn suggest_boxing_when_appropriate( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return; - } - if !expected.is_box() || found.is_box() { - return; - } - let boxed_found = self.tcx.mk_box(found); - if let (true, Ok(snippet)) = ( - self.can_coerce(boxed_found, expected), - self.sess().source_map().span_to_snippet(expr.span), - ) { - err.span_suggestion( - expr.span, - "store this in the heap by calling `Box::new`", - format!("Box::new({})", snippet), - Applicability::MachineApplicable, - ); - err.note( - "for more on the distinction between the stack and the heap, read \ - https://doc.rust-lang.org/book/ch15-01-box.html, \ - https://doc.rust-lang.org/rust-by-example/std/box.html, and \ - https://doc.rust-lang.org/std/boxed/index.html", - ); - } - } - - fn note_internal_mutation_in_method( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - if found != self.tcx.types.unit { - return; - } - if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind { - if self - .typeck_results - .borrow() - .expr_ty_adjusted_opt(rcvr) - .map_or(true, |ty| expected.peel_refs() != ty.peel_refs()) - { - return; - } - let mut sp = MultiSpan::from_span(path_segment.ident.span); - sp.push_span_label( - path_segment.ident.span, - format!( - "this call modifies {} in-place", - match rcvr.kind { - ExprKind::Path(QPath::Resolved( - None, - hir::Path { segments: [segment], .. }, - )) => format!("`{}`", segment.ident), - _ => "its receiver".to_string(), - } - ), - ); - sp.push_span_label( - rcvr.span, - "you probably want to use this value after calling the method...".to_string(), - ); - err.span_note( - sp, - &format!("method `{}` modifies its receiver in-place", path_segment.ident), - ); - err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); - } - } - - /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. - fn suggest_calling_boxed_future_when_appropriate( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) -> bool { - // Handle #68197. - - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return false; - } - let pin_did = self.tcx.lang_items().pin_type(); - match expected.kind { - ty::Adt(def, _) if Some(def.did) != pin_did => return false, - // This guards the `unwrap` and `mk_box` below. - _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false, - _ => {} - } - let boxed_found = self.tcx.mk_box(found); - let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap(); - if let (true, Ok(snippet)) = ( - self.can_coerce(new_found, expected), - self.sess().source_map().span_to_snippet(expr.span), - ) { - match found.kind { - ty::Adt(def, _) if def.is_box() => { - err.help("use `Box::pin`"); - } - _ => { - err.span_suggestion( - expr.span, - "you need to pin and box this expression", - format!("Box::pin({})", snippet), - Applicability::MachineApplicable, - ); - } - } - true - } else { - false - } - } - - /// A common error is to forget to add a semicolon at the end of a block, e.g., - /// - /// ``` - /// fn foo() { - /// bar_that_returns_u32() - /// } - /// ``` - /// - /// This routine checks if the return expression in a block would make sense on its own as a - /// statement and the return type has been left as default or has been specified as `()`. If so, - /// it suggests adding a semicolon. - fn suggest_missing_semicolon( - &self, - err: &mut DiagnosticBuilder<'_>, - expression: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - cause_span: Span, - ) { - if expected.is_unit() { - // `BlockTailExpression` only relevant if the tail expr would be - // useful on its own. - match expression.kind { - ExprKind::Call(..) - | ExprKind::MethodCall(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) - | ExprKind::Block(..) => { - err.span_suggestion( - cause_span.shrink_to_hi(), - "try adding a semicolon", - ";".to_string(), - Applicability::MachineApplicable, - ); - } - _ => (), - } - } - } - - /// A possible error is to forget to add a return type that is needed: - /// - /// ``` - /// fn foo() { - /// bar_that_returns_u32() - /// } - /// ``` - /// - /// This routine checks if the return type is left as default, the method is not part of an - /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return - /// type. - fn suggest_missing_return_type( - &self, - err: &mut DiagnosticBuilder<'_>, - fn_decl: &hir::FnDecl<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - can_suggest: bool, - ) -> bool { - // Only suggest changing the return type for methods that - // haven't set a return type at all (and aren't `fn main()` or an impl). - match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) { - (&hir::FnRetTy::DefaultReturn(span), true, true, true) => { - err.span_suggestion( - span, - "try adding a return type", - format!("-> {} ", self.resolve_vars_with_obligations(found)), - Applicability::MachineApplicable, - ); - true - } - (&hir::FnRetTy::DefaultReturn(span), false, true, true) => { - err.span_label(span, "possibly return type missing here?"); - true - } - (&hir::FnRetTy::DefaultReturn(span), _, false, true) => { - // `fn main()` must return `()`, do not suggest changing return type - err.span_label(span, "expected `()` because of default return type"); - true - } - // expectation was caused by something else, not the default return - (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false, - (&hir::FnRetTy::Return(ref ty), _, _, _) => { - // Only point to return type if the expected type is the return type, as if they - // are not, the expectation must have been caused by something else. - debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); - let sp = ty.span; - let ty = AstConv::ast_ty_to_ty(self, ty); - debug!("suggest_missing_return_type: return type {:?}", ty); - debug!("suggest_missing_return_type: expected type {:?}", ty); - if ty.kind == expected.kind { - err.span_label(sp, format!("expected `{}` because of return type", expected)); - return true; - } - false - } - } - } - - /// A possible error is to forget to add `.await` when using futures: - /// - /// ``` - /// async fn make_u32() -> u32 { - /// 22 - /// } - /// - /// fn take_u32(x: u32) {} - /// - /// async fn foo() { - /// let x = make_u32(); - /// take_u32(x); - /// } - /// ``` - /// - /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the - /// expected type. If this is the case, and we are inside of an async body, it suggests adding - /// `.await` to the tail of the expression. - fn suggest_missing_await( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found); - // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the - // body isn't `async`. - let item_id = self.tcx().hir().get_parent_node(self.body_id); - if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) { - let body = self.tcx().hir().body(body_id); - if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { - let sp = expr.span; - // Check for `Future` implementations by constructing a predicate to - // prove: `<T as Future>::Output == U` - let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp)); - let item_def_id = self - .tcx - .associated_items(future_trait) - .in_definition_order() - .next() - .unwrap() - .def_id; - // `<T as Future>::Output` - let projection_ty = ty::ProjectionTy { - // `T` - substs: self - .tcx - .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)), - // `Future::Output` - item_def_id, - }; - - let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate { - projection_ty, - ty: expected, - }) - .potentially_quantified(self.tcx, ty::PredicateKind::ForAll); - let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); - - debug!("suggest_missing_await: trying obligation {:?}", obligation); - - if self.infcx.predicate_may_hold(&obligation) { - debug!("suggest_missing_await: obligation held: {:?}", obligation); - if let Ok(code) = self.sess().source_map().span_to_snippet(sp) { - err.span_suggestion( - sp, - "consider using `.await` here", - format!("{}.await", code), - Applicability::MaybeIncorrect, - ); - } else { - debug!("suggest_missing_await: no snippet for {:?}", sp); - } - } else { - debug!("suggest_missing_await: obligation did not hold: {:?}", obligation) - } - } - } - } - - fn suggest_missing_parentheses(&self, err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>) { - let sp = self.tcx.sess.source_map().start_point(expr.span); - if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { - // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` - self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None); - } - } - - fn note_need_for_fn_pointer( - &self, - err: &mut DiagnosticBuilder<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - let (sig, did, substs) = match (&expected.kind, &found.kind) { - (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { - let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); - let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); - if sig1 != sig2 { - return; - } - err.note( - "different `fn` items always have unique types, even if their signatures are \ - the same", - ); - (sig1, *did1, substs1) - } - (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { - let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs); - if sig1 != *sig2 { - return; - } - (sig1, *did, substs) - } - _ => return, - }; - err.help(&format!("change the expected type to be function pointer `{}`", sig)); - err.help(&format!( - "if the expected type is due to type inference, cast the expected `fn` to a function \ - pointer: `{} as {}`", - self.tcx.def_path_str_with_substs(did, substs), - sig - )); - } - - /// A common error is to add an extra semicolon: - /// - /// ``` - /// fn foo() -> usize { - /// 22; - /// } - /// ``` - /// - /// This routine checks if the final statement in a block is an - /// expression with an explicit semicolon whose type is compatible - /// with `expected_ty`. If so, it suggests removing the semicolon. - fn consider_hint_about_removing_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - err: &mut DiagnosticBuilder<'_>, - ) { - if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) { - err.span_suggestion( - span_semi, - "consider removing this semicolon", - String::new(), - Applicability::MachineApplicable, - ); - } - } - - fn could_remove_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - ) -> Option<Span> { - // Be helpful when the user wrote `{... expr;}` and - // taking the `;` off is enough to fix the error. - let last_stmt = blk.stmts.last()?; - let last_expr = match last_stmt.kind { - hir::StmtKind::Semi(ref e) => e, - _ => return None, - }; - let last_expr_ty = self.node_ty(last_expr.hir_id); - if matches!(last_expr_ty.kind, ty::Error(_)) - || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() - { - return None; - } - let original_span = original_sp(last_stmt.span, blk.span); - Some(original_span.with_lo(original_span.hi() - BytePos(1))) - } - - // Instantiates the given path, which must refer to an item with the given - // number of type parameters and type. - pub fn instantiate_value_path( - &self, - segments: &[hir::PathSegment<'_>], - self_ty: Option<Ty<'tcx>>, - res: Res, - span: Span, - hir_id: hir::HirId, - ) -> (Ty<'tcx>, Res) { - debug!( - "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})", - segments, self_ty, res, hir_id, - ); - - let tcx = self.tcx; - - let path_segs = match res { - Res::Local(_) | Res::SelfCtor(_) => vec![], - Res::Def(kind, def_id) => { - AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id) - } - _ => bug!("instantiate_value_path on {:?}", res), - }; - - let mut user_self_ty = None; - let mut is_alias_variant_ctor = false; - match res { - Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => { - if let Some(self_ty) = self_ty { - let adt_def = self_ty.ty_adt_def().unwrap(); - user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty }); - is_alias_variant_ctor = true; - } - } - Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { - let container = tcx.associated_item(def_id).container; - debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container); - match container { - ty::TraitContainer(trait_did) => { - callee::check_legal_trait_for_method_call(tcx, span, None, trait_did) - } - ty::ImplContainer(impl_def_id) => { - if segments.len() == 1 { - // `<T>::assoc` will end up here, and so - // can `T::assoc`. It this came from an - // inherent impl, we need to record the - // `T` for posterity (see `UserSelfTy` for - // details). - let self_ty = self_ty.expect("UFCS sugared assoc missing Self"); - user_self_ty = Some(UserSelfTy { impl_def_id, self_ty }); - } - } - } - } - _ => {} - } - - // Now that we have categorized what space the parameters for each - // segment belong to, let's sort out the parameters that the user - // provided (if any) into their appropriate spaces. We'll also report - // errors if type parameters are provided in an inappropriate place. - - let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); - let generics_has_err = AstConv::prohibit_generics( - self, - segments.iter().enumerate().filter_map(|(index, seg)| { - if !generic_segs.contains(&index) || is_alias_variant_ctor { - Some(seg) - } else { - None - } - }), - ); - - if let Res::Local(hid) = res { - let ty = self.local_ty(span, hid).decl_ty; - let ty = self.normalize_associated_types_in(span, &ty); - self.write_ty(hir_id, ty); - return (ty, res); - } - - if generics_has_err { - // Don't try to infer type parameters when prohibited generic arguments were given. - user_self_ty = None; - } - - // Now we have to compare the types that the user *actually* - // provided against the types that were *expected*. If the user - // did not provide any types, then we want to substitute inference - // variables. If the user provided some types, we may still need - // to add defaults. If the user provided *too many* types, that's - // a problem. - - let mut infer_args_for_err = FxHashSet::default(); - for &PathSeg(def_id, index) in &path_segs { - let seg = &segments[index]; - let generics = tcx.generics_of(def_id); - // Argument-position `impl Trait` is treated as a normal generic - // parameter internally, but we don't allow users to specify the - // parameter's value explicitly, so we have to do some error- - // checking here. - if let GenericArgCountResult { - correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }), - .. - } = AstConv::check_generic_arg_count_for_call( - tcx, span, &generics, &seg, false, // `is_method_call` - ) { - infer_args_for_err.insert(index); - self.set_tainted_by_errors(); // See issue #53251. - } - } - - let has_self = path_segs - .last() - .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self) - .unwrap_or(false); - - let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { - let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id)); - match ty.kind { - ty::Adt(adt_def, substs) if adt_def.has_ctor() => { - let variant = adt_def.non_enum_variant(); - let ctor_def_id = variant.ctor_def_id.unwrap(); - ( - Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id), - Some(substs), - ) - } - _ => { - let mut err = tcx.sess.struct_span_err( - span, - "the `Self` constructor can only be used with tuple or unit structs", - ); - if let Some(adt_def) = ty.ty_adt_def() { - match adt_def.adt_kind() { - AdtKind::Enum => { - err.help("did you mean to use one of the enum's variants?"); - } - AdtKind::Struct | AdtKind::Union => { - err.span_suggestion( - span, - "use curly brackets", - String::from("Self { /* fields */ }"), - Applicability::HasPlaceholders, - ); - } - } - } - err.emit(); - - return (tcx.ty_error(), res); - } - } - } else { - (res, None) - }; - let def_id = res.def_id(); - - // The things we are substituting into the type should not contain - // escaping late-bound regions, and nor should the base type scheme. - let ty = tcx.type_of(def_id); - - let arg_count = GenericArgCountResult { - explicit_late_bound: ExplicitLateBound::No, - correct: if infer_args_for_err.is_empty() { - Ok(()) - } else { - Err(GenericArgCountMismatch::default()) - }, - }; - - let substs = self_ctor_substs.unwrap_or_else(|| { - AstConv::create_substs_for_generic_args( - tcx, - def_id, - &[][..], - has_self, - self_ty, - arg_count, - // Provide the generic args, and whether types should be inferred. - |def_id| { - if let Some(&PathSeg(_, index)) = - path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) - { - // If we've encountered an `impl Trait`-related error, we're just - // going to infer the arguments for better error messages. - if !infer_args_for_err.contains(&index) { - // Check whether the user has provided generic arguments. - if let Some(ref data) = segments[index].args { - return (Some(data), segments[index].infer_args); - } - } - return (None, segments[index].infer_args); - } - - (None, true) - }, - // Provide substitutions for parameters for which (valid) arguments have been provided. - |param, arg| match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - AstConv::ast_region_to_region(self, lt, Some(param)).into() - } - (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.to_ty(ty).into() - } - (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - self.const_arg_to_const(&ct.value, param.def_id).into() - } - _ => unreachable!(), - }, - // Provide substitutions for parameters for which arguments are inferred. - |substs, param, infer_args| { - match param.kind { - GenericParamDefKind::Lifetime => { - self.re_infer(Some(param), span).unwrap().into() - } - GenericParamDefKind::Type { has_default, .. } => { - if !infer_args && has_default { - // If we have a default, then we it doesn't matter that we're not - // inferring the type arguments: we provide the default where any - // is missing. - let default = tcx.type_of(param.def_id); - self.normalize_ty( - span, - default.subst_spanned(tcx, substs.unwrap(), Some(span)), - ) - .into() - } else { - // If no type arguments were provided, we have to infer them. - // This case also occurs as a result of some malformed input, e.g. - // a lifetime argument being given instead of a type parameter. - // Using inference instead of `Error` gives better error messages. - self.var_for_def(span, param) - } - } - GenericParamDefKind::Const => { - // FIXME(const_generics:defaults) - // No const parameters were provided, we have to infer them. - self.var_for_def(span, param) - } - } - }, - ) - }); - assert!(!substs.has_escaping_bound_vars()); - assert!(!ty.has_escaping_bound_vars()); - - // First, store the "user substs" for later. - self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); - - self.add_required_obligations(span, def_id, &substs); - - // Substitute the values for the type parameters into the type of - // the referenced item. - let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty); - - if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { - // In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method` - // is inherent, there is no `Self` parameter; instead, the impl needs - // type parameters, which we can infer by unifying the provided `Self` - // with the substituted impl type. - // This also occurs for an enum variant on a type alias. - let ty = tcx.type_of(impl_def_id); - - let impl_ty = self.instantiate_type_scheme(span, &substs, &ty); - match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) { - Ok(ok) => self.register_infer_ok_obligations(ok), - Err(_) => { - self.tcx.sess.delay_span_bug( - span, - &format!( - "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?", - self_ty, - impl_ty, - ), - ); - } - } - } - - self.check_rustc_args_require_const(def_id, hir_id, span); - - debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted); - self.write_substs(hir_id, substs); - - (ty_substituted, res) - } - - /// Add all the obligations that are required, substituting and normalized appropriately. - fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) { - let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs); - - for (i, mut 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); - } - } - - fn check_rustc_args_require_const(&self, def_id: DefId, hir_id: hir::HirId, span: Span) { - // We're only interested in functions tagged with - // #[rustc_args_required_const], so ignore anything that's not. - if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) { - return; - } - - // If our calling expression is indeed the function itself, we're good! - // If not, generate an error that this can only be called directly. - if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) { - if let ExprKind::Call(ref callee, ..) = expr.kind { - if callee.hir_id == hir_id { - return; - } - } - } - - self.tcx.sess.span_err( - span, - "this function can only be invoked directly, not through a function pointer", - ); - } - - /// Resolves `typ` by a single level if `typ` is a type variable. - /// If no resolution is possible, then an error is reported. - /// Numeric inference variables may be left unresolved. - pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.resolve_vars_with_obligations(ty); - if !ty.is_ty_var() { - ty - } else { - if !self.is_tainted_by_errors() { - self.need_type_info_err((**self).body_id, sp, ty, E0282) - .note("type must be known at this point") - .emit(); - } - let err = self.tcx.ty_error(); - self.demand_suptype(sp, err, ty); - err - } - } +/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field. +#[derive(Copy, Clone)] +struct MaybeInProgressTables<'a, 'tcx> { + maybe_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>, +} - fn with_breakable_ctxt<F: FnOnce() -> R, R>( - &self, - id: hir::HirId, - ctxt: BreakableCtxt<'tcx>, - f: F, - ) -> (BreakableCtxt<'tcx>, R) { - let index; - { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - index = enclosing_breakables.stack.len(); - enclosing_breakables.by_id.insert(id, index); - enclosing_breakables.stack.push(ctxt); +impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> { + fn borrow(self) -> Ref<'a, ty::TypeckResults<'tcx>> { + match self.maybe_typeck_results { + Some(typeck_results) => typeck_results.borrow(), + None => bug!( + "MaybeInProgressTables: inh/fcx.typeck_results.borrow() with no typeck results" + ), } - let result = f(); - let ctxt = { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - debug_assert!(enclosing_breakables.stack.len() == index + 1); - enclosing_breakables.by_id.remove(&id).expect("missing breakable context"); - enclosing_breakables.stack.pop().expect("missing breakable context") - }; - (ctxt, result) - } - - /// Instantiate a QueryResponse in a probe context, without a - /// good ObligationCause. - fn probe_instantiate_query_response( - &self, - span: Span, - original_values: &OriginalQueryValues<'tcx>, - query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, - ) -> InferResult<'tcx, Ty<'tcx>> { - self.instantiate_query_response_and_region_obligations( - &traits::ObligationCause::misc(span, self.body_id), - self.param_env, - original_values, - query_result, - ) } - /// Returns `true` if an expression is contained inside the LHS of an assignment expression. - fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool { - let mut contained_in_place = false; - - while let hir::Node::Expr(parent_expr) = - self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id)) - { - match &parent_expr.kind { - hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => { - if lhs.hir_id == expr_id { - contained_in_place = true; - break; - } - } - _ => (), - } - expr_id = parent_expr.hir_id; + fn borrow_mut(self) -> RefMut<'a, ty::TypeckResults<'tcx>> { + match self.maybe_typeck_results { + Some(typeck_results) => typeck_results.borrow_mut(), + None => bug!( + "MaybeInProgressTables: inh/fcx.typeck_results.borrow_mut() with no typeck results" + ), } - - contained_in_place } } -fn check_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, generics: &ty::Generics, ty: Ty<'tcx>) { - debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty); - - assert_eq!(generics.parent, None); - - if generics.own_counts().types == 0 { - return; - } - - let mut params_used = BitSet::new_empty(generics.params.len()); - - if ty.references_error() { - // If there is already another error, do not emit - // an error for not using a type parameter. - assert!(tcx.sess.has_errors()); - return; - } +struct CheckItemTypesVisitor<'tcx> { + tcx: TyCtxt<'tcx>, +} - for leaf in ty.walk() { - if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { - if let ty::Param(param) = leaf_ty.kind { - debug!("found use of ty param {:?}", param); - params_used.insert(param.index); - } - } +impl ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'tcx> { + fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) { + check_item_type(self.tcx, i); } + fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} + fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} +} - for param in &generics.params { - if !params_used.contains(param.index) { - if let ty::GenericParamDefKind::Type { .. } = param.kind { - let span = tcx.def_span(param.def_id); - struct_span_err!( - tcx.sess, - span, - E0091, - "type parameter `{}` is unused", - param.name, - ) - .span_label(span, "unused type parameter") - .emit(); - } - } - } +fn typeck_item_bodies(tcx: TyCtxt<'_>, crate_num: CrateNum) { + debug_assert!(crate_num == LOCAL_CRATE); + tcx.par_body_owners(|body_owner_def_id| { + tcx.ensure().typeck(body_owner_def_id); + }); } fn fatally_break_rust(sess: &Session) { diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 66fb01a54f5..66975f32a1f 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -206,7 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(method) => { let by_ref_binop = !op.node.is_by_value(); if is_assign == IsAssign::Yes || by_ref_binop { - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind { + if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() { let mutbl = match mutbl { hir::Mutability::Not => AutoBorrowMutability::Not, hir::Mutability::Mut => AutoBorrowMutability::Mut { @@ -223,7 +223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if by_ref_binop { - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind { + if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() { let mutbl = match mutbl { hir::Mutability::Not => AutoBorrowMutability::Not, hir::Mutability::Mut => AutoBorrowMutability::Mut { @@ -395,7 +395,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; let mut suggested_deref = false; - if let Ref(_, rty, _) = lhs_ty.kind { + if let Ref(_, rty, _) = lhs_ty.kind() { if { self.infcx.type_is_copy_modulo_regions(self.param_env, rty, lhs_expr.span) && self @@ -436,7 +436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // concatenation (e.g., "Hello " + "World!"). This means // we don't want the note in the else clause to be emitted } else if let [ty] = &visitor.0[..] { - if let ty::Param(p) = ty.kind { + if let ty::Param(p) = *ty.kind() { // Check if the method would be found if the type param wasn't // involved. If so, it means that adding a trait bound to the param is // enough. Otherwise we do not give the suggestion. @@ -468,7 +468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } } else { - bug!("type param visitor stored a non type param: {:?}", ty.kind); + bug!("type param visitor stored a non type param: {:?}", ty.kind()); } } else if !suggested_deref && !involves_fn { suggest_impl_missing(&mut err, lhs_ty, &missing_trait); @@ -494,7 +494,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_assign: IsAssign, ) -> bool /* did we suggest to call a function because of missing parenthesis? */ { err.span_label(span, ty.to_string()); - if let FnDef(def_id, _) = ty.kind { + if let FnDef(def_id, _) = *ty.kind() { let source_map = self.tcx.sess.source_map(); if !self.tcx.has_typeck_results(def_id) { return false; @@ -502,7 +502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We're emitting a suggestion, so we can just ignore regions let fn_sig = self.tcx.fn_sig(def_id).skip_binder(); - let other_ty = if let FnDef(def_id, _) = other_ty.kind { + let other_ty = if let FnDef(def_id, _) = *other_ty.kind() { if !self.tcx.has_typeck_results(def_id) { return false; } @@ -568,10 +568,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => false, }; - match (&lhs_ty.kind, &rhs_ty.kind) { + match (lhs_ty.kind(), rhs_ty.kind()) { (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str - if (l_ty.kind == Str || is_std_string(l_ty)) && ( - r_ty.kind == Str || is_std_string(r_ty) || + if (*l_ty.kind() == Str || is_std_string(l_ty)) && ( + *r_ty.kind() == Str || is_std_string(r_ty) || &format!("{:?}", rhs_ty) == "&&str" ) => { @@ -589,10 +589,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { msg }, - if lstring.starts_with('&') { + if let Some(stripped) = lstring.strip_prefix('&') { // let a = String::new(); // let _ = &a + "bar"; - lstring[1..].to_string() + stripped.to_string() } else { format!("{}.to_owned()", lstring) }, @@ -605,7 +605,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true } (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String` - if (l_ty.kind == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) => + if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) => { err.span_label( op.span, @@ -617,10 +617,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_assign, ) { (Ok(l), Ok(r), IsAssign::No) => { - let to_string = if l.starts_with('&') { + let to_string = if let Some(stripped) = l.strip_prefix('&') { // let a = String::new(); let b = String::new(); // let _ = &a + b; - l[1..].to_string() + stripped.to_string() } else { format!("{}.to_owned()", l) }; @@ -670,12 +670,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ex.span, format!("cannot apply unary operator `{}`", op.as_str()), ); - match actual.kind { + match actual.kind() { Uint(_) if op == hir::UnOp::UnNeg => { err.note("unsigned values cannot be negated"); } Str | Never | Char | Tuple(_) | Array(_, _) => {} - Ref(_, ref lty, _) if lty.kind == Str => {} + Ref(_, ref lty, _) if *lty.kind() == Str => {} _ => { let missing_trait = match op { hir::UnOp::UnNeg => "std::ops::Neg", @@ -844,7 +844,7 @@ enum Op { /// Dereferences a single level of immutable referencing. fn deref_ty_if_possible(ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { + match ty.kind() { ty::Ref(_, ty, hir::Mutability::Not) => ty, _ => ty, } @@ -903,7 +903,7 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool /// If applicable, note that an implementation of `trait` for `ty` may fix the error. fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_trait: &str) { - if let Adt(def, _) = ty.peel_refs().kind { + if let Adt(def, _) = ty.peel_refs().kind() { if def.did.is_local() { err.note(&format!( "an implementation of `{}` might be missing for `{}`", @@ -957,7 +957,7 @@ struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>); impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - if let ty::Param(_) = ty.kind { + if let ty::Param(_) = ty.kind() { self.0.push(ty); } ty.super_visit_with(self) @@ -972,7 +972,7 @@ impl TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind { + match ty.kind() { ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span: self.1, diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index d1864ee2b35..321472b8fe8 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -1,5 +1,6 @@ use crate::check::FnCtxt; use rustc_ast as ast; + use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; @@ -10,7 +11,7 @@ use rustc_hir::{HirId, Pat, PatKind}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, BindingMode, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::Ident; @@ -281,7 +282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 { + PatKind::Lit(lt) => match self.check_expr(lt).kind() { ty::Ref(..) => AdjustMode::Pass, _ => AdjustMode::Peel, }, @@ -341,7 +342,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // See the examples in `ui/match-defbm*.rs`. let mut pat_adjustments = vec![]; - while let ty::Ref(_, inner_ty, inner_mutability) = expected.kind { + while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() { debug!("inspecting {:?}", expected); debug!("current discriminant is Ref, inserting implicit deref"); @@ -389,9 +390,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut pat_ty = ty; if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(_), .. }) = lt.kind { let expected = self.structurally_resolved_type(span, expected); - if let ty::Ref(_, ty::TyS { kind: ty::Slice(_), .. }, _) = expected.kind { - let tcx = self.tcx; - pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8)); + if let ty::Ref(_, inner_ty, _) = expected.kind() { + if matches!(inner_ty.kind(), ty::Slice(_)) { + let tcx = self.tcx; + pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8)); + } } } @@ -639,7 +642,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_dereferenceable(&self, span: Span, expected: Ty<'tcx>, inner: &Pat<'_>) -> bool { if let PatKind::Binding(..) = inner.kind { if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) { - if let ty::Dynamic(..) = mt.ty.kind { + if let ty::Dynamic(..) = mt.ty.kind() { // This is "x = SomeTrait" being reduced from // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error. let type_str = self.ty_to_string(expected); @@ -733,17 +736,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(err) = self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) { - self.emit_bad_pat_path(err, pat.span, res, pat_res, segments, ti.parent_pat); + self.emit_bad_pat_path(err, pat.span, res, pat_res, pat_ty, segments, ti.parent_pat); } pat_ty } + fn maybe_suggest_range_literal( + &self, + e: &mut DiagnosticBuilder<'_>, + opt_def_id: Option<hir::def_id::DefId>, + ident: Ident, + ) -> bool { + match opt_def_id { + Some(def_id) => match self.tcx.hir().get_if_local(def_id) { + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(_, body_id), .. + })) => match self.tcx.hir().get(body_id.hir_id) { + hir::Node::Expr(expr) => { + if hir::is_range_literal(expr) { + let span = self.tcx.hir().span(body_id.hir_id); + if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) { + e.span_suggestion_verbose( + ident.span, + "you may want to move the range into the match block", + snip, + Applicability::MachineApplicable, + ); + return true; + } + } + } + _ => (), + }, + _ => (), + }, + _ => (), + } + false + } + fn emit_bad_pat_path( &self, mut e: DiagnosticBuilder<'_>, pat_span: Span, res: Res, pat_res: Res, + pat_ty: Ty<'tcx>, segments: &'b [hir::PathSegment<'b>], parent_pat: Option<&Pat<'_>>, ) { @@ -769,9 +807,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } _ => { - let msg = "introduce a new binding instead"; - let sugg = format!("other_{}", ident.as_str().to_lowercase()); - e.span_suggestion(ident.span, msg, sugg, Applicability::HasPlaceholders); + let (type_def_id, item_def_id) = match pat_ty.kind() { + Adt(def, _) => match res { + Res::Def(DefKind::Const, def_id) => (Some(def.did), Some(def_id)), + _ => (None, None), + }, + _ => (None, None), + }; + + let ranges = &[ + self.tcx.lang_items().range_struct(), + self.tcx.lang_items().range_from_struct(), + self.tcx.lang_items().range_to_struct(), + self.tcx.lang_items().range_full_struct(), + self.tcx.lang_items().range_inclusive_struct(), + self.tcx.lang_items().range_to_inclusive_struct(), + ]; + if type_def_id != None && ranges.contains(&type_def_id) { + if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) { + let msg = "constants only support matching by type, \ + if you meant to match against a range of values, \ + consider using a range pattern like `min ..= max` in the match block"; + e.note(msg); + } + } else { + let msg = "introduce a new binding instead"; + let sugg = format!("other_{}", ident.as_str().to_lowercase()); + e.span_suggestion( + ident.span, + msg, + sugg, + Applicability::HasPlaceholders, + ); + } } }; } @@ -871,7 +939,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if subpats.len() == variant.fields.len() || subpats.len() < variant.fields.len() && ddpos.is_some() { - let substs = match pat_ty.kind { + let substs = match pat_ty.kind() { ty::Adt(_, substs) => substs, _ => bug!("unexpected pattern type {:?}", pat_ty), }; @@ -924,13 +992,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // More generally, the expected type wants a tuple variant with one field of an // N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern // with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`. - let missing_parenthesis = match (&expected.kind, fields, had_err) { + let missing_parenthesis = match (&expected.kind(), fields, had_err) { // #67037: only do this if we could successfully type-check the expected type against // the tuple struct pattern. Otherwise the substs could get out of range on e.g., // `let P() = U;` where `P != U` with `struct P<T>(T);`. (ty::Adt(_, substs), [field], false) => { let field_ty = self.field_ty(pat_span, field, substs); - match field_ty.kind { + match field_ty.kind() { ty::Tuple(_) => field_ty.tuple_fields().count() == subpats.len(), _ => false, } @@ -981,7 +1049,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut expected_len = elements.len(); if ddpos.is_some() { // Require known type only when `..` is present. - if let ty::Tuple(ref tys) = self.structurally_resolved_type(span, expected).kind { + if let ty::Tuple(ref tys) = self.structurally_resolved_type(span, expected).kind() { expected_len = tys.len(); } } @@ -1025,7 +1093,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> bool { let tcx = self.tcx; - let (substs, adt) = match adt_ty.kind { + let (substs, adt) = match adt_ty.kind() { ty::Adt(adt, substs) => (substs, adt), _ => span_bug!(pat.span, "struct pattern is not an ADT"), }; @@ -1076,8 +1144,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut unmentioned_fields = variant .fields .iter() - .map(|field| field.ident.normalize_to_macros_2_0()) - .filter(|ident| !used_fields.contains_key(&ident)) + .map(|field| (field, field.ident.normalize_to_macros_2_0())) + .filter(|(_, ident)| !used_fields.contains_key(&ident)) .collect::<Vec<_>>(); let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered()) { @@ -1108,7 +1176,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit(); } } else if !etc && !unmentioned_fields.is_empty() { - unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields)); + let no_accessible_unmentioned_fields = unmentioned_fields + .iter() + .find(|(field, _)| { + field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx) + }) + .is_none(); + + if no_accessible_unmentioned_fields { + unmentioned_err = Some(self.error_no_accessible_fields(pat, &fields)); + } else { + unmentioned_err = + Some(self.error_unmentioned_fields(pat, &unmentioned_fields, &fields)); + } } match (inexistent_fields_err, unmentioned_err) { (Some(mut i), Some(mut u)) => { @@ -1171,7 +1251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, kind_name: &str, inexistent_fields: &[Ident], - unmentioned_fields: &mut Vec<Ident>, + unmentioned_fields: &mut Vec<(&ty::FieldDef, Ident)>, variant: &ty::VariantDef, ) -> DiagnosticBuilder<'tcx> { let tcx = self.tcx; @@ -1213,7 +1293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); if plural == "" { - let input = unmentioned_fields.iter().map(|field| &field.name); + let input = unmentioned_fields.iter().map(|(_, field)| &field.name); let suggested_name = find_best_match_for_name(input, ident.name, None); if let Some(suggested_name) = suggested_name { err.span_suggestion( @@ -1230,7 +1310,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `smart_resolve_context_dependent_help`. if suggested_name.to_ident_string().parse::<usize>().is_err() { // We don't want to throw `E0027` in case we have thrown `E0026` for them. - unmentioned_fields.retain(|&x| x.name != suggested_name); + unmentioned_fields.retain(|&(_, x)| x.name != suggested_name); } } } @@ -1298,17 +1378,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } + /// Returns a diagnostic reporting a struct pattern which is missing an `..` due to + /// inaccessible fields. + /// + /// ```ignore (diagnostic) + /// error: pattern requires `..` due to inaccessible fields + /// --> src/main.rs:10:9 + /// | + /// LL | let foo::Foo {} = foo::Foo::default(); + /// | ^^^^^^^^^^^ + /// | + /// help: add a `..` + /// | + /// LL | let foo::Foo { .. } = foo::Foo::default(); + /// | ^^^^^^ + /// ``` + fn error_no_accessible_fields( + &self, + pat: &Pat<'_>, + fields: &'tcx [hir::FieldPat<'tcx>], + ) -> DiagnosticBuilder<'tcx> { + let mut err = self + .tcx + .sess + .struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields"); + + if let Some(field) = fields.last() { + err.span_suggestion_verbose( + field.span.shrink_to_hi(), + "ignore the inaccessible and unused fields", + ", ..".to_string(), + Applicability::MachineApplicable, + ); + } else { + let qpath_span = if let PatKind::Struct(qpath, ..) = &pat.kind { + qpath.span() + } else { + bug!("`error_no_accessible_fields` called on non-struct pattern"); + }; + + // Shrink the span to exclude the `foo:Foo` in `foo::Foo { }`. + let span = pat.span.with_lo(qpath_span.shrink_to_hi().hi()); + err.span_suggestion_verbose( + span, + "ignore the inaccessible and unused fields", + " { .. }".to_string(), + Applicability::MachineApplicable, + ); + } + err + } + + /// Returns a diagnostic reporting a struct pattern which does not mention some fields. + /// + /// ```ignore (diagnostic) + /// error[E0027]: pattern does not mention field `you_cant_use_this_field` + /// --> src/main.rs:15:9 + /// | + /// LL | let foo::Foo {} = foo::Foo::new(); + /// | ^^^^^^^^^^^ missing field `you_cant_use_this_field` + /// ``` fn error_unmentioned_fields( &self, pat: &Pat<'_>, - unmentioned_fields: &[Ident], + unmentioned_fields: &[(&ty::FieldDef, Ident)], + fields: &'tcx [hir::FieldPat<'tcx>], ) -> DiagnosticBuilder<'tcx> { let field_names = if unmentioned_fields.len() == 1 { - format!("field `{}`", unmentioned_fields[0]) + format!("field `{}`", unmentioned_fields[0].1) } else { let fields = unmentioned_fields .iter() - .map(|name| format!("`{}`", name)) + .map(|(_, name)| format!("`{}`", name)) .collect::<Vec<String>>() .join(", "); format!("fields {}", fields) @@ -1321,14 +1462,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field_names ); err.span_label(pat.span, format!("missing {}", field_names)); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "This error indicates that a pattern for a struct fails to specify a \ - sub-pattern for every one of the struct's fields. Ensure that each field \ - from the struct's definition is mentioned in the pattern, or use `..` to \ - ignore unwanted fields.", - ); - } + let len = unmentioned_fields.len(); + let (prefix, postfix, sp) = match fields { + [] => match &pat.kind { + PatKind::Struct(path, [], false) => { + (" { ", " }", path.span().shrink_to_hi().until(pat.span.shrink_to_hi())) + } + _ => return err, + }, + [.., field] => ( + match pat.kind { + PatKind::Struct(_, [_, ..], _) => ", ", + _ => "", + }, + "", + field.span.shrink_to_hi(), + ), + }; + err.span_suggestion( + sp, + &format!( + "include the missing field{} in the pattern", + if len == 1 { "" } else { "s" }, + ), + format!( + "{}{}{}", + prefix, + unmentioned_fields + .iter() + .map(|(_, name)| name.to_string()) + .collect::<Vec<_>>() + .join(", "), + postfix, + ), + Applicability::MachineApplicable, + ); + err.span_suggestion( + sp, + &format!( + "if you don't care about {} missing field{}, you can explicitely ignore {}", + if len == 1 { "this" } else { "these" }, + if len == 1 { "" } else { "s" }, + if len == 1 { "it" } else { "them" }, + ), + format!("{}..{}", prefix, postfix), + Applicability::MachineApplicable, + ); err } @@ -1378,7 +1557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // to avoid creating needless variables. This also helps with // the bad interactions of the given hack detailed in (note_1). debug!("check_pat_ref: expected={:?}", expected); - match expected.kind { + match *expected.kind() { ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), _ => { let inner_ty = self.next_ty_var(TypeVariableOrigin { @@ -1434,7 +1613,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ti: TopInfo<'tcx>, ) -> Ty<'tcx> { let expected = self.structurally_resolved_type(span, expected); - let (element_ty, opt_slice_ty, inferred) = match expected.kind { + let (element_ty, opt_slice_ty, inferred) = match *expected.kind() { // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. ty::Array(element_ty, len) => { let min = before.len() as u64 + after.len() as u64; @@ -1570,8 +1749,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "expected an array or slice, found `{}`", expected_ty ); - if let ty::Ref(_, ty, _) = expected_ty.kind { - if let ty::Array(..) | ty::Slice(..) = ty.kind { + if let ty::Ref(_, ty, _) = expected_ty.kind() { + if let ty::Array(..) | ty::Slice(..) = ty.kind() { err.help("the semantics of slice patterns changed recently; see issue #62254"); } } diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs index 4bef9aecd2e..502cb562385 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs @@ -25,7 +25,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?; let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind { + if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { self.apply_adjustments( oprnd_expr, vec![Adjustment { @@ -86,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut self_ty = adjusted_ty; if unsize { // We only unsize arrays here. - if let ty::Array(element_ty, _) = adjusted_ty.kind { + if let ty::Array(element_ty, _) = adjusted_ty.kind() { self_ty = self.tcx.mk_slice(element_ty); } else { continue; @@ -108,7 +108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let method = self.register_infer_ok_obligations(ok); let mut adjustments = self.adjust_steps(autoderef); - if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind { + if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)), target: self.tcx.mk_ref( @@ -193,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` /// into `DerefMut` and `IndexMut` respectively. /// - /// This is a second pass of typechecking derefs/indices. We need this we do not + /// This is a second pass of typechecking derefs/indices. We need this because we do not /// always know whether a place needs to be mutable or not in the first pass. /// This happens whether there is an implicit mutable reborrow, e.g. when the type /// is used as the receiver of a method call. @@ -211,13 +211,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); // Fix up autoderefs and derefs. + let mut inside_union = false; for (i, &expr) in exprs.iter().rev().enumerate() { debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); + let mut source = self.node_ty(expr.hir_id); + if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnDeref, _)) { + // Clear previous flag; after a pointer indirection it does not apply any more. + inside_union = false; + } + if source.ty_adt_def().map_or(false, |adt| adt.is_union()) { + inside_union = true; + } // Fix up the autoderefs. Autorefs can only occur immediately preceding // overloaded place ops, and will be fixed by them in order to get // the correct region. - let mut source = self.node_ty(expr.hir_id); // Do not mutate adjustments in place, but rather take them, // and replace them after mutating them, to avoid having the // typeck results borrowed during (`deref_mut`) method resolution. @@ -233,8 +241,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PlaceOp::Deref, ) { let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, mutbl) = method.sig.output().kind { - *deref = OverloadedDeref { region, mutbl }; + if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { + *deref = OverloadedDeref { region, mutbl, span: deref.span }; + } + // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). + // This helps avoid accidental drops. + if inside_union + && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop()) + { + let mut err = self.tcx.sess.struct_span_err( + expr.span, + "not automatically applying `DerefMut` on `ManuallyDrop` union field", + ); + err.help( + "writing to this reference calls the destructor for the old value", + ); + err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor"); + err.emit(); } } } @@ -305,7 +328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 { + let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind() { r } 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 484961dbdb8..ba0f22513a1 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -624,7 +624,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { ); let rptr_ty = self.resolve_node_type(id); - if let ty::Ref(r, _, _) = rptr_ty.kind { + if let ty::Ref(r, _, _) = rptr_ty.kind() { debug!("rptr_ty={}", rptr_ty); self.link_region(span, r, ty::BorrowKind::from_mutbl(mutbl), cmt_borrowed); } @@ -649,7 +649,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { "link_region(borrow_region={:?}, borrow_kind={:?}, pointer_ty={:?})", borrow_region, borrow_kind, borrow_place ); - match pointer_ty.kind { + match *pointer_ty.kind() { ty::RawPtr(_) => return, ty::Ref(ref_region, _, ref_mutability) => { if self.link_reborrowed_region(span, borrow_region, ref_region, ref_mutability) @@ -794,7 +794,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { // A closure capture can't be borrowed for longer than the // reference to the closure. - if let ty::Closure(_, substs) = ty.kind { + if let ty::Closure(_, substs) = ty.kind() { match self.infcx.closure_kind(substs) { Some(ty::ClosureKind::Fn | ty::ClosureKind::FnMut) => { // Region of environment pointer diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 9bb84c07868..2c3be0da5dd 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -88,7 +88,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Extract the type of the closure. let ty = self.node_ty(closure_hir_id); - let (closure_def_id, substs) = match ty.kind { + let (closure_def_id, substs) = match *ty.kind() { ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), ty::Error(_) => { @@ -349,7 +349,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { let mut borrow_kind = ty::MutBorrow; for pointer_ty in place_with_id.place.deref_tys() { - match pointer_ty.kind { + match pointer_ty.kind() { // Raw pointers don't inherit mutability. ty::RawPtr(_) => return, // assignment to deref of an `&mut` diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 9c692edaa7f..5203f3fa8f1 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -291,7 +291,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { let err_ty_str; let mut is_ptr = true; let err = if tcx.features().min_const_generics { - match ty.kind { + match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None, ty::FnPtr(_) => Some("function pointers"), ty::RawPtr(_) => Some("raw pointers"), @@ -302,7 +302,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { } } } else { - match ty.peel_refs().kind { + match ty.peel_refs().kind() { ty::FnPtr(_) => Some("function pointers"), ty::RawPtr(_) => Some("raw pointers"), _ => None, @@ -338,7 +338,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { // We use the same error code in both branches, because this is really the same // issue: we just special-case the message for type parameters to make it // clearer. - if let ty::Param(_) = ty.peel_refs().kind { + if let ty::Param(_) = ty.peel_refs().kind() { // Const parameters may not have type parameters as their types, // because we cannot be sure that the type parameter derives `PartialEq` // and `Eq` (just implementing them is not enough for `structural_match`). @@ -638,7 +638,7 @@ fn check_associated_type_defaults(fcx: &FnCtxt<'_, '_>, trait_def_id: DefId) { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind { + match t.kind() { ty::Projection(proj_ty) => { if let Some(default) = self.map.get(&proj_ty) { default @@ -709,7 +709,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo let mut forbid_unsized = true; if allow_foreign_ty { let tail = fcx.tcx.struct_tail_erasing_lifetimes(item_ty, fcx.param_env); - if let ty::Foreign(_) = tail.kind { + if let ty::Foreign(_) = tail.kind() { forbid_unsized = false; } } @@ -867,7 +867,7 @@ fn check_where_clauses<'tcx, 'fcx>( } impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams { fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - if let ty::Param(param) = t.kind { + if let ty::Param(param) = t.kind() { self.params.insert(param.index); } t.super_visit_with(self) @@ -1001,7 +1001,7 @@ fn check_opaque_types<'fcx, 'tcx>( ty.fold_with(&mut ty::fold::BottomUpFolder { tcx: fcx.tcx, ty_op: |ty| { - if let ty::Opaque(def_id, substs) = ty.kind { + if let ty::Opaque(def_id, substs) = *ty.kind() { trace!("check_opaque_types: opaque_ty, {:?}, {:?}", def_id, substs); let generics = tcx.generics_of(def_id); @@ -1044,7 +1044,7 @@ fn check_opaque_types<'fcx, 'tcx>( let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default(); for (i, arg) in substs.iter().enumerate() { let arg_is_param = match arg.unpack() { - GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)), + GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), GenericArgKind::Lifetime(region) => { if let ty::ReStatic = region { @@ -1177,7 +1177,7 @@ fn e0307(fcx: &FnCtxt<'fcx, 'tcx>, span: Span, receiver_ty: Ty<'_>) { fcx.tcx.sess.diagnostic(), span, E0307, - "invalid `self` parameter type: {:?}", + "invalid `self` parameter type: {}", receiver_ty, ) .note("type of `self` must be `Self` or a type that dereferences to it") diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 67f67e64dd4..b55f62ee436 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -193,7 +193,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let mut typeck_results = self.fcx.typeck_results.borrow_mut(); // All valid indexing looks like this; might encounter non-valid indexes at this point. - let base_ty = typeck_results.expr_ty_adjusted_opt(&base).map(|t| &t.kind); + let base_ty = typeck_results.expr_ty_adjusted_opt(&base).map(|t| t.kind()); if base_ty.is_none() { // When encountering `return [0][0]` outside of a `fn` body we can encounter a base // that isn't in the type table. We assume more relevant errors have already been @@ -459,7 +459,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let mut skip_add = false; - if let ty::Opaque(defin_ty_def_id, _substs) = definition_ty.kind { + if let ty::Opaque(defin_ty_def_id, _substs) = *definition_ty.kind() { if let hir::OpaqueTyOrigin::Misc = opaque_defn.origin { if def_id == defin_ty_def_id { debug!( diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index 0d3cac7f7f3..89270fb6c77 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -1,6 +1,7 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. +use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem}; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -48,7 +49,7 @@ impl<'tcx> Checker<'tcx> { fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { // Destructors only work on nominal types. - if let ty::Adt(..) | ty::Error(_) = tcx.type_of(impl_did).kind { + if let ty::Adt(..) | ty::Error(_) = tcx.type_of(impl_did).kind() { return; } @@ -58,14 +59,7 @@ fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { _ => bug!("expected Drop impl item"), }; - struct_span_err!( - tcx.sess, - sp, - E0120, - "the `Drop` trait may only be implemented for structs, enums, and unions", - ) - .span_label(sp, "must be a struct, enum, or union") - .emit(); + tcx.sess.emit_err(DropImplOnWrongItem { span: sp }); } fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { @@ -108,25 +102,10 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { let span = if let ItemKind::Impl { self_ty, .. } = item.kind { self_ty.span } else { span }; - struct_span_err!( - tcx.sess, - span, - E0206, - "the trait `Copy` may not be implemented for this type" - ) - .span_label(span, "type is not a structure or enumeration") - .emit(); + tcx.sess.emit_err(CopyImplOnNonAdt { span }); } Err(CopyImplementationError::HasDestructor) => { - struct_span_err!( - tcx.sess, - span, - E0184, - "the trait `Copy` may not be implemented for this type; the \ - type has a destructor" - ) - .span_label(span, "Copy not allowed on types with destructors") - .emit(); + tcx.sess.emit_err(CopyImplOnTypeWithDtor { span }); } } } @@ -168,7 +147,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef let cause = ObligationCause::misc(span, impl_hir_id); use ty::TyKind::*; - match (&source.kind, &target.kind) { + match (source.kind(), target.kind()) { (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok() && mutbl_a == *mutbl_b => {} (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), @@ -352,7 +331,7 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI } (mt_a.ty, mt_b.ty, unsize_trait, None) }; - let (source, target, trait_def_id, kind) = match (&source.kind, &target.kind) { + let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) { (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a); let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs index 859d510dcbe..373acb95c9e 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs @@ -52,7 +52,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { let def_id = self.tcx.hir().local_def_id(item.hir_id); let self_ty = self.tcx.type_of(def_id); let lang_items = self.tcx.lang_items(); - match self_ty.kind { + match *self_ty.kind() { ty::Adt(def, _) => { self.check_def_id(item, def.did); } @@ -123,7 +123,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { ); } ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Not }) - if matches!(inner.kind, ty::Slice(_)) => + if matches!(inner.kind(), ty::Slice(_)) => { self.check_primitive_impl( def_id, @@ -135,7 +135,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { ); } ty::RawPtr(ty::TypeAndMut { ty: inner, mutbl: hir::Mutability::Mut }) - if matches!(inner.kind, ty::Slice(_)) => + if matches!(inner.kind(), ty::Slice(_)) => { self.check_primitive_impl( def_id, @@ -308,18 +308,25 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { } ty::Error(_) => {} _ => { - struct_span_err!( + let mut err = struct_span_err!( self.tcx.sess, ty.span, E0118, - "no base type found for inherent implementation" - ) - .span_label(ty.span, "impl requires a base type") - .note( - "either implement a trait on it or create a newtype \ - to wrap it instead", - ) - .emit(); + "no nominal type found for inherent implementation" + ); + + err.span_label(ty.span, "impl requires a nominal type") + .note("either implement a trait on it or create a newtype to wrap it instead"); + + if let ty::Ref(_, subty, _) = self_ty.kind() { + err.note(&format!( + "you could also try moving the reference to \ + uses of `{}` (such as `self`) within the implementation", + subty + )); + } + + err.emit(); } } } diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs index 1483244717b..4294450333c 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_typeck/src/coherence/mod.rs @@ -209,7 +209,7 @@ fn check_object_overlap<'tcx>( } // check for overlap with the automatic `impl Trait for dyn Trait` - if let ty::Dynamic(ref data, ..) = trait_ref.self_ty().kind { + if let ty::Dynamic(ref data, ..) = trait_ref.self_ty().kind() { // This is something like impl Trait1 for Trait2. Illegal // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe. diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index 71469770f2a..917fc5631c4 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -52,7 +52,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { // Remove the lifetimes unnecessary for this error. ty = infcx.freshen(ty); }); - ty = match ty.kind { + 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 @@ -62,7 +62,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { _ => ty, }; let this = "this".to_string(); - let (ty, postfix) = match &ty.kind { + 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"), @@ -185,7 +185,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { ); 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 { + let opt_self_def_id = match *self_ty.kind() { ty::Adt(self_def, _) => Some(self_def.did), ty::Foreign(did) => Some(did), _ => None, @@ -230,6 +230,14 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { return; } } + + 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(); + } } } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 7a3f7ec56a2..a571bd58abc 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -18,6 +18,7 @@ use crate::astconv::{AstConv, SizedByDefault}; use crate::bounds::Bounds; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::constrained_generic_params as cgp; +use crate::errors; use crate::middle::resolve_lifetime as rl; use rustc_ast as ast; use rustc_ast::MetaItemKind; @@ -834,16 +835,11 @@ fn convert_variant( let fid = tcx.hir().local_def_id(f.hir_id); let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned(); if let Some(prev_span) = dup_span { - struct_span_err!( - tcx.sess, - f.span, - E0124, - "field `{}` is already declared", - f.ident - ) - .span_label(f.span, "field already declared") - .span_label(prev_span, format!("`{}` first declared here", f.ident)) - .emit(); + tcx.sess.emit_err(errors::FieldAlreadyDeclared { + field_name: f.ident, + span: f.span, + prev_span, + }); } else { seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span); } @@ -1676,6 +1672,7 @@ fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicate .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied()); } } + debug!("predicates_defined_on({:?}) = {:?}", def_id, result); result } @@ -1717,29 +1714,6 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat debug!("explicit_predicates_of(def_id={:?})", def_id); - /// A data structure with unique elements, which preserves order of insertion. - /// Preserving the order of insertion is important here so as not to break - /// compile-fail UI tests. - struct UniquePredicates<'tcx> { - predicates: FxIndexSet<(ty::Predicate<'tcx>, Span)>, - } - - impl<'tcx> UniquePredicates<'tcx> { - fn new() -> Self { - UniquePredicates { predicates: FxIndexSet::default() } - } - - fn push(&mut self, value: (ty::Predicate<'tcx>, Span)) { - self.predicates.insert(value); - } - - fn extend<I: IntoIterator<Item = (ty::Predicate<'tcx>, Span)>>(&mut self, iter: I) { - for value in iter { - self.push(value); - } - } - } - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let node = tcx.hir().get(hir_id); @@ -1752,7 +1726,10 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat const NO_GENERICS: &hir::Generics<'_> = &hir::Generics::empty(); - let mut predicates = UniquePredicates::new(); + // We use an `IndexSet` to preserves order of insertion. + // Preserving the order of insertion is important here so as not to break + // compile-fail UI tests. + let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default(); let ast_generics = match node { Node::TraitItem(item) => { @@ -1854,7 +1831,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat // (see below). Recall that a default impl is not itself an impl, but rather a // set of defaults that can be incorporated into another impl. if let Some(trait_ref) = is_default_impl_trait { - predicates.push(( + predicates.insert(( trait_ref.to_poly_trait_ref().without_const().to_predicate(tcx), tcx.def_span(def_id), )); @@ -1878,7 +1855,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat hir::GenericBound::Outlives(lt) => { let bound = AstConv::ast_region_to_region(&icx, <, None); let outlives = ty::Binder::bind(ty::OutlivesPredicate(region, bound)); - predicates.push((outlives.to_predicate(tcx), lt.span)); + predicates.insert((outlives.to_predicate(tcx), lt.span)); } _ => bug!(), }); @@ -1922,7 +1899,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` // is still checked for WF. if bound_pred.bounds.is_empty() { - if let ty::Param(_) = ty.kind { + if let ty::Param(_) = ty.kind() { // This is a `where T:`, which can be in the HIR from the // transformation that moves `?Sized` to `T`'s declaration. // We can skip the predicate because type parameters are @@ -1933,7 +1910,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat let span = bound_pred.bounded_ty.span; let re_root_empty = tcx.lifetimes.re_root_empty; let predicate = ty::OutlivesPredicate(ty, re_root_empty); - predicates.push(( + predicates.insert(( ty::PredicateAtom::TypeOutlives(predicate) .potentially_quantified(tcx, ty::PredicateKind::ForAll), span, @@ -1977,11 +1954,11 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat &hir::GenericBound::Outlives(ref lifetime) => { let region = AstConv::ast_region_to_region(&icx, lifetime, None); - predicates.push(( + predicates.insert(( ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, region)) .potentially_quantified(tcx, ty::PredicateKind::ForAll), lifetime.span, - )) + )); } } } @@ -2026,7 +2003,11 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat })) } - let mut predicates: Vec<_> = predicates.predicates.into_iter().collect(); + if tcx.features().const_evaluatable_checked { + predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local())); + } + + let mut predicates: Vec<_> = predicates.into_iter().collect(); // Subtle: before we store the predicates into the tcx, we // sort them so that predicates like `T: Foo<Item=U>` come @@ -2052,6 +2033,97 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat result } +fn const_evaluatable_predicates_of<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> FxIndexSet<(ty::Predicate<'tcx>, Span)> { + struct ConstCollector<'tcx> { + tcx: TyCtxt<'tcx>, + preds: FxIndexSet<(ty::Predicate<'tcx>, Span)>, + } + + impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> { + intravisit::NestedVisitorMap::None + } + + fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { + let def_id = self.tcx.hir().local_def_id(c.hir_id); + let ct = ty::Const::from_anon_const(self.tcx, def_id); + if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val { + let span = self.tcx.hir().span(c.hir_id); + self.preds.insert(( + ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx), + span, + )); + } + } + + // Look into `TyAlias`. + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + use ty::fold::{TypeFoldable, TypeVisitor}; + struct TyAliasVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + preds: &'a mut FxIndexSet<(ty::Predicate<'tcx>, Span)>, + span: Span, + } + + impl<'a, 'tcx> TypeVisitor<'tcx> for TyAliasVisitor<'a, 'tcx> { + fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> bool { + if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val { + self.preds.insert(( + ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx), + self.span, + )); + } + false + } + } + + if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind { + if let Res::Def(DefKind::TyAlias, def_id) = path.res { + let mut visitor = + TyAliasVisitor { tcx: self.tcx, preds: &mut self.preds, span: path.span }; + self.tcx.type_of(def_id).visit_with(&mut visitor); + } + } + + intravisit::walk_ty(self, ty) + } + } + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let node = tcx.hir().get(hir_id); + + let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() }; + if let hir::Node::Item(item) = node { + if let hir::ItemKind::Impl { ref of_trait, ref self_ty, .. } = item.kind { + if let Some(of_trait) = of_trait { + warn!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id); + collector.visit_trait_ref(of_trait); + } + + warn!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id); + collector.visit_ty(self_ty); + } + } + + if let Some(generics) = node.generics() { + warn!("const_evaluatable_predicates_of({:?}): visit_generics", def_id); + collector.visit_generics(generics); + } + + if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) { + warn!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id); + collector.visit_fn_decl(fn_sig.decl); + } + warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds); + + collector.preds +} + fn projection_ty_from_predicates( tcx: TyCtxt<'tcx>, key: ( @@ -2306,8 +2378,8 @@ fn from_target_feature( item.span(), format!("`{}` is not valid for this target", feature), ); - if feature.starts_with('+') { - let valid = supported_target_features.contains_key(&feature[1..]); + if let Some(stripped) = feature.strip_prefix('+') { + let valid = supported_target_features.contains_key(stripped); if valid { err.help("consider removing the leading `+` in the feature name"); } @@ -2326,7 +2398,6 @@ fn from_target_feature( Some(sym::mips_target_feature) => rust_features.mips_target_feature, Some(sym::riscv_target_feature) => rust_features.riscv_target_feature, Some(sym::avx512_target_feature) => rust_features.avx512_target_feature, - Some(sym::mmx_target_feature) => rust_features.mmx_target_feature, Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, @@ -2490,10 +2561,17 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs.export_name = Some(s); } } else if tcx.sess.check_name(attr, sym::target_feature) { - if !tcx.features().target_feature_11 { - check_target_feature_safe_fn(tcx, id, attr.span); - } else if let Some(local_id) = id.as_local() { - if tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal { + if !tcx.is_closure(id) && tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal { + if !tcx.features().target_feature_11 { + let mut err = feature_err( + &tcx.sess.parse_sess, + sym::target_feature_11, + attr.span, + "`#[target_feature(..)]` can only be applied to `unsafe` functions", + ); + err.span_label(tcx.def_span(id), "not an `unsafe` function"); + err.emit(); + } else if let Some(local_id) = id.as_local() { check_target_feature_trait_unsafe(tcx, local_id, attr.span); } } @@ -2750,21 +2828,6 @@ fn check_link_name_xor_ordinal( } } -/// Checks the function annotated with `#[target_feature]` is unsafe, -/// reporting an error if it isn't. -fn check_target_feature_safe_fn(tcx: TyCtxt<'_>, id: DefId, attr_span: Span) { - if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal { - let mut err = feature_err( - &tcx.sess.parse_sess, - sym::target_feature_11, - attr_span, - "`#[target_feature(..)]` can only be applied to `unsafe` functions", - ); - err.span_label(tcx.def_span(id), "not an `unsafe` function"); - err.emit(); - } -} - /// Checks the function annotated with `#[target_feature]` is not a safe /// trait method implementation, reporting an error if it is. fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 70ed92c5614..4b3250a1d44 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -1,5 +1,6 @@ +use crate::errors::AssocTypeOnInherentImpl; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{struct_span_err, Applicability, ErrorReported, StashKey}; +use rustc_errors::{Applicability, ErrorReported, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -382,7 +383,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { let mut used_params: FxHashSet<_> = FxHashSet::default(); for (i, arg) in substs.iter().enumerate() { let arg_is_param = match arg.unpack() { - GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)), + GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), GenericArgKind::Lifetime(lt) => { matches!(lt, ty::ReEarlyBound(_) | ty::ReFree(_)) } @@ -607,7 +608,7 @@ fn infer_placeholder_type( } None => { let mut diag = bad_placeholder_type(tcx, vec![span]); - if !matches!(ty.kind, ty::Error(_)) { + if !matches!(ty.kind(), ty::Error(_)) { diag.span_suggestion( span, "replace `_` with the correct type", @@ -627,11 +628,5 @@ fn infer_placeholder_type( } fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) { - struct_span_err!( - tcx.sess, - span, - E0202, - "associated types are not yet supported in inherent impls (see #8995)" - ) - .emit(); + tcx.sess.emit_err(AssocTypeOnInherentImpl { span }); } diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs index 7c80315ee19..09b5a9b0a65 100644 --- a/compiler/rustc_typeck/src/constrained_generic_params.rs +++ b/compiler/rustc_typeck/src/constrained_generic_params.rs @@ -57,7 +57,7 @@ struct ParameterCollector { impl<'tcx> TypeVisitor<'tcx> for ParameterCollector { fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { - match t.kind { + match *t.kind() { ty::Projection(..) | ty::Opaque(..) if !self.include_nonconstraining => { // projections are not injective return false; diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs new file mode 100644 index 00000000000..a769e48d2ca --- /dev/null +++ b/compiler/rustc_typeck/src/errors.rs @@ -0,0 +1,199 @@ +//! Errors emitted by typeck. +use rustc_macros::SessionDiagnostic; +use rustc_span::{symbol::Ident, Span, Symbol}; + +#[derive(SessionDiagnostic)] +#[error = "E0062"] +pub struct FieldMultiplySpecifiedInInitializer { + #[message = "field `{ident}` specified more than once"] + #[label = "used more than once"] + pub span: Span, + #[label = "first use of `{ident}`"] + pub prev_span: Span, + pub ident: Ident, +} + +#[derive(SessionDiagnostic)] +#[error = "E0092"] +pub struct UnrecognizedAtomicOperation<'a> { + #[message = "unrecognized atomic operation function: `{op}`"] + #[label = "unrecognized atomic operation"] + pub span: Span, + pub op: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error = "E0094"] +pub struct WrongNumberOfTypeArgumentsToInstrinsic { + #[message = "intrinsic has wrong number of type \ + parameters: found {found}, expected {expected}"] + #[label = "expected {expected} type parameter"] + pub span: Span, + pub found: usize, + pub expected: usize, +} + +#[derive(SessionDiagnostic)] +#[error = "E0093"] +pub struct UnrecognizedIntrinsicFunction { + #[message = "unrecognized intrinsic function: `{name}`"] + #[label = "unrecognized intrinsic"] + pub span: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error = "E0195"] +pub struct LifetimesOrBoundsMismatchOnTrait { + #[message = "lifetime parameters or bounds on {item_kind} `{ident}` do not match the trait declaration"] + #[label = "lifetimes do not match {item_kind} in trait"] + pub span: Span, + #[label = "lifetimes in impl do not match this {item_kind} in trait"] + pub generics_span: Option<Span>, + pub item_kind: &'static str, + pub ident: Ident, +} + +#[derive(SessionDiagnostic)] +#[error = "E0120"] +pub struct DropImplOnWrongItem { + #[message = "the `Drop` trait may only be implemented for structs, enums, and unions"] + #[label = "must be a struct, enum, or union"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0124"] +pub struct FieldAlreadyDeclared { + pub field_name: Ident, + #[message = "field `{field_name}` is already declared"] + #[label = "field already declared"] + pub span: Span, + #[label = "`{field_name}` first declared here"] + pub prev_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0184"] +pub struct CopyImplOnTypeWithDtor { + #[message = "the trait `Copy` may not be implemented for this type; the \ + type has a destructor"] + #[label = "Copy not allowed on types with destructors"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0202"] +pub struct AssocTypeOnInherentImpl { + #[message = "associated types are not yet supported in inherent impls (see #8995)"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0203"] +pub struct MultipleRelaxedDefaultBounds { + #[message = "type parameter has more than one relaxed default bound, only one is supported"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0206"] +pub struct CopyImplOnNonAdt { + #[message = "the trait `Copy` may not be implemented for this type"] + #[label = "type is not a structure or enumeration"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0224"] +pub struct TraitObjectDeclaredWithNoTraits { + #[message = "at least one trait is required for an object type"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0227"] +pub struct AmbiguousLifetimeBound { + #[message = "ambiguous lifetime bound, explicit lifetime bound required"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0229"] +pub struct AssocTypeBindingNotAllowed { + #[message = "associated type bindings are not allowed here"] + #[label = "associated type not allowed here"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0439"] +pub struct SimdShuffleMissingLength { + #[message = "invalid `simd_shuffle`, needs length: `{name}`"] + pub span: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error = "E0436"] +pub struct FunctionalRecordUpdateOnNonStruct { + #[message = "functional record update syntax requires a struct"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0516"] +pub struct TypeofReservedKeywordUsed { + #[message = "`typeof` is a reserved keyword but unimplemented"] + #[label = "reserved keyword"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0572"] +pub struct ReturnStmtOutsideOfFnBody { + #[message = "return statement outside of function body"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0627"] +pub struct YieldExprOutsideOfGenerator { + #[message = "yield expression outside of generator literal"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0639"] +pub struct StructExprNonExhaustive { + #[message = "cannot create non-exhaustive {what} using struct expression"] + pub span: Span, + pub what: &'static str, +} + +#[derive(SessionDiagnostic)] +#[error = "E0699"] +pub struct MethodCallOnUnknownType { + #[message = "the type of this value must be known to call a method on a raw pointer on it"] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error = "E0719"] +pub struct ValueOfAssociatedStructAlreadySpecified { + #[message = "the value of the associated type `{item_name}` (from trait `{def_path}`) is already specified"] + #[label = "re-bound here"] + pub span: Span, + #[label = "`{item_name}` bound here first"] + pub prev_span: Span, + pub item_name: Ident, + pub def_path: String, +} + +#[derive(SessionDiagnostic)] +#[error = "E0745"] +pub struct AddressOfTemporaryTaken { + #[message = "cannot take address of a temporary"] + #[label = "temporary value"] + pub span: Span, +} diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index e774f2d095d..e16f26c3304 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -387,7 +387,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // Select just those fields of the `with` // expression that will actually be used - match with_place.place.ty().kind { + match with_place.place.ty().kind() { ty::Adt(adt, substs) if adt.is_struct() => { // Consume those fields of the with expression that are needed. for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() { diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index 891e482b431..4901d6041d6 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -187,7 +187,7 @@ fn enforce_impl_params_are_constrained( } // (*) This is a horrible concession to reality. I think it'd be - // better to just ban unconstrianed lifetimes outright, but in + // better to just ban unconstrained lifetimes outright, but in // practice people do non-hygenic macros like: // // ``` @@ -207,7 +207,7 @@ fn enforce_impl_params_are_constrained( } fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) { - struct_span_err!( + let mut err = struct_span_err!( tcx.sess, span, E0207, @@ -215,9 +215,17 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) impl trait, self type, or predicates", kind, name - ) - .span_label(span, format!("unconstrained {} parameter", kind)) - .emit(); + ); + err.span_label(span, format!("unconstrained {} parameter", kind)); + if kind == "const" { + err.note( + "expressions using a const parameter must map each value to a distinct output value", + ); + err.note( + "proving the result of expressions other than the parameter are unique is not supported", + ); + } + err.emit(); } /// Enforce that we do not have two items in an impl with the same name. 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 3746e5778aa..60b9467fca8 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -405,6 +405,7 @@ fn trait_predicate_kind<'tcx>( | ty::PredicateAtom::ObjectSafe(_) | ty::PredicateAtom::ClosureKind(..) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => None, + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, } } diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 62f92fe7ffa..84efb92582e 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -55,7 +55,7 @@ This API is completely unstable and subject to change. */ -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![allow(non_camel_case_types)] #![feature(bool_to_option)] #![feature(box_syntax)] @@ -84,6 +84,7 @@ mod check_unused; mod coherence; mod collect; mod constrained_generic_params; +mod errors; mod impl_wf_check; mod mem_categorization; mod outlives; @@ -158,7 +159,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) { let main_id = tcx.hir().local_def_id_to_hir_id(main_def_id); let main_span = tcx.def_span(main_def_id); let main_t = tcx.type_of(main_def_id); - match main_t.kind { + match main_t.kind() { ty::FnDef(..) => { if let Some(Node::Item(it)) = tcx.hir().find(main_id) { if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind { @@ -254,7 +255,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) { let start_id = tcx.hir().local_def_id_to_hir_id(start_def_id); let start_span = tcx.def_span(start_def_id); let start_t = tcx.type_of(start_def_id); - match start_t.kind { + match start_t.kind() { ty::FnDef(..) => { if let Some(Node::Item(it)) = tcx.hir().find(start_id) { if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind { diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index 8a6fe620af7..04ead74936f 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -482,7 +482,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { let place_ty = self.expr_ty(expr)?; let base_ty = self.expr_ty_adjusted(base)?; - let (region, mutbl) = match base_ty.kind { + let (region, mutbl) = match *base_ty.kind() { ty::Ref(region, _, mutbl) => (region, mutbl), _ => span_bug!(expr.span, "cat_overloaded_place: base is not a reference"), }; @@ -542,7 +542,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ) -> McResult<VariantIdx> { let res = self.typeck_results.qpath_res(qpath, pat_hir_id); let ty = self.typeck_results.node_type(pat_hir_id); - let adt_def = match ty.kind { + let adt_def = match ty.kind() { ty::Adt(adt_def, _) => adt_def, _ => { self.tcx() @@ -577,7 +577,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { span: Span, ) -> McResult<usize> { let ty = self.typeck_results.node_type(pat_hir_id); - match ty.kind { + match ty.kind() { ty::Adt(adt_def, _) => Ok(adt_def.variants[variant_index].fields.len()), _ => { self.tcx() @@ -592,7 +592,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Here `pat_hir_id` is the HirId of the pattern itself. fn total_fields_in_tuple(&self, pat_hir_id: hir::HirId, span: Span) -> McResult<usize> { let ty = self.typeck_results.node_type(pat_hir_id); - match ty.kind { + match ty.kind() { ty::Tuple(substs) => Ok(substs.len()), _ => { self.tcx().sess.delay_span_bug(span, "tuple pattern not applied to a tuple"); diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_typeck/src/outlives/explicit.rs index 135960a4c11..ae336ccca45 100644 --- a/compiler/rustc_typeck/src/outlives/explicit.rs +++ b/compiler/rustc_typeck/src/outlives/explicit.rs @@ -57,7 +57,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { | ty::PredicateAtom::ClosureKind(..) | ty::PredicateAtom::Subtype(..) | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => (), + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => (), } } diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs index 762d4216f70..e7a9e078a73 100644 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs @@ -128,7 +128,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue, }; - match ty.kind { + match *ty.kind() { // The field is of type &'a T which means that we will have // a predicate requirement of T: 'a (T outlives 'a). // diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs index 535530a2ed4..b2b062e4095 100644 --- a/compiler/rustc_typeck/src/variance/constraints.rs +++ b/compiler/rustc_typeck/src/variance/constraints.rs @@ -140,7 +140,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { let id = tcx.hir().local_def_id_to_hir_id(def_id); let inferred_start = self.terms_cx.inferred_starts[&id]; let current_item = &CurrentItem { inferred_start }; - match tcx.type_of(def_id).kind { + match tcx.type_of(def_id).kind() { ty::Adt(def, _) => { // Not entirely obvious: constraints on structs/enums do not // affect the variance of their type parameters. See discussion @@ -257,7 +257,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { ) { debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance); - match ty.kind { + match *ty.kind() { ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_typeck/src/variance/solve.rs b/compiler/rustc_typeck/src/variance/solve.rs index 7402117a7eb..2d3369cba7a 100644 --- a/compiler/rustc_typeck/src/variance/solve.rs +++ b/compiler/rustc_typeck/src/variance/solve.rs @@ -107,7 +107,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> { self.enforce_const_invariance(generics, variances); // Functions are permitted to have unused generic parameters: make those invariant. - if let ty::FnDef(..) = tcx.type_of(def_id).kind { + if let ty::FnDef(..) = tcx.type_of(def_id).kind() { for variance in variances.iter_mut() { if *variance == ty::Bivariant { *variance = ty::Invariant; |
