Merge remote-tracking branch 'upstream/master' into box-alloc
This commit is contained in:
commit
06e4497a04
989 changed files with 13348 additions and 12044 deletions
2
.mailmap
2
.mailmap
|
@ -29,7 +29,6 @@ Ariel Ben-Yehuda <arielb1@mail.tau.ac.il> arielb1 <arielb1@mail.tau.ac.il>
|
|||
Austin Seipp <mad.one@gmail.com> <as@hacks.yi.org>
|
||||
Aydin Kim <ladinjin@hanmail.net> aydin.kim <aydin.kim@samsung.com>
|
||||
Barosl Lee <vcs@barosl.com> Barosl LEE <github@barosl.com>
|
||||
Bastian Kauschke <bastian_kauschke@hotmail.de>
|
||||
Ben Alpert <ben@benalpert.com> <spicyjalapeno@gmail.com>
|
||||
Ben Sago <ogham@users.noreply.github.com> Ben S <ogham@bsago.me>
|
||||
Ben Sago <ogham@users.noreply.github.com> Ben S <ogham@users.noreply.github.com>
|
||||
|
@ -161,6 +160,7 @@ Kyle J Strand <batmanaod@gmail.com> <kyle.j.strand@gmail.com>
|
|||
Kyle J Strand <batmanaod@gmail.com> <kyle.strand@pieinsurance.com>
|
||||
Kyle J Strand <batmanaod@gmail.com> <kyle.strand@rms.com>
|
||||
Laurențiu Nicola <lnicola@dend.ro>
|
||||
lcnr <bastian_kauschke@hotmail.de>
|
||||
Lee Jeffery <leejeffery@gmail.com> Lee Jeffery <lee@leejeffery.co.uk>
|
||||
Lee Wondong <wdlee91@gmail.com>
|
||||
Lennart Kudling <github@kudling.de>
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
Thank you for your interest in contributing to Rust!
|
||||
|
||||
To get started, read the [Getting Started] guide in the [rustc-dev-guide].
|
||||
To get started, read the [Contributing to Rust] chapter of the [rustc-dev-guide].
|
||||
|
||||
## Bug reports
|
||||
|
||||
Did a compiler error message tell you to come here? If you want to create an ICE report,
|
||||
refer to [this section][contributing-bug-reports] and [open an issue][issue template].
|
||||
|
||||
[Getting Started]: https://rustc-dev-guide.rust-lang.org/getting-started.html
|
||||
[Contributing to Rust]: https://rustc-dev-guide.rust-lang.org/contributing.html#contributing-to-rust
|
||||
[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
|
||||
[contributing-bug-reports]: https://rustc-dev-guide.rust-lang.org/contributing.html#bug-reports
|
||||
[issue template]: https://github.com/rust-lang/rust/issues/new/choose
|
||||
|
|
97
Cargo.lock
97
Cargo.lock
|
@ -310,7 +310,7 @@ dependencies = [
|
|||
"crypto-hash",
|
||||
"curl",
|
||||
"curl-sys",
|
||||
"env_logger 0.7.1",
|
||||
"env_logger 0.8.1",
|
||||
"filetime",
|
||||
"flate2",
|
||||
"fwdansi",
|
||||
|
@ -418,6 +418,17 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345"
|
||||
dependencies = [
|
||||
"semver 0.11.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargotest2"
|
||||
version = "0.1.0"
|
||||
|
@ -530,15 +541,14 @@ dependencies = [
|
|||
name = "clippy"
|
||||
version = "0.0.212"
|
||||
dependencies = [
|
||||
"cargo_metadata 0.11.1",
|
||||
"cargo_metadata 0.12.0",
|
||||
"clippy-mini-macro-test",
|
||||
"clippy_lints",
|
||||
"compiletest_rs",
|
||||
"derive-new",
|
||||
"lazy_static",
|
||||
"rustc-workspace-hack",
|
||||
"rustc_tools_util 0.2.0",
|
||||
"semver 0.10.0",
|
||||
"semver 0.11.0",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"tester",
|
||||
|
@ -552,14 +562,14 @@ version = "0.2.0"
|
|||
name = "clippy_lints"
|
||||
version = "0.0.212"
|
||||
dependencies = [
|
||||
"cargo_metadata 0.11.1",
|
||||
"cargo_metadata 0.12.0",
|
||||
"if_chain",
|
||||
"itertools 0.9.0",
|
||||
"pulldown-cmark 0.8.0",
|
||||
"quine-mc_cluskey",
|
||||
"quote",
|
||||
"regex-syntax",
|
||||
"semver 0.10.0",
|
||||
"semver 0.11.0",
|
||||
"serde",
|
||||
"smallvec 1.4.2",
|
||||
"syn",
|
||||
|
@ -626,9 +636,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.35"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3fcd8aba10d17504c87ef12d4f62ef404c6a4703d16682a9eb5543e6cf24455"
|
||||
checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"rustc-std-workspace-core",
|
||||
|
@ -1035,6 +1045,19 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime 2.0.1",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error_index_generator"
|
||||
version = "0.0.0"
|
||||
|
@ -1353,9 +1376,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.15"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
|
||||
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"libc",
|
||||
|
@ -1744,6 +1767,10 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "linkchecker"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
|
@ -1940,6 +1967,17 @@ dependencies = [
|
|||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "measureme"
|
||||
version = "9.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22bf8d885d073610aee20e7fa205c4341ed32a761dbde96da5fd96301a8d3e82"
|
||||
dependencies = [
|
||||
"parking_lot 0.11.0",
|
||||
"rustc-hash",
|
||||
"smallvec 1.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
|
@ -3072,7 +3110,7 @@ dependencies = [
|
|||
"indexmap",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"measureme",
|
||||
"measureme 0.7.1",
|
||||
"parking_lot 0.11.0",
|
||||
"rustc-ap-rustc_graphviz",
|
||||
"rustc-ap-rustc_index",
|
||||
|
@ -3272,9 +3310,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.16"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-core",
|
||||
|
@ -3474,7 +3512,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"measureme",
|
||||
"measureme 9.0.0",
|
||||
"rustc-demangle",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
|
@ -3540,7 +3578,7 @@ dependencies = [
|
|||
"indexmap",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"measureme",
|
||||
"measureme 9.0.0",
|
||||
"parking_lot 0.11.0",
|
||||
"rustc-hash",
|
||||
"rustc-rayon",
|
||||
|
@ -3845,7 +3883,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"chalk-ir",
|
||||
"measureme",
|
||||
"measureme 9.0.0",
|
||||
"polonius-engine",
|
||||
"rustc-rayon-core",
|
||||
"rustc_apfloat",
|
||||
|
@ -4356,7 +4394,7 @@ version = "0.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
"semver-parser 0.7.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -4366,7 +4404,17 @@ version = "0.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "394cec28fa623e00903caf7ba4fa6fb9a0e260280bb8cdbbba029611108a0190"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
"semver-parser 0.7.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
dependencies = [
|
||||
"semver-parser 0.10.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -4376,6 +4424,15 @@ version = "0.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428"
|
||||
dependencies = [
|
||||
"pest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.115"
|
||||
|
@ -4407,9 +4464,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.57"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
|
||||
checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
|
|
@ -9,8 +9,7 @@ standard library, and documentation.
|
|||
|
||||
**Note: this README is for _users_ rather than _contributors_.
|
||||
If you wish to _contribute_ to the compiler, you should read the
|
||||
[Getting Started][gettingstarted] of the rustc-dev-guide instead of this
|
||||
section.**
|
||||
[Getting Started][gettingstarted] section of the rustc-dev-guide instead.**
|
||||
|
||||
## Quick Start
|
||||
|
||||
|
|
|
@ -217,16 +217,18 @@ impl<T> TypedArena<T> {
|
|||
let mut chunks = self.chunks.borrow_mut();
|
||||
let mut new_cap;
|
||||
if let Some(last_chunk) = chunks.last_mut() {
|
||||
// If a type is `!needs_drop`, we don't need to keep track of how many elements
|
||||
// the chunk stores - the field will be ignored anyway.
|
||||
if mem::needs_drop::<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 len is less than HUGE_PAGE
|
||||
// bytes, then this chunk will be least double the previous
|
||||
// chunk's size.
|
||||
new_cap = last_chunk.storage.len();
|
||||
if new_cap < HUGE_PAGE / elem_size {
|
||||
new_cap = new_cap.checked_mul(2).unwrap();
|
||||
}
|
||||
new_cap = last_chunk.storage.len().min(HUGE_PAGE / elem_size / 2);
|
||||
new_cap = new_cap * 2;
|
||||
} else {
|
||||
new_cap = PAGE / elem_size;
|
||||
}
|
||||
|
@ -343,10 +345,8 @@ impl DroplessArena {
|
|||
// If the previous chunk's len is less than HUGE_PAGE
|
||||
// bytes, then this chunk will be least double the previous
|
||||
// chunk's size.
|
||||
new_cap = last_chunk.storage.len();
|
||||
if new_cap < HUGE_PAGE {
|
||||
new_cap = new_cap.checked_mul(2).unwrap();
|
||||
}
|
||||
new_cap = last_chunk.storage.len().min(HUGE_PAGE / 2);
|
||||
new_cap = new_cap * 2;
|
||||
} else {
|
||||
new_cap = PAGE;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ pub use UnsafeSource::*;
|
|||
|
||||
use crate::ptr::P;
|
||||
use crate::token::{self, CommentKind, DelimToken};
|
||||
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
|
||||
use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree};
|
||||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
|
@ -97,7 +97,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>,
|
||||
pub tokens: Option<LazyTokenStream>,
|
||||
}
|
||||
|
||||
impl PartialEq<Symbol> for Path {
|
||||
|
@ -535,7 +535,7 @@ pub struct Block {
|
|||
/// Distinguishes between `unsafe { ... }` and `{ ... }`.
|
||||
pub rules: BlockCheckMode,
|
||||
pub span: Span,
|
||||
pub tokens: Option<TokenStream>,
|
||||
pub tokens: Option<LazyTokenStream>,
|
||||
}
|
||||
|
||||
/// A match pattern.
|
||||
|
@ -546,7 +546,7 @@ pub struct Pat {
|
|||
pub id: NodeId,
|
||||
pub kind: PatKind,
|
||||
pub span: Span,
|
||||
pub tokens: Option<TokenStream>,
|
||||
pub tokens: Option<LazyTokenStream>,
|
||||
}
|
||||
|
||||
impl Pat {
|
||||
|
@ -892,7 +892,7 @@ pub struct Stmt {
|
|||
pub id: NodeId,
|
||||
pub kind: StmtKind,
|
||||
pub span: Span,
|
||||
pub tokens: Option<TokenStream>,
|
||||
pub tokens: Option<LazyTokenStream>,
|
||||
}
|
||||
|
||||
impl Stmt {
|
||||
|
@ -1040,7 +1040,7 @@ pub struct Expr {
|
|||
pub kind: ExprKind,
|
||||
pub span: Span,
|
||||
pub attrs: AttrVec,
|
||||
pub tokens: Option<TokenStream>,
|
||||
pub tokens: Option<LazyTokenStream>,
|
||||
}
|
||||
|
||||
// `Expr` is used a lot. Make sure it doesn't unintentionally get bigger.
|
||||
|
@ -1152,6 +1152,7 @@ impl Expr {
|
|||
match self.kind {
|
||||
ExprKind::Box(_) => ExprPrecedence::Box,
|
||||
ExprKind::Array(_) => ExprPrecedence::Array,
|
||||
ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock,
|
||||
ExprKind::Call(..) => ExprPrecedence::Call,
|
||||
ExprKind::MethodCall(..) => ExprPrecedence::MethodCall,
|
||||
ExprKind::Tup(_) => ExprPrecedence::Tup,
|
||||
|
@ -1207,6 +1208,8 @@ pub enum ExprKind {
|
|||
Box(P<Expr>),
|
||||
/// An array (`[a, b, c, d]`)
|
||||
Array(Vec<P<Expr>>),
|
||||
/// Allow anonymous constants from an inline `const` block
|
||||
ConstBlock(AnonConst),
|
||||
/// A function call
|
||||
///
|
||||
/// The first field resolves to the function itself,
|
||||
|
@ -1832,7 +1835,7 @@ pub struct Ty {
|
|||
pub id: NodeId,
|
||||
pub kind: TyKind,
|
||||
pub span: Span,
|
||||
pub tokens: Option<TokenStream>,
|
||||
pub tokens: Option<LazyTokenStream>,
|
||||
}
|
||||
|
||||
impl Clone for Ty {
|
||||
|
@ -2405,7 +2408,7 @@ impl<D: Decoder> rustc_serialize::Decodable<D> for AttrId {
|
|||
pub struct AttrItem {
|
||||
pub path: Path,
|
||||
pub args: MacArgs,
|
||||
pub tokens: Option<TokenStream>,
|
||||
pub tokens: Option<LazyTokenStream>,
|
||||
}
|
||||
|
||||
/// A list of attributes.
|
||||
|
@ -2420,6 +2423,7 @@ pub struct Attribute {
|
|||
/// or the construct this attribute is contained within (inner).
|
||||
pub style: AttrStyle,
|
||||
pub span: Span,
|
||||
pub tokens: Option<LazyTokenStream>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
|
@ -2479,7 +2483,7 @@ pub enum CrateSugar {
|
|||
pub struct Visibility {
|
||||
pub kind: VisibilityKind,
|
||||
pub span: Span,
|
||||
pub tokens: Option<TokenStream>,
|
||||
pub tokens: Option<LazyTokenStream>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
|
@ -2566,7 +2570,7 @@ pub struct Item<K = ItemKind> {
|
|||
///
|
||||
/// Note that the tokens here do not include the outer attributes, but will
|
||||
/// include inner attributes.
|
||||
pub tokens: Option<TokenStream>,
|
||||
pub tokens: Option<LazyTokenStream>,
|
||||
}
|
||||
|
||||
impl Item {
|
||||
|
|
|
@ -325,7 +325,7 @@ pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attri
|
|||
}
|
||||
|
||||
pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute {
|
||||
Attribute { kind: AttrKind::Normal(item), id: mk_attr_id(), style, span }
|
||||
Attribute { kind: AttrKind::Normal(item), id: mk_attr_id(), style, span, tokens: None }
|
||||
}
|
||||
|
||||
/// Returns an inner attribute with the given value and span.
|
||||
|
@ -344,7 +344,13 @@ pub fn mk_doc_comment(
|
|||
data: Symbol,
|
||||
span: Span,
|
||||
) -> Attribute {
|
||||
Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span }
|
||||
Attribute {
|
||||
kind: AttrKind::DocComment(comment_kind, data),
|
||||
id: mk_attr_id(),
|
||||
style,
|
||||
span,
|
||||
tokens: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
|
||||
|
@ -623,7 +629,8 @@ impl HasAttrs for StmtKind {
|
|||
match *self {
|
||||
StmtKind::Local(ref local) => local.attrs(),
|
||||
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
|
||||
StmtKind::Empty | StmtKind::Item(..) => &[],
|
||||
StmtKind::Item(ref item) => item.attrs(),
|
||||
StmtKind::Empty => &[],
|
||||
StmtKind::MacCall(ref mac) => mac.attrs.attrs(),
|
||||
}
|
||||
}
|
||||
|
@ -632,7 +639,8 @@ impl HasAttrs for StmtKind {
|
|||
match self {
|
||||
StmtKind::Local(local) => local.visit_attrs(f),
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
|
||||
StmtKind::Empty | StmtKind::Item(..) => {}
|
||||
StmtKind::Item(item) => item.visit_attrs(f),
|
||||
StmtKind::Empty => {}
|
||||
StmtKind::MacCall(mac) => {
|
||||
mac.attrs.visit_attrs(f);
|
||||
}
|
||||
|
|
|
@ -577,7 +577,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;
|
||||
let Attribute { kind, id: _, style: _, span, tokens: _ } = attr;
|
||||
match kind {
|
||||
AttrKind::Normal(AttrItem { path, args, tokens: _ }) => {
|
||||
vis.visit_path(path);
|
||||
|
@ -1106,6 +1106,9 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
|||
match kind {
|
||||
ExprKind::Box(expr) => vis.visit_expr(expr),
|
||||
ExprKind::Array(exprs) => visit_exprs(exprs, vis),
|
||||
ExprKind::ConstBlock(anon_const) => {
|
||||
vis.visit_anon_const(anon_const);
|
||||
}
|
||||
ExprKind::Repeat(expr, count) => {
|
||||
vis.visit_expr(expr);
|
||||
vis.visit_anon_const(count);
|
||||
|
|
|
@ -153,6 +153,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
|
|||
kw::Do,
|
||||
kw::Box,
|
||||
kw::Break,
|
||||
kw::Const,
|
||||
kw::Continue,
|
||||
kw::False,
|
||||
kw::For,
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
use crate::token::{self, DelimToken, Token, TokenKind};
|
||||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::sync::{self, Lrc};
|
||||
use rustc_macros::HashStable_Generic;
|
||||
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
|
@ -119,13 +120,84 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// A cloneable callback which produces a `TokenStream`. Each clone
|
||||
// of this should produce the same `TokenStream`
|
||||
pub trait CreateTokenStream: sync::Send + sync::Sync + FnOnce() -> TokenStream {
|
||||
// Workaround for the fact that `Clone` is not object-safe
|
||||
fn clone_it(&self) -> Box<dyn CreateTokenStream>;
|
||||
}
|
||||
|
||||
impl<F: 'static + Clone + sync::Send + sync::Sync + FnOnce() -> TokenStream> CreateTokenStream
|
||||
for F
|
||||
{
|
||||
fn clone_it(&self) -> Box<dyn CreateTokenStream> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Box<dyn CreateTokenStream> {
|
||||
fn clone(&self) -> Self {
|
||||
let val: &(dyn CreateTokenStream) = &**self;
|
||||
val.clone_it()
|
||||
}
|
||||
}
|
||||
|
||||
/// A lazy version of `TokenStream`, which may defer creation
|
||||
/// of an actual `TokenStream` until it is needed.
|
||||
pub type LazyTokenStream = Lrc<LazyTokenStreamInner>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum LazyTokenStreamInner {
|
||||
Lazy(Box<dyn CreateTokenStream>),
|
||||
Ready(TokenStream),
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for LazyTokenStreamInner {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
LazyTokenStreamInner::Lazy(..) => f.debug_struct("LazyTokenStream::Lazy").finish(),
|
||||
LazyTokenStreamInner::Ready(..) => f.debug_struct("LazyTokenStream::Ready").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LazyTokenStreamInner {
|
||||
pub fn into_token_stream(&self) -> TokenStream {
|
||||
match self {
|
||||
// Note that we do not cache this. If this ever becomes a performance
|
||||
// problem, we should investigate wrapping `LazyTokenStreamInner`
|
||||
// in a lock
|
||||
LazyTokenStreamInner::Lazy(cb) => (cb.clone())(),
|
||||
LazyTokenStreamInner::Ready(stream) => stream.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Encoder> Encodable<S> for LazyTokenStreamInner {
|
||||
fn encode(&self, _s: &mut S) -> Result<(), S::Error> {
|
||||
panic!("Attempted to encode LazyTokenStream");
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Decoder> Decodable<D> for LazyTokenStreamInner {
|
||||
fn decode(_d: &mut D) -> Result<Self, D::Error> {
|
||||
panic!("Attempted to decode LazyTokenStream");
|
||||
}
|
||||
}
|
||||
|
||||
impl<CTX> HashStable<CTX> for LazyTokenStreamInner {
|
||||
fn hash_stable(&self, _hcx: &mut CTX, _hasher: &mut StableHasher) {
|
||||
panic!("Attempted to compute stable hash for LazyTokenStream");
|
||||
}
|
||||
}
|
||||
|
||||
/// A `TokenStream` is an abstract sequence of tokens, organized into `TokenTree`s.
|
||||
///
|
||||
/// The goal is for procedural macros to work with `TokenStream`s and `TokenTree`s
|
||||
/// 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<TreeAndSpacing>>);
|
||||
pub struct TokenStream(pub(crate) Lrc<Vec<TreeAndSpacing>>);
|
||||
|
||||
pub type TreeAndSpacing = (TokenTree, Spacing);
|
||||
|
||||
|
@ -286,12 +358,12 @@ impl TokenStream {
|
|||
t1.next().is_none() && t2.next().is_none()
|
||||
}
|
||||
|
||||
pub fn map_enumerated<F: FnMut(usize, TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
|
||||
pub fn map_enumerated<F: FnMut(usize, &TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
|
||||
TokenStream(Lrc::new(
|
||||
self.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, (tree, is_joint))| (f(i, tree.clone()), *is_joint))
|
||||
.map(|(i, (tree, is_joint))| (f(i, tree), *is_joint))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
@ -394,8 +466,8 @@ impl Cursor {
|
|||
self.index = index;
|
||||
}
|
||||
|
||||
pub fn look_ahead(&self, n: usize) -> Option<TokenTree> {
|
||||
self.stream.0[self.index..].get(n).map(|(tree, _)| tree.clone())
|
||||
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
|
||||
self.stream.0[self.index..].get(n).map(|(tree, _)| tree)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ where
|
|||
T: Iterator<Item = &'a Symbol>,
|
||||
{
|
||||
let lookup = &lookup.as_str();
|
||||
let max_dist = dist.map_or_else(|| cmp::max(lookup.len(), 3) / 3, |d| d);
|
||||
let max_dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3);
|
||||
let name_vec: Vec<&Symbol> = iter_names.collect();
|
||||
|
||||
let (case_insensitive_match, levenshtein_match) = name_vec
|
||||
|
|
|
@ -282,6 +282,7 @@ pub enum ExprPrecedence {
|
|||
ForLoop,
|
||||
Loop,
|
||||
Match,
|
||||
ConstBlock,
|
||||
Block,
|
||||
TryBlock,
|
||||
Struct,
|
||||
|
@ -346,6 +347,7 @@ impl ExprPrecedence {
|
|||
ExprPrecedence::ForLoop |
|
||||
ExprPrecedence::Loop |
|
||||
ExprPrecedence::Match |
|
||||
ExprPrecedence::ConstBlock |
|
||||
ExprPrecedence::Block |
|
||||
ExprPrecedence::TryBlock |
|
||||
ExprPrecedence::Async |
|
||||
|
|
|
@ -200,11 +200,7 @@ pub trait Visitor<'ast>: Sized {
|
|||
walk_generic_args(self, path_span, generic_args)
|
||||
}
|
||||
fn visit_generic_arg(&mut self, generic_arg: &'ast GenericArg) {
|
||||
match generic_arg {
|
||||
GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
|
||||
GenericArg::Type(ty) => self.visit_ty(ty),
|
||||
GenericArg::Const(ct) => self.visit_anon_const(ct),
|
||||
}
|
||||
walk_generic_arg(self, generic_arg)
|
||||
}
|
||||
fn visit_assoc_ty_constraint(&mut self, constraint: &'ast AssocTyConstraint) {
|
||||
walk_assoc_ty_constraint(self, constraint)
|
||||
|
@ -486,6 +482,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn walk_generic_arg<'a, V>(visitor: &mut V, generic_arg: &'a GenericArg)
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
{
|
||||
match generic_arg {
|
||||
GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt),
|
||||
GenericArg::Type(ty) => visitor.visit_ty(ty),
|
||||
GenericArg::Const(ct) => visitor.visit_anon_const(ct),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>(
|
||||
visitor: &mut V,
|
||||
constraint: &'a AssocTyConstraint,
|
||||
|
@ -717,6 +724,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
|||
ExprKind::Array(ref subexpressions) => {
|
||||
walk_list!(visitor, visit_expr, subexpressions);
|
||||
}
|
||||
ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const),
|
||||
ExprKind::Repeat(ref element, ref count) => {
|
||||
visitor.visit_expr(element);
|
||||
visitor.visit_anon_const(count)
|
||||
|
|
|
@ -30,6 +30,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let kind = match e.kind {
|
||||
ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)),
|
||||
ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
|
||||
ExprKind::ConstBlock(ref anon_const) => {
|
||||
let anon_const = self.lower_anon_const(anon_const);
|
||||
hir::ExprKind::ConstBlock(anon_const)
|
||||
}
|
||||
ExprKind::Repeat(ref expr, ref count) => {
|
||||
let expr = self.lower_expr(expr);
|
||||
let count = self.lower_anon_const(count);
|
||||
|
@ -206,9 +210,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
ex.span = e.span;
|
||||
}
|
||||
// Merge attributes into the inner expression.
|
||||
let mut attrs = e.attrs.clone();
|
||||
let mut attrs: Vec<_> = e.attrs.iter().map(|a| self.lower_attr(a)).collect();
|
||||
attrs.extend::<Vec<_>>(ex.attrs.into());
|
||||
ex.attrs = attrs;
|
||||
ex.attrs = attrs.into();
|
||||
return ex;
|
||||
}
|
||||
|
||||
|
@ -1186,7 +1190,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
input| {
|
||||
match used_regs.entry(r) {
|
||||
Entry::Occupied(o) => {
|
||||
if !skip {
|
||||
if skip {
|
||||
return;
|
||||
}
|
||||
skip = true;
|
||||
|
||||
let idx2 = *o.get();
|
||||
|
@ -1203,14 +1209,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
reg2.name()
|
||||
);
|
||||
let mut err = sess.struct_span_err(op_sp, &msg);
|
||||
err.span_label(
|
||||
op_sp,
|
||||
&format!("register `{}`", reg.name()),
|
||||
);
|
||||
err.span_label(
|
||||
op_sp2,
|
||||
&format!("register `{}`", reg2.name()),
|
||||
);
|
||||
err.span_label(op_sp, &format!("register `{}`", reg.name()));
|
||||
err.span_label(op_sp2, &format!("register `{}`", reg2.name()));
|
||||
|
||||
match (op, op2) {
|
||||
(
|
||||
|
@ -1232,7 +1232,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(idx);
|
||||
}
|
||||
|
@ -1472,13 +1471,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::MatchSource::ForLoopDesugar,
|
||||
));
|
||||
|
||||
let attrs: Vec<_> = e.attrs.iter().map(|a| self.lower_attr(a)).collect();
|
||||
|
||||
// This is effectively `{ let _result = ...; _result }`.
|
||||
// The construct was introduced in #21984 and is necessary to make sure that
|
||||
// temporaries in the `head` expression are dropped and do not leak to the
|
||||
// surrounding scope of the `match` since the `match` is not a terminating scope.
|
||||
//
|
||||
// Also, add the attributes to the outer returned expr node.
|
||||
self.expr_drop_temps_mut(desugared_span, match_expr, e.attrs.clone())
|
||||
self.expr_drop_temps_mut(desugared_span, match_expr, attrs.into())
|
||||
}
|
||||
|
||||
/// Desugar `ExprKind::Try` from: `<expr>?` into:
|
||||
|
|
|
@ -538,6 +538,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
self.visit_fn_ret_ty(&f.decl.output)
|
||||
}
|
||||
TyKind::ImplTrait(def_node_id, _) => {
|
||||
self.lctx.allocate_hir_id_counter(def_node_id);
|
||||
self.with_hir_id_owner(Some(def_node_id), |this| {
|
||||
visit::walk_ty(this, t);
|
||||
});
|
||||
}
|
||||
_ => visit::walk_ty(self, t),
|
||||
}
|
||||
}
|
||||
|
@ -972,7 +978,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
|
||||
};
|
||||
|
||||
Attribute { kind, id: attr.id, style: attr.style, span: attr.span }
|
||||
// Tokens aren't needed after macro expansion and parsing
|
||||
Attribute { kind, id: attr.id, style: attr.style, span: attr.span, tokens: None }
|
||||
}
|
||||
|
||||
fn lower_mac_args(&mut self, args: &MacArgs) -> MacArgs {
|
||||
|
@ -1346,10 +1353,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// Add a definition for the in-band `Param`.
|
||||
let def_id = self.resolver.local_def_id(def_node_id);
|
||||
|
||||
let hir_bounds = self.lower_param_bounds(
|
||||
self.allocate_hir_id_counter(def_node_id);
|
||||
|
||||
let hir_bounds = self.with_hir_id_owner(def_node_id, |this| {
|
||||
this.lower_param_bounds(
|
||||
bounds,
|
||||
ImplTraitContext::Universal(in_band_ty_params),
|
||||
);
|
||||
)
|
||||
});
|
||||
// Set the name to `impl Bound1 + Bound2`.
|
||||
let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span);
|
||||
in_band_ty_params.push(hir::GenericParam {
|
||||
|
@ -1713,7 +1724,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
pat: self.lower_pat(&l.pat),
|
||||
init,
|
||||
span: l.span,
|
||||
attrs: l.attrs.clone(),
|
||||
attrs: l.attrs.iter().map(|a| self.lower_attr(a)).collect::<Vec<_>>().into(),
|
||||
source: hir::LocalSource::Normal,
|
||||
},
|
||||
ids,
|
||||
|
@ -2200,7 +2211,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
.attrs
|
||||
.iter()
|
||||
.filter(|attr| self.sess.check_name(attr, sym::rustc_synthetic))
|
||||
.map(|_| hir::SyntheticTyParamKind::ImplTrait)
|
||||
.map(|_| hir::SyntheticTyParamKind::FromAttr)
|
||||
.next(),
|
||||
};
|
||||
|
||||
|
|
|
@ -10,43 +10,46 @@ use rustc_span::symbol::Ident;
|
|||
use rustc_span::{source_map::Spanned, Span};
|
||||
|
||||
impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
crate fn lower_pat(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
|
||||
crate fn lower_pat(&mut self, mut pattern: &Pat) -> &'hir hir::Pat<'hir> {
|
||||
ensure_sufficient_stack(|| {
|
||||
let node = match p.kind {
|
||||
PatKind::Wild => hir::PatKind::Wild,
|
||||
// loop here to avoid recursion
|
||||
let node = loop {
|
||||
match pattern.kind {
|
||||
PatKind::Wild => break hir::PatKind::Wild,
|
||||
PatKind::Ident(ref binding_mode, ident, ref sub) => {
|
||||
let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s));
|
||||
let node = self.lower_pat_ident(p, binding_mode, ident, lower_sub);
|
||||
node
|
||||
break self.lower_pat_ident(pattern, binding_mode, ident, lower_sub);
|
||||
}
|
||||
PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)),
|
||||
PatKind::Lit(ref e) => break hir::PatKind::Lit(self.lower_expr(e)),
|
||||
PatKind::TupleStruct(ref path, ref pats) => {
|
||||
let qpath = self.lower_qpath(
|
||||
p.id,
|
||||
pattern.id,
|
||||
&None,
|
||||
path,
|
||||
ParamMode::Optional,
|
||||
ImplTraitContext::disallowed(),
|
||||
);
|
||||
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
|
||||
hir::PatKind::TupleStruct(qpath, pats, ddpos)
|
||||
break hir::PatKind::TupleStruct(qpath, pats, ddpos);
|
||||
}
|
||||
PatKind::Or(ref pats) => hir::PatKind::Or(
|
||||
PatKind::Or(ref pats) => {
|
||||
break hir::PatKind::Or(
|
||||
self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))),
|
||||
),
|
||||
);
|
||||
}
|
||||
PatKind::Path(ref qself, ref path) => {
|
||||
let qpath = self.lower_qpath(
|
||||
p.id,
|
||||
pattern.id,
|
||||
qself,
|
||||
path,
|
||||
ParamMode::Optional,
|
||||
ImplTraitContext::disallowed(),
|
||||
);
|
||||
hir::PatKind::Path(qpath)
|
||||
break hir::PatKind::Path(qpath);
|
||||
}
|
||||
PatKind::Struct(ref path, ref fields, etc) => {
|
||||
let qpath = self.lower_qpath(
|
||||
p.id,
|
||||
pattern.id,
|
||||
&None,
|
||||
path,
|
||||
ParamMode::Optional,
|
||||
|
@ -60,32 +63,37 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
is_shorthand: f.is_shorthand,
|
||||
span: f.span,
|
||||
}));
|
||||
hir::PatKind::Struct(qpath, fs, etc)
|
||||
break hir::PatKind::Struct(qpath, fs, etc);
|
||||
}
|
||||
PatKind::Tuple(ref pats) => {
|
||||
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
|
||||
hir::PatKind::Tuple(pats, ddpos)
|
||||
break hir::PatKind::Tuple(pats, ddpos);
|
||||
}
|
||||
PatKind::Box(ref inner) => {
|
||||
break hir::PatKind::Box(self.lower_pat(inner));
|
||||
}
|
||||
PatKind::Ref(ref inner, mutbl) => {
|
||||
break hir::PatKind::Ref(self.lower_pat(inner), mutbl);
|
||||
}
|
||||
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
|
||||
PatKind::Ref(ref inner, mutbl) => hir::PatKind::Ref(self.lower_pat(inner), mutbl),
|
||||
PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => {
|
||||
hir::PatKind::Range(
|
||||
break hir::PatKind::Range(
|
||||
e1.as_deref().map(|e| self.lower_expr(e)),
|
||||
e2.as_deref().map(|e| self.lower_expr(e)),
|
||||
self.lower_range_end(end, e2.is_some()),
|
||||
)
|
||||
);
|
||||
}
|
||||
PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
|
||||
PatKind::Slice(ref pats) => break self.lower_pat_slice(pats),
|
||||
PatKind::Rest => {
|
||||
// If we reach here the `..` pattern is not semantically allowed.
|
||||
self.ban_illegal_rest_pat(p.span)
|
||||
break self.ban_illegal_rest_pat(pattern.span);
|
||||
}
|
||||
// return inner to be processed in next loop
|
||||
PatKind::Paren(ref inner) => pattern = inner,
|
||||
PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
|
||||
}
|
||||
// FIXME: consider not using recursion to lower this.
|
||||
PatKind::Paren(ref inner) => return self.lower_pat(inner),
|
||||
PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", p.span),
|
||||
};
|
||||
|
||||
self.pat_with_node_id_of(p, node)
|
||||
self.pat_with_node_id_of(pattern, node)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -287,7 +287,7 @@ impl<'a> AstValidator<'a> {
|
|||
// ```
|
||||
fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) {
|
||||
match expr.kind {
|
||||
ExprKind::Lit(..) | ExprKind::Err => {}
|
||||
ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Err => {}
|
||||
ExprKind::Path(..) if allow_paths => {}
|
||||
ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
|
||||
_ => self.err_handler().span_err(
|
||||
|
|
|
@ -629,6 +629,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
|
|||
gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
|
||||
gate_all!(const_trait_impl, "const trait impls are experimental");
|
||||
gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
|
||||
gate_all!(inline_const, "inline-const is experimental");
|
||||
|
||||
// All uses of `gate_all!` below this point were added in #65742,
|
||||
// and subsequently disabled (with the non-early gating readded).
|
||||
|
|
|
@ -390,7 +390,7 @@ impl Printer {
|
|||
self.scan_stack.pop_front().unwrap()
|
||||
}
|
||||
|
||||
fn scan_top(&mut self) -> usize {
|
||||
fn scan_top(&self) -> usize {
|
||||
*self.scan_stack.front().unwrap()
|
||||
}
|
||||
|
||||
|
@ -484,7 +484,7 @@ impl Printer {
|
|||
self.pending_indentation += amount;
|
||||
}
|
||||
|
||||
fn get_top(&mut self) -> PrintStackElem {
|
||||
fn get_top(&self) -> PrintStackElem {
|
||||
*self.print_stack.last().unwrap_or({
|
||||
&PrintStackElem { offset: 0, pbreak: PrintStackBreak::Broken(Breaks::Inconsistent) }
|
||||
})
|
||||
|
|
|
@ -63,7 +63,7 @@ impl<'a> Comments<'a> {
|
|||
}
|
||||
|
||||
pub fn trailing_comment(
|
||||
&mut self,
|
||||
&self,
|
||||
span: rustc_span::Span,
|
||||
next_pos: Option<BytePos>,
|
||||
) -> Option<Comment> {
|
||||
|
@ -1714,6 +1714,14 @@ impl<'a> State<'a> {
|
|||
self.end();
|
||||
}
|
||||
|
||||
fn print_expr_anon_const(&mut self, expr: &ast::AnonConst, attrs: &[ast::Attribute]) {
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.s.word("const");
|
||||
self.print_inner_attributes_inline(attrs);
|
||||
self.print_expr(&expr.value);
|
||||
self.end();
|
||||
}
|
||||
|
||||
fn print_expr_repeat(
|
||||
&mut self,
|
||||
element: &ast::Expr,
|
||||
|
@ -1890,6 +1898,9 @@ impl<'a> State<'a> {
|
|||
ast::ExprKind::Array(ref exprs) => {
|
||||
self.print_expr_vec(&exprs[..], attrs);
|
||||
}
|
||||
ast::ExprKind::ConstBlock(ref anon_const) => {
|
||||
self.print_expr_anon_const(anon_const, attrs);
|
||||
}
|
||||
ast::ExprKind::Repeat(ref element, ref count) => {
|
||||
self.print_expr_repeat(element, count, attrs);
|
||||
}
|
||||
|
|
|
@ -1013,13 +1013,28 @@ pub fn allow_internal_unstable<'a>(
|
|||
sess: &'a Session,
|
||||
attrs: &'a [Attribute],
|
||||
) -> Option<impl Iterator<Item = Symbol> + 'a> {
|
||||
let attrs = sess.filter_by_name(attrs, sym::allow_internal_unstable);
|
||||
allow_unstable(sess, attrs, sym::allow_internal_unstable)
|
||||
}
|
||||
|
||||
pub fn rustc_allow_const_fn_unstable<'a>(
|
||||
sess: &'a Session,
|
||||
attrs: &'a [Attribute],
|
||||
) -> Option<impl Iterator<Item = Symbol> + 'a> {
|
||||
allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
|
||||
}
|
||||
|
||||
fn allow_unstable<'a>(
|
||||
sess: &'a Session,
|
||||
attrs: &'a [Attribute],
|
||||
symbol: Symbol,
|
||||
) -> Option<impl Iterator<Item = Symbol> + 'a> {
|
||||
let attrs = sess.filter_by_name(attrs, symbol);
|
||||
let list = attrs
|
||||
.filter_map(move |attr| {
|
||||
attr.meta_item_list().or_else(|| {
|
||||
sess.diagnostic().span_err(
|
||||
attr.span,
|
||||
"`allow_internal_unstable` expects a list of feature names",
|
||||
&format!("`{}` expects a list of feature names", symbol.to_ident_string()),
|
||||
);
|
||||
None
|
||||
})
|
||||
|
@ -1029,8 +1044,10 @@ pub fn allow_internal_unstable<'a>(
|
|||
Some(list.into_iter().filter_map(move |it| {
|
||||
let name = it.ident().map(|ident| ident.name);
|
||||
if name.is_none() {
|
||||
sess.diagnostic()
|
||||
.span_err(it.span(), "`allow_internal_unstable` expects feature names");
|
||||
sess.diagnostic().span_err(
|
||||
it.span(),
|
||||
&format!("`{}` expects feature names", symbol.to_ident_string()),
|
||||
);
|
||||
}
|
||||
name
|
||||
}))
|
||||
|
|
|
@ -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, tokens: _ } = match parser.parse_attr_item() {
|
||||
let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item(false) {
|
||||
Ok(ai) => ai,
|
||||
Err(mut err) => {
|
||||
err.emit();
|
||||
|
|
|
@ -11,11 +11,11 @@ doctest = false
|
|||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
libc = "0.2"
|
||||
measureme = "0.7.1"
|
||||
measureme = "9.0.0"
|
||||
snap = "1"
|
||||
tracing = "0.1"
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc-demangle = "0.1"
|
||||
rustc-demangle = "0.1.18"
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
|
|
|
@ -139,7 +139,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
unsafe { llvm::LLVMGetInsertBlock(self.llbuilder) }
|
||||
}
|
||||
|
||||
fn set_span(&self, _span: Span) {}
|
||||
fn set_span(&mut self, _span: Span) {}
|
||||
|
||||
fn position_at_end(&mut self, llbb: &'ll BasicBlock) {
|
||||
unsafe {
|
||||
|
|
|
@ -324,8 +324,8 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn coverage_context(&'a self) -> &'a coverageinfo::CrateCoverageContext<'tcx> {
|
||||
self.coverage_cx.as_ref().unwrap()
|
||||
pub fn coverage_context(&'a self) -> Option<&'a coverageinfo::CrateCoverageContext<'tcx>> {
|
||||
self.coverage_cx.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,10 @@ use tracing::debug;
|
|||
/// undocumented details in Clang's implementation (that may or may not be important) were also
|
||||
/// replicated for Rust's Coverage Map.
|
||||
pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
|
||||
let function_coverage_map = cx.coverage_context().take_function_coverage_map();
|
||||
let function_coverage_map = match cx.coverage_context() {
|
||||
Some(ctx) => ctx.take_function_coverage_map(),
|
||||
None => return,
|
||||
};
|
||||
if function_coverage_map.is_empty() {
|
||||
// This module has no functions with coverage instrumentation
|
||||
return;
|
||||
|
|
|
@ -64,17 +64,22 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
function_source_hash: u64,
|
||||
id: CounterValueReference,
|
||||
region: CodeRegion,
|
||||
) {
|
||||
) -> bool {
|
||||
if let Some(coverage_context) = self.coverage_context() {
|
||||
debug!(
|
||||
"adding counter to coverage_regions: instance={:?}, function_source_hash={}, id={:?}, \
|
||||
at {:?}",
|
||||
instance, function_source_hash, id, region,
|
||||
);
|
||||
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
|
||||
let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut();
|
||||
coverage_regions
|
||||
.entry(instance)
|
||||
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
|
||||
.add_counter(function_source_hash, id, region);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn add_counter_expression_region(
|
||||
|
@ -85,29 +90,39 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
op: Op,
|
||||
rhs: ExpressionOperandId,
|
||||
region: CodeRegion,
|
||||
) {
|
||||
) -> bool {
|
||||
if let Some(coverage_context) = self.coverage_context() {
|
||||
debug!(
|
||||
"adding counter expression to coverage_regions: instance={:?}, id={:?}, {:?} {:?} {:?}, \
|
||||
at {:?}",
|
||||
instance, id, lhs, op, rhs, region,
|
||||
);
|
||||
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
|
||||
let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut();
|
||||
coverage_regions
|
||||
.entry(instance)
|
||||
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
|
||||
.add_counter_expression(id, lhs, op, rhs, region);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion) {
|
||||
fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool {
|
||||
if let Some(coverage_context) = self.coverage_context() {
|
||||
debug!(
|
||||
"adding unreachable code to coverage_regions: instance={:?}, at {:?}",
|
||||
instance, region,
|
||||
);
|
||||
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
|
||||
let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut();
|
||||
coverage_regions
|
||||
.entry(instance)
|
||||
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
|
||||
.add_unreachable_region(region);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -334,8 +334,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
self.call(expect, &[cond, self.const_bool(expected)], None)
|
||||
}
|
||||
|
||||
fn sideeffect(&mut self) {
|
||||
if self.tcx.sess.opts.debugging_opts.insert_sideeffect {
|
||||
fn sideeffect(&mut self, unconditional: bool) {
|
||||
if unconditional || self.tcx.sess.opts.debugging_opts.insert_sideeffect {
|
||||
let fnname = self.get_intrinsic(&("llvm.sideeffect"));
|
||||
self.call(fnname, &[], None);
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ fn codegen_msvc_try(
|
|||
) {
|
||||
let llfn = get_rust_try_fn(bx, &mut |mut bx| {
|
||||
bx.set_personality_fn(bx.eh_personality());
|
||||
bx.sideeffect();
|
||||
bx.sideeffect(false);
|
||||
|
||||
let mut normal = bx.build_sibling_block("normal");
|
||||
let mut catchswitch = bx.build_sibling_block("catchswitch");
|
||||
|
@ -553,7 +553,7 @@ fn codegen_gnu_try(
|
|||
// call %catch_func(%data, %ptr)
|
||||
// ret 1
|
||||
|
||||
bx.sideeffect();
|
||||
bx.sideeffect(false);
|
||||
|
||||
let mut then = bx.build_sibling_block("then");
|
||||
let mut catch = bx.build_sibling_block("catch");
|
||||
|
@ -615,7 +615,7 @@ fn codegen_emcc_try(
|
|||
// call %catch_func(%data, %catch_data)
|
||||
// ret 1
|
||||
|
||||
bx.sideeffect();
|
||||
bx.sideeffect(false);
|
||||
|
||||
let mut then = bx.build_sibling_block("then");
|
||||
let mut catch = bx.build_sibling_block("catch");
|
||||
|
@ -673,17 +673,9 @@ fn codegen_emcc_try(
|
|||
fn gen_fn<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
name: &str,
|
||||
inputs: Vec<Ty<'tcx>>,
|
||||
output: Ty<'tcx>,
|
||||
rust_fn_sig: ty::PolyFnSig<'tcx>,
|
||||
codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>),
|
||||
) -> &'ll Value {
|
||||
let rust_fn_sig = ty::Binder::bind(cx.tcx.mk_fn_sig(
|
||||
inputs.into_iter(),
|
||||
output,
|
||||
false,
|
||||
hir::Unsafety::Unsafe,
|
||||
Abi::Rust,
|
||||
));
|
||||
let fn_abi = FnAbi::of_fn_ptr(cx, rust_fn_sig, &[]);
|
||||
let llfn = cx.declare_fn(name, &fn_abi);
|
||||
cx.set_frame_pointer_elimination(llfn);
|
||||
|
@ -710,22 +702,31 @@ fn get_rust_try_fn<'ll, 'tcx>(
|
|||
// Define the type up front for the signature of the rust_try function.
|
||||
let tcx = cx.tcx;
|
||||
let i8p = tcx.mk_mut_ptr(tcx.types.i8);
|
||||
let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig(
|
||||
// `unsafe fn(*mut i8) -> ()`
|
||||
let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
|
||||
iter::once(i8p),
|
||||
tcx.mk_unit(),
|
||||
false,
|
||||
hir::Unsafety::Unsafe,
|
||||
Abi::Rust,
|
||||
)));
|
||||
let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig(
|
||||
// `unsafe fn(*mut i8, *mut i8) -> ()`
|
||||
let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
|
||||
[i8p, i8p].iter().cloned(),
|
||||
tcx.mk_unit(),
|
||||
false,
|
||||
hir::Unsafety::Unsafe,
|
||||
Abi::Rust,
|
||||
)));
|
||||
let output = tcx.types.i32;
|
||||
let rust_try = gen_fn(cx, "__rust_try", vec![try_fn_ty, i8p, catch_fn_ty], output, codegen);
|
||||
// `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32`
|
||||
let rust_fn_sig = ty::Binder::dummy(cx.tcx.mk_fn_sig(
|
||||
vec![try_fn_ty, i8p, catch_fn_ty].into_iter(),
|
||||
tcx.types.i32,
|
||||
false,
|
||||
hir::Unsafety::Unsafe,
|
||||
Abi::Rust,
|
||||
));
|
||||
let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen);
|
||||
cx.rust_try_fn.set(Some(rust_try));
|
||||
rust_try
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
|
|||
target <= self.bb
|
||||
&& target.start_location().is_predecessor_of(self.bb.start_location(), mir)
|
||||
}) {
|
||||
bx.sideeffect();
|
||||
bx.sideeffect(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -964,7 +964,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
}
|
||||
|
||||
mir::TerminatorKind::Goto { target } => {
|
||||
if bb == target {
|
||||
// This is an unconditional branch back to this same basic
|
||||
// block. That means we have something like a `loop {}`
|
||||
// statement. Currently LLVM miscompiles this because it
|
||||
// assumes forward progress. We want to prevent this in all
|
||||
// cases, but that has a fairly high cost to compile times
|
||||
// currently. Instead, try to handle this specific case
|
||||
// which comes up commonly in practice (e.g., in embedded
|
||||
// code).
|
||||
//
|
||||
// The `true` here means we insert side effects regardless
|
||||
// of -Zinsert-sideeffect being passed on unconditional
|
||||
// branching to the same basic block.
|
||||
bx.sideeffect(true);
|
||||
} else {
|
||||
helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
|
||||
}
|
||||
helper.funclet_br(self, &mut bx, target);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let Coverage { kind, code_region } = coverage;
|
||||
match kind {
|
||||
CoverageKind::Counter { function_source_hash, id } => {
|
||||
bx.add_counter_region(self.instance, function_source_hash, id, code_region);
|
||||
|
||||
if bx.add_counter_region(self.instance, function_source_hash, id, code_region) {
|
||||
let coverageinfo = bx.tcx().coverageinfo(self.instance.def_id());
|
||||
|
||||
let fn_name = bx.create_pgo_func_name_var(self.instance);
|
||||
|
@ -24,6 +23,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
);
|
||||
bx.instrprof_increment(fn_name, hash, num_counters, id);
|
||||
}
|
||||
}
|
||||
CoverageKind::Expression { id, lhs, op, rhs } => {
|
||||
bx.add_counter_expression_region(self.instance, id, lhs, op, rhs, code_region);
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
bx.set_personality_fn(cx.eh_personality());
|
||||
}
|
||||
|
||||
bx.sideeffect();
|
||||
bx.sideeffect(false);
|
||||
|
||||
let cleanup_kinds = analyze::cleanup_kinds(&mir);
|
||||
// Allocate a `Block` for every basic block, except
|
||||
|
|
|
@ -45,7 +45,7 @@ pub trait BuilderMethods<'a, 'tcx>:
|
|||
fn build_sibling_block(&self, name: &str) -> Self;
|
||||
fn cx(&self) -> &Self::CodegenCx;
|
||||
fn llbb(&self) -> Self::BasicBlock;
|
||||
fn set_span(&self, span: Span);
|
||||
fn set_span(&mut self, span: Span);
|
||||
|
||||
fn position_at_end(&mut self, llbb: Self::BasicBlock);
|
||||
fn ret_void(&mut self);
|
||||
|
|
|
@ -9,14 +9,18 @@ pub trait CoverageInfoMethods: BackendTypes {
|
|||
pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
|
||||
fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value;
|
||||
|
||||
/// Returns true if the counter was added to the coverage map; false if `-Z instrument-coverage`
|
||||
/// is not enabled (a coverage map is not being generated).
|
||||
fn add_counter_region(
|
||||
&mut self,
|
||||
instance: Instance<'tcx>,
|
||||
function_source_hash: u64,
|
||||
id: CounterValueReference,
|
||||
region: CodeRegion,
|
||||
);
|
||||
) -> bool;
|
||||
|
||||
/// Returns true if the expression was added to the coverage map; false if
|
||||
/// `-Z instrument-coverage` is not enabled (a coverage map is not being generated).
|
||||
fn add_counter_expression_region(
|
||||
&mut self,
|
||||
instance: Instance<'tcx>,
|
||||
|
@ -25,7 +29,9 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
|
|||
op: Op,
|
||||
rhs: ExpressionOperandId,
|
||||
region: CodeRegion,
|
||||
);
|
||||
) -> bool;
|
||||
|
||||
fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion);
|
||||
/// Returns true if the region was added to the coverage map; false if `-Z instrument-coverage`
|
||||
/// is not enabled (a coverage map is not being generated).
|
||||
fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,9 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes {
|
|||
fn abort(&mut self);
|
||||
fn assume(&mut self, val: Self::Value);
|
||||
fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value;
|
||||
fn sideeffect(&mut self);
|
||||
/// Normally, sideeffect is only emitted if -Zinsert-sideeffect is passed;
|
||||
/// in some cases though we want to emit it regardless.
|
||||
fn sideeffect(&mut self, unconditional: bool);
|
||||
/// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in
|
||||
/// Rust defined C-variadic functions.
|
||||
fn va_start(&mut self, val: Self::Value) -> Self::Value;
|
||||
|
|
|
@ -25,7 +25,7 @@ rustc-hash = "1.1.0"
|
|||
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
|
||||
rustc_index = { path = "../rustc_index", package = "rustc_index" }
|
||||
bitflags = "1.2.1"
|
||||
measureme = "0.7.1"
|
||||
measureme = "9.0.0"
|
||||
libc = "0.2"
|
||||
stacker = "0.1.12"
|
||||
tempfile = "3.0.5"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::{DirectedGraph, WithNumNodes, WithStartNode, WithSuccessors};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
@ -86,10 +87,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Allows searches to terminate early with a value.
|
||||
// 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.
|
||||
///
|
||||
/// See the documentation of `TriColorDepthFirstSearch` to see how a node's status is updated
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#![feature(const_panic)]
|
||||
#![feature(min_const_generics)]
|
||||
#![feature(once_cell)]
|
||||
#![feature(maybe_uninit_uninit_array)]
|
||||
#![allow(rustc::default_hash_types)]
|
||||
|
||||
#[macro_use]
|
||||
|
|
|
@ -149,8 +149,8 @@ pub struct ObligationForest<O: ForestObligation> {
|
|||
/// comments in `process_obligation` for details.
|
||||
active_cache: FxHashMap<O::CacheKey, usize>,
|
||||
|
||||
/// A vector reused in compress(), to avoid allocating new vectors.
|
||||
node_rewrites: Vec<usize>,
|
||||
/// A vector reused in compress() and find_cycles_from_node(), to avoid allocating new vectors.
|
||||
reused_node_vec: Vec<usize>,
|
||||
|
||||
obligation_tree_id_generator: ObligationTreeIdGenerator,
|
||||
|
||||
|
@ -251,12 +251,22 @@ enum NodeState {
|
|||
Error,
|
||||
}
|
||||
|
||||
/// This trait allows us to have two different Outcome types:
|
||||
/// - the normal one that does as little as possible
|
||||
/// - one for tests that does some additional work and checking
|
||||
pub trait OutcomeTrait {
|
||||
type Error;
|
||||
type Obligation;
|
||||
|
||||
fn new() -> Self;
|
||||
fn mark_not_stalled(&mut self);
|
||||
fn is_stalled(&self) -> bool;
|
||||
fn record_completed(&mut self, outcome: &Self::Obligation);
|
||||
fn record_error(&mut self, error: Self::Error);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Outcome<O, E> {
|
||||
/// Obligations that were completely evaluated, including all
|
||||
/// (transitive) subobligations. Only computed if requested.
|
||||
pub completed: Option<Vec<O>>,
|
||||
|
||||
/// Backtrace of obligations that were found to be in error.
|
||||
pub errors: Vec<Error<O, E>>,
|
||||
|
||||
|
@ -269,12 +279,29 @@ pub struct Outcome<O, E> {
|
|||
pub stalled: bool,
|
||||
}
|
||||
|
||||
/// Should `process_obligations` compute the `Outcome::completed` field of its
|
||||
/// result?
|
||||
#[derive(PartialEq)]
|
||||
pub enum DoCompleted {
|
||||
No,
|
||||
Yes,
|
||||
impl<O, E> OutcomeTrait for Outcome<O, E> {
|
||||
type Error = Error<O, E>;
|
||||
type Obligation = O;
|
||||
|
||||
fn new() -> Self {
|
||||
Self { stalled: true, errors: vec![] }
|
||||
}
|
||||
|
||||
fn mark_not_stalled(&mut self) {
|
||||
self.stalled = false;
|
||||
}
|
||||
|
||||
fn is_stalled(&self) -> bool {
|
||||
self.stalled
|
||||
}
|
||||
|
||||
fn record_completed(&mut self, _outcome: &Self::Obligation) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
fn record_error(&mut self, error: Self::Error) {
|
||||
self.errors.push(error)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -289,7 +316,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
nodes: vec![],
|
||||
done_cache: Default::default(),
|
||||
active_cache: Default::default(),
|
||||
node_rewrites: vec![],
|
||||
reused_node_vec: vec![],
|
||||
obligation_tree_id_generator: (0..).map(ObligationTreeId),
|
||||
error_cache: Default::default(),
|
||||
}
|
||||
|
@ -363,8 +390,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
.map(|(index, _node)| Error { error: error.clone(), backtrace: self.error_at(index) })
|
||||
.collect();
|
||||
|
||||
let successful_obligations = self.compress(DoCompleted::Yes);
|
||||
assert!(successful_obligations.unwrap().is_empty());
|
||||
self.compress(|_| assert!(false));
|
||||
errors
|
||||
}
|
||||
|
||||
|
@ -392,16 +418,12 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
/// be called in a loop until `outcome.stalled` is false.
|
||||
///
|
||||
/// This _cannot_ be unrolled (presently, at least).
|
||||
pub fn process_obligations<P>(
|
||||
&mut self,
|
||||
processor: &mut P,
|
||||
do_completed: DoCompleted,
|
||||
) -> Outcome<O, P::Error>
|
||||
pub fn process_obligations<P, OUT>(&mut self, processor: &mut P) -> OUT
|
||||
where
|
||||
P: ObligationProcessor<Obligation = O>,
|
||||
OUT: OutcomeTrait<Obligation = O, Error = Error<O, P::Error>>,
|
||||
{
|
||||
let mut errors = vec![];
|
||||
let mut stalled = true;
|
||||
let mut outcome = OUT::new();
|
||||
|
||||
// Note that the loop body can append new nodes, and those new nodes
|
||||
// will then be processed by subsequent iterations of the loop.
|
||||
|
@ -429,7 +451,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
}
|
||||
ProcessResult::Changed(children) => {
|
||||
// We are not (yet) stalled.
|
||||
stalled = false;
|
||||
outcome.mark_not_stalled();
|
||||
node.state.set(NodeState::Success);
|
||||
|
||||
for child in children {
|
||||
|
@ -442,28 +464,22 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
}
|
||||
}
|
||||
ProcessResult::Error(err) => {
|
||||
stalled = false;
|
||||
errors.push(Error { error: err, backtrace: self.error_at(index) });
|
||||
outcome.mark_not_stalled();
|
||||
outcome.record_error(Error { error: err, backtrace: self.error_at(index) });
|
||||
}
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if stalled {
|
||||
// There's no need to perform marking, cycle processing and compression when nothing
|
||||
// changed.
|
||||
return Outcome {
|
||||
completed: if do_completed == DoCompleted::Yes { Some(vec![]) } else { None },
|
||||
errors,
|
||||
stalled,
|
||||
};
|
||||
}
|
||||
|
||||
if !outcome.is_stalled() {
|
||||
self.mark_successes();
|
||||
self.process_cycles(processor);
|
||||
let completed = self.compress(do_completed);
|
||||
self.compress(|obl| outcome.record_completed(obl));
|
||||
}
|
||||
|
||||
Outcome { completed, errors, stalled }
|
||||
outcome
|
||||
}
|
||||
|
||||
/// Returns a vector of obligations for `p` and all of its
|
||||
|
@ -526,7 +542,6 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
let node = &self.nodes[index];
|
||||
let state = node.state.get();
|
||||
if state == NodeState::Success {
|
||||
node.state.set(NodeState::Waiting);
|
||||
// This call site is cold.
|
||||
self.uninlined_mark_dependents_as_waiting(node);
|
||||
} else {
|
||||
|
@ -538,17 +553,18 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
// This never-inlined function is for the cold call site.
|
||||
#[inline(never)]
|
||||
fn uninlined_mark_dependents_as_waiting(&self, node: &Node<O>) {
|
||||
// Mark node Waiting in the cold uninlined code instead of the hot inlined
|
||||
node.state.set(NodeState::Waiting);
|
||||
self.inlined_mark_dependents_as_waiting(node)
|
||||
}
|
||||
|
||||
/// Report cycles between all `Success` nodes, and convert all `Success`
|
||||
/// nodes to `Done`. This must be called after `mark_successes`.
|
||||
fn process_cycles<P>(&self, processor: &mut P)
|
||||
fn process_cycles<P>(&mut self, processor: &mut P)
|
||||
where
|
||||
P: ObligationProcessor<Obligation = O>,
|
||||
{
|
||||
let mut stack = vec![];
|
||||
|
||||
let mut stack = std::mem::take(&mut self.reused_node_vec);
|
||||
for (index, node) in self.nodes.iter().enumerate() {
|
||||
// For some benchmarks this state test is extremely hot. It's a win
|
||||
// to handle the no-op cases immediately to avoid the cost of the
|
||||
|
@ -559,6 +575,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
}
|
||||
|
||||
debug_assert!(stack.is_empty());
|
||||
self.reused_node_vec = stack;
|
||||
}
|
||||
|
||||
fn find_cycles_from_node<P>(&self, stack: &mut Vec<usize>, processor: &mut P, index: usize)
|
||||
|
@ -591,13 +608,12 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
/// indices and hence invalidates any outstanding indices. `process_cycles`
|
||||
/// must be run beforehand to remove any cycles on `Success` nodes.
|
||||
#[inline(never)]
|
||||
fn compress(&mut self, do_completed: DoCompleted) -> Option<Vec<O>> {
|
||||
fn compress(&mut self, mut outcome_cb: impl FnMut(&O)) {
|
||||
let orig_nodes_len = self.nodes.len();
|
||||
let mut node_rewrites: Vec<_> = std::mem::take(&mut self.node_rewrites);
|
||||
let mut node_rewrites: Vec<_> = std::mem::take(&mut self.reused_node_vec);
|
||||
debug_assert!(node_rewrites.is_empty());
|
||||
node_rewrites.extend(0..orig_nodes_len);
|
||||
let mut dead_nodes = 0;
|
||||
let mut removed_done_obligations: Vec<O> = vec![];
|
||||
|
||||
// Move removable nodes to the end, preserving the order of the
|
||||
// remaining nodes.
|
||||
|
@ -627,10 +643,8 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
} else {
|
||||
self.done_cache.insert(node.obligation.as_cache_key().clone());
|
||||
}
|
||||
if do_completed == DoCompleted::Yes {
|
||||
// Extract the success stories.
|
||||
removed_done_obligations.push(node.obligation.clone());
|
||||
}
|
||||
outcome_cb(&node.obligation);
|
||||
node_rewrites[index] = orig_nodes_len;
|
||||
dead_nodes += 1;
|
||||
}
|
||||
|
@ -654,9 +668,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
|||
}
|
||||
|
||||
node_rewrites.truncate(0);
|
||||
self.node_rewrites = node_rewrites;
|
||||
|
||||
if do_completed == DoCompleted::Yes { Some(removed_done_obligations) } else { None }
|
||||
self.reused_node_vec = node_rewrites;
|
||||
}
|
||||
|
||||
fn apply_rewrites(&mut self, node_rewrites: &[usize]) {
|
||||
|
|
|
@ -17,6 +17,40 @@ struct ClosureObligationProcessor<OF, BF, O, E> {
|
|||
marker: PhantomData<(O, E)>,
|
||||
}
|
||||
|
||||
struct TestOutcome<O, E> {
|
||||
pub completed: Vec<O>,
|
||||
pub errors: Vec<Error<O, E>>,
|
||||
pub stalled: bool,
|
||||
}
|
||||
|
||||
impl<O, E> OutcomeTrait for TestOutcome<O, E>
|
||||
where
|
||||
O: Clone,
|
||||
{
|
||||
type Error = Error<O, E>;
|
||||
type Obligation = O;
|
||||
|
||||
fn new() -> Self {
|
||||
Self { errors: vec![], stalled: false, completed: vec![] }
|
||||
}
|
||||
|
||||
fn mark_not_stalled(&mut self) {
|
||||
self.stalled = false;
|
||||
}
|
||||
|
||||
fn is_stalled(&self) -> bool {
|
||||
self.stalled
|
||||
}
|
||||
|
||||
fn record_completed(&mut self, outcome: &Self::Obligation) {
|
||||
self.completed.push(outcome.clone())
|
||||
}
|
||||
|
||||
fn record_error(&mut self, error: Self::Error) {
|
||||
self.errors.push(error)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn C<OF, BF, O>(of: OF, bf: BF) -> ClosureObligationProcessor<OF, BF, O, &'static str>
|
||||
where
|
||||
|
@ -65,8 +99,7 @@ fn push_pop() {
|
|||
// A |-> A.1
|
||||
// |-> A.2
|
||||
// |-> A.3
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
|
||||
"B" => ProcessResult::Error("B is for broken"),
|
||||
|
@ -75,10 +108,8 @@ fn push_pop() {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap(), vec!["C"]);
|
||||
));
|
||||
assert_eq!(ok, vec!["C"]);
|
||||
assert_eq!(err, vec![Error { error: "B is for broken", backtrace: vec!["B"] }]);
|
||||
|
||||
// second round: two delays, one success, creating an uneven set of subtasks:
|
||||
|
@ -88,8 +119,7 @@ fn push_pop() {
|
|||
// D |-> D.1
|
||||
// |-> D.2
|
||||
forest.register_obligation("D");
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.1" => ProcessResult::Unchanged,
|
||||
"A.2" => ProcessResult::Unchanged,
|
||||
|
@ -99,18 +129,15 @@ fn push_pop() {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap(), Vec::<&'static str>::new());
|
||||
));
|
||||
assert_eq!(ok, Vec::<&'static str>::new());
|
||||
assert_eq!(err, Vec::new());
|
||||
|
||||
// third round: ok in A.1 but trigger an error in A.2. Check that it
|
||||
// propagates to A, but not D.1 or D.2.
|
||||
// D |-> D.1 |-> D.1.i
|
||||
// |-> D.2 |-> D.2.i
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.1" => ProcessResult::Changed(vec![]),
|
||||
"A.2" => ProcessResult::Error("A is for apple"),
|
||||
|
@ -121,27 +148,22 @@ fn push_pop() {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
let mut ok = ok.unwrap();
|
||||
));
|
||||
let mut ok = ok;
|
||||
ok.sort();
|
||||
assert_eq!(ok, vec!["A.1", "A.3", "A.3.i"]);
|
||||
assert_eq!(err, vec![Error { error: "A is for apple", backtrace: vec!["A.2", "A"] }]);
|
||||
|
||||
// fourth round: error in D.1.i
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"D.1.i" => ProcessResult::Error("D is for dumb"),
|
||||
"D.2.i" => ProcessResult::Changed(vec![]),
|
||||
_ => panic!("unexpected obligation {:?}", obligation),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
let mut ok = ok.unwrap();
|
||||
));
|
||||
let mut ok = ok;
|
||||
ok.sort();
|
||||
assert_eq!(ok, vec!["D.2", "D.2.i"]);
|
||||
assert_eq!(err, vec![Error { error: "D is for dumb", backtrace: vec!["D.1.i", "D.1", "D"] }]);
|
||||
|
@ -160,8 +182,7 @@ fn success_in_grandchildren() {
|
|||
let mut forest = ObligationForest::new();
|
||||
forest.register_obligation("A");
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
|
||||
"A.1" => ProcessResult::Changed(vec![]),
|
||||
|
@ -171,61 +192,50 @@ fn success_in_grandchildren() {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
let mut ok = ok.unwrap();
|
||||
));
|
||||
let mut ok = ok;
|
||||
ok.sort();
|
||||
assert_eq!(ok, vec!["A.1", "A.3"]);
|
||||
assert!(err.is_empty());
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.2.i" => ProcessResult::Unchanged,
|
||||
"A.2.ii" => ProcessResult::Changed(vec![]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap(), vec!["A.2.ii"]);
|
||||
));
|
||||
assert_eq!(ok, vec!["A.2.ii"]);
|
||||
assert!(err.is_empty());
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]),
|
||||
"A.2.i.a" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert!(ok.unwrap().is_empty());
|
||||
));
|
||||
assert!(ok.is_empty());
|
||||
assert!(err.is_empty());
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.2.i.a" => ProcessResult::Changed(vec![]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
let mut ok = ok.unwrap();
|
||||
));
|
||||
let mut ok = ok;
|
||||
ok.sort();
|
||||
assert_eq!(ok, vec!["A", "A.2", "A.2.i", "A.2.i.a"]);
|
||||
assert!(err.is_empty());
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } =
|
||||
forest.process_obligations(&mut C(|_| unreachable!(), |_| {}), DoCompleted::Yes);
|
||||
let TestOutcome { completed: ok, errors: err, .. } =
|
||||
forest.process_obligations(&mut C(|_| unreachable!(), |_| {}));
|
||||
|
||||
assert!(ok.unwrap().is_empty());
|
||||
assert!(ok.is_empty());
|
||||
assert!(err.is_empty());
|
||||
}
|
||||
|
||||
|
@ -235,18 +245,15 @@ fn to_errors_no_throw() {
|
|||
// yields to correct errors (and does not panic, in particular).
|
||||
let mut forest = ObligationForest::new();
|
||||
forest.register_obligation("A");
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]),
|
||||
"A.1" | "A.2" | "A.3" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
));
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(err.len(), 0);
|
||||
let errors = forest.to_errors(());
|
||||
assert_eq!(errors[0].backtrace, vec!["A.1", "A"]);
|
||||
|
@ -260,22 +267,18 @@ fn diamond() {
|
|||
// check that diamond dependencies are handled correctly
|
||||
let mut forest = ObligationForest::new();
|
||||
forest.register_obligation("A");
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Changed(vec!["A.1", "A.2"]),
|
||||
"A.1" | "A.2" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
));
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(err.len(), 0);
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A.1" => ProcessResult::Changed(vec!["D"]),
|
||||
"A.2" => ProcessResult::Changed(vec!["D"]),
|
||||
|
@ -283,15 +286,12 @@ fn diamond() {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
));
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(err.len(), 0);
|
||||
|
||||
let mut d_count = 0;
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"D" => {
|
||||
d_count += 1;
|
||||
|
@ -300,11 +300,9 @@ fn diamond() {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
));
|
||||
assert_eq!(d_count, 1);
|
||||
let mut ok = ok.unwrap();
|
||||
let mut ok = ok;
|
||||
ok.sort();
|
||||
assert_eq!(ok, vec!["A", "A.1", "A.2", "D"]);
|
||||
assert_eq!(err.len(), 0);
|
||||
|
@ -313,22 +311,18 @@ fn diamond() {
|
|||
assert_eq!(errors.len(), 0);
|
||||
|
||||
forest.register_obligation("A'");
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]),
|
||||
"A'.1" | "A'.2" => ProcessResult::Unchanged,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
));
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(err.len(), 0);
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A'.1" => ProcessResult::Changed(vec!["D'", "A'"]),
|
||||
"A'.2" => ProcessResult::Changed(vec!["D'"]),
|
||||
|
@ -336,15 +330,12 @@ fn diamond() {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
));
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(err.len(), 0);
|
||||
|
||||
let mut d_count = 0;
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"D'" => {
|
||||
d_count += 1;
|
||||
|
@ -353,11 +344,9 @@ fn diamond() {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
));
|
||||
assert_eq!(d_count, 1);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(
|
||||
err,
|
||||
vec![super::Error { error: "operation failed", backtrace: vec!["D'", "A'.1", "A'"] }]
|
||||
|
@ -375,35 +364,27 @@ fn done_dependency() {
|
|||
forest.register_obligation("B: Sized");
|
||||
forest.register_obligation("C: Sized");
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
let mut ok = ok.unwrap();
|
||||
));
|
||||
let mut ok = ok;
|
||||
ok.sort();
|
||||
assert_eq!(ok, vec!["A: Sized", "B: Sized", "C: Sized"]);
|
||||
assert_eq!(err.len(), 0);
|
||||
|
||||
forest.register_obligation("(A,B,C): Sized");
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"(A,B,C): Sized" => {
|
||||
ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"])
|
||||
}
|
||||
"(A,B,C): Sized" => ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap(), vec!["(A,B,C): Sized"]);
|
||||
));
|
||||
assert_eq!(ok, vec!["(A,B,C): Sized"]);
|
||||
assert_eq!(err.len(), 0);
|
||||
}
|
||||
|
||||
|
@ -416,8 +397,7 @@ fn orphan() {
|
|||
forest.register_obligation("C1");
|
||||
forest.register_obligation("C2");
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Changed(vec!["D", "E"]),
|
||||
"B" => ProcessResult::Unchanged,
|
||||
|
@ -427,53 +407,42 @@ fn orphan() {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
let mut ok = ok.unwrap();
|
||||
));
|
||||
let mut ok = ok;
|
||||
ok.sort();
|
||||
assert_eq!(ok, vec!["C1", "C2"]);
|
||||
assert_eq!(err.len(), 0);
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"D" | "E" => ProcessResult::Unchanged,
|
||||
"B" => ProcessResult::Changed(vec!["D"]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
));
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(err.len(), 0);
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"D" => ProcessResult::Unchanged,
|
||||
"E" => ProcessResult::Error("E is for error"),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
));
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(err, vec![super::Error { error: "E is for error", backtrace: vec!["E", "A"] }]);
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"D" => ProcessResult::Error("D is dead"),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
));
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(err, vec![super::Error { error: "D is dead", backtrace: vec!["D"] }]);
|
||||
|
||||
let errors = forest.to_errors(());
|
||||
|
@ -487,35 +456,29 @@ fn simultaneous_register_and_error() {
|
|||
forest.register_obligation("A");
|
||||
forest.register_obligation("B");
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Error("An error"),
|
||||
"B" => ProcessResult::Changed(vec!["A"]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
));
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(err, vec![super::Error { error: "An error", backtrace: vec!["A"] }]);
|
||||
|
||||
let mut forest = ObligationForest::new();
|
||||
forest.register_obligation("B");
|
||||
forest.register_obligation("A");
|
||||
|
||||
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(
|
||||
&mut C(
|
||||
let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C(
|
||||
|obligation| match *obligation {
|
||||
"A" => ProcessResult::Error("An error"),
|
||||
"B" => ProcessResult::Changed(vec!["A"]),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|_| {},
|
||||
),
|
||||
DoCompleted::Yes,
|
||||
);
|
||||
assert_eq!(ok.unwrap().len(), 0);
|
||||
));
|
||||
assert_eq!(ok.len(), 0);
|
||||
assert_eq!(err, vec![super::Error { error: "An error", backtrace: vec!["A"] }]);
|
||||
}
|
||||
|
|
|
@ -94,34 +94,9 @@ use std::process;
|
|||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use measureme::{EventId, EventIdBuilder, SerializableString, StringId};
|
||||
use measureme::{EventId, EventIdBuilder, Profiler, SerializableString, StringId};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(any(windows, target_os = "wasi"))] {
|
||||
/// FileSerializationSink is faster on Windows
|
||||
type SerializationSink = measureme::FileSerializationSink;
|
||||
} else if #[cfg(target_arch = "wasm32")] {
|
||||
type SerializationSink = measureme::ByteVecSink;
|
||||
} else {
|
||||
/// MmapSerializatioSink is faster on macOS and Linux
|
||||
type SerializationSink = measureme::MmapSerializationSink;
|
||||
}
|
||||
}
|
||||
|
||||
type Profiler = measureme::Profiler<SerializationSink>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub enum ProfileCategory {
|
||||
Parsing,
|
||||
Expansion,
|
||||
TypeChecking,
|
||||
BorrowChecking,
|
||||
Codegen,
|
||||
Linking,
|
||||
Other,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
struct EventFilter: u32 {
|
||||
const GENERIC_ACTIVITIES = 1 << 0;
|
||||
|
@ -400,7 +375,7 @@ impl SelfProfiler {
|
|||
output_directory: &Path,
|
||||
crate_name: Option<&str>,
|
||||
event_filters: &Option<Vec<String>>,
|
||||
) -> Result<SelfProfiler, Box<dyn Error>> {
|
||||
) -> Result<SelfProfiler, Box<dyn Error + Send + Sync>> {
|
||||
fs::create_dir_all(output_directory)?;
|
||||
|
||||
let crate_name = crate_name.unwrap_or("unknown-crate");
|
||||
|
@ -511,13 +486,13 @@ impl SelfProfiler {
|
|||
self.event_filter_mask.contains(EventFilter::QUERY_KEYS)
|
||||
}
|
||||
|
||||
pub fn event_id_builder(&self) -> EventIdBuilder<'_, SerializationSink> {
|
||||
pub fn event_id_builder(&self) -> EventIdBuilder<'_> {
|
||||
EventIdBuilder::new(&self.profiler)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct TimingGuard<'a>(Option<measureme::TimingGuard<'a, SerializationSink>>);
|
||||
pub struct TimingGuard<'a>(Option<measureme::TimingGuard<'a>>);
|
||||
|
||||
impl<'a> TimingGuard<'a> {
|
||||
#[inline]
|
||||
|
|
|
@ -1,21 +1,53 @@
|
|||
//! This is a copy of `core::hash::sip` adapted to providing 128 bit hashes.
|
||||
|
||||
use std::cmp;
|
||||
use std::hash::Hasher;
|
||||
use std::mem;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
use std::ptr;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// The SipHash algorithm operates on 8-byte chunks.
|
||||
const ELEM_SIZE: usize = mem::size_of::<u64>();
|
||||
|
||||
// Size of the buffer in number of elements, not including the spill.
|
||||
//
|
||||
// The selection of this size was guided by rustc-perf benchmark comparisons of
|
||||
// different buffer sizes. It should be periodically reevaluated as the compiler
|
||||
// implementation and input characteristics change.
|
||||
//
|
||||
// Using the same-sized buffer for everything we hash is a performance versus
|
||||
// complexity tradeoff. The ideal buffer size, and whether buffering should even
|
||||
// be used, depends on what is being hashed. It may be worth it to size the
|
||||
// buffer appropriately (perhaps by making SipHasher128 generic over the buffer
|
||||
// size) or disable buffering depending on what is being hashed. But at this
|
||||
// time, we use the same buffer size for everything.
|
||||
const BUFFER_CAPACITY: usize = 8;
|
||||
|
||||
// Size of the buffer in bytes, not including the spill.
|
||||
const BUFFER_SIZE: usize = BUFFER_CAPACITY * ELEM_SIZE;
|
||||
|
||||
// Size of the buffer in number of elements, including the spill.
|
||||
const BUFFER_WITH_SPILL_CAPACITY: usize = BUFFER_CAPACITY + 1;
|
||||
|
||||
// Size of the buffer in bytes, including the spill.
|
||||
const BUFFER_WITH_SPILL_SIZE: usize = BUFFER_WITH_SPILL_CAPACITY * ELEM_SIZE;
|
||||
|
||||
// Index of the spill element in the buffer.
|
||||
const BUFFER_SPILL_INDEX: usize = BUFFER_WITH_SPILL_CAPACITY - 1;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct SipHasher128 {
|
||||
k0: u64,
|
||||
k1: u64,
|
||||
length: usize, // how many bytes we've processed
|
||||
// The access pattern during hashing consists of accesses to `nbuf` and
|
||||
// `buf` until the buffer is full, followed by accesses to `state` and
|
||||
// `processed`, and then repetition of that pattern until hashing is done.
|
||||
// This is the basis for the ordering of fields below. However, in practice
|
||||
// the cache miss-rate for data access is extremely low regardless of order.
|
||||
nbuf: usize, // how many bytes in buf are valid
|
||||
buf: [MaybeUninit<u64>; BUFFER_WITH_SPILL_CAPACITY], // unprocessed bytes le
|
||||
state: State, // hash State
|
||||
tail: u64, // unprocessed bytes le
|
||||
ntail: usize, // how many bytes in tail are valid
|
||||
processed: usize, // how many bytes we've processed
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -51,178 +83,328 @@ macro_rules! compress {
|
|||
}};
|
||||
}
|
||||
|
||||
/// Loads an integer of the desired type from a byte stream, in LE order. Uses
|
||||
/// `copy_nonoverlapping` to let the compiler generate the most efficient way
|
||||
/// to load it from a possibly unaligned address.
|
||||
///
|
||||
/// Unsafe because: unchecked indexing at i..i+size_of(int_ty)
|
||||
macro_rules! load_int_le {
|
||||
($buf:expr, $i:expr, $int_ty:ident) => {{
|
||||
debug_assert!($i + mem::size_of::<$int_ty>() <= $buf.len());
|
||||
let mut data = 0 as $int_ty;
|
||||
ptr::copy_nonoverlapping(
|
||||
$buf.get_unchecked($i),
|
||||
&mut data as *mut _ as *mut u8,
|
||||
mem::size_of::<$int_ty>(),
|
||||
);
|
||||
data.to_le()
|
||||
}};
|
||||
}
|
||||
|
||||
/// Loads a u64 using up to 7 bytes of a byte slice. It looks clumsy but the
|
||||
/// `copy_nonoverlapping` calls that occur (via `load_int_le!`) all have fixed
|
||||
/// sizes and avoid calling `memcpy`, which is good for speed.
|
||||
///
|
||||
/// Unsafe because: unchecked indexing at start..start+len
|
||||
// Copies up to 8 bytes from source to destination. This performs better than
|
||||
// `ptr::copy_nonoverlapping` on microbenchmarks and may perform better on real
|
||||
// workloads since all of the copies have fixed sizes and avoid calling memcpy.
|
||||
//
|
||||
// This is specifically designed for copies of up to 8 bytes, because that's the
|
||||
// maximum of number bytes needed to fill an 8-byte-sized element on which
|
||||
// SipHash operates. Note that for variable-sized copies which are known to be
|
||||
// less than 8 bytes, this function will perform more work than necessary unless
|
||||
// the compiler is able to optimize the extra work away.
|
||||
#[inline]
|
||||
unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 {
|
||||
debug_assert!(len < 8);
|
||||
let mut i = 0; // current byte index (from LSB) in the output u64
|
||||
let mut out = 0;
|
||||
if i + 3 < len {
|
||||
out = load_int_le!(buf, start + i, u32) as u64;
|
||||
i += 4;
|
||||
}
|
||||
if i + 1 < len {
|
||||
out |= (load_int_le!(buf, start + i, u16) as u64) << (i * 8);
|
||||
i += 2
|
||||
}
|
||||
if i < len {
|
||||
out |= (*buf.get_unchecked(start + i) as u64) << (i * 8);
|
||||
i += 1;
|
||||
}
|
||||
debug_assert_eq!(i, len);
|
||||
out
|
||||
}
|
||||
unsafe fn copy_nonoverlapping_small(src: *const u8, dst: *mut u8, count: usize) {
|
||||
debug_assert!(count <= 8);
|
||||
|
||||
impl SipHasher128 {
|
||||
#[inline]
|
||||
pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher128 {
|
||||
let mut state = SipHasher128 {
|
||||
k0: key0,
|
||||
k1: key1,
|
||||
length: 0,
|
||||
state: State { v0: 0, v1: 0, v2: 0, v3: 0 },
|
||||
tail: 0,
|
||||
ntail: 0,
|
||||
};
|
||||
state.reset();
|
||||
state
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reset(&mut self) {
|
||||
self.length = 0;
|
||||
self.state.v0 = self.k0 ^ 0x736f6d6570736575;
|
||||
self.state.v1 = self.k1 ^ 0x646f72616e646f6d;
|
||||
self.state.v2 = self.k0 ^ 0x6c7967656e657261;
|
||||
self.state.v3 = self.k1 ^ 0x7465646279746573;
|
||||
self.ntail = 0;
|
||||
|
||||
// This is only done in the 128 bit version:
|
||||
self.state.v1 ^= 0xee;
|
||||
}
|
||||
|
||||
// A specialized write function for values with size <= 8.
|
||||
//
|
||||
// The input must be zero-extended to 64-bits by the caller. This extension
|
||||
// isn't hashed, but the implementation requires it for correctness.
|
||||
//
|
||||
// This function, given the same integer size and value, has the same effect
|
||||
// on both little- and big-endian hardware. It operates on values without
|
||||
// depending on their sequence in memory, so is independent of endianness.
|
||||
//
|
||||
// However, we want SipHasher128 to be platform-dependent, in order to be
|
||||
// consistent with the platform-dependent SipHasher in libstd. In other
|
||||
// words, we want:
|
||||
//
|
||||
// - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])`
|
||||
// - big-endian: `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])`
|
||||
//
|
||||
// Therefore, in order to produce endian-dependent results, SipHasher128's
|
||||
// `write_xxx` Hasher trait methods byte-swap `x` prior to zero-extending.
|
||||
//
|
||||
// If clients of SipHasher128 itself want platform-independent results, they
|
||||
// *also* must byte-swap integer inputs before invoking the `write_xxx`
|
||||
// methods on big-endian hardware (that is, two byte-swaps must occur--one
|
||||
// in the client, and one in SipHasher128). Additionally, they must extend
|
||||
// `usize` and `isize` types to 64 bits on 32-bit systems.
|
||||
#[inline]
|
||||
fn short_write<T>(&mut self, _x: T, x: u64) {
|
||||
let size = mem::size_of::<T>();
|
||||
self.length += size;
|
||||
|
||||
// The original number must be zero-extended, not sign-extended.
|
||||
debug_assert!(if size < 8 { x >> (8 * size) == 0 } else { true });
|
||||
|
||||
// The number of bytes needed to fill `self.tail`.
|
||||
let needed = 8 - self.ntail;
|
||||
|
||||
// SipHash parses the input stream as 8-byte little-endian integers.
|
||||
// Inputs are put into `self.tail` until 8 bytes of data have been
|
||||
// collected, and then that word is processed.
|
||||
//
|
||||
// For example, imagine that `self.tail` is 0x0000_00EE_DDCC_BBAA,
|
||||
// `self.ntail` is 5 (because 5 bytes have been put into `self.tail`),
|
||||
// and `needed` is therefore 3.
|
||||
//
|
||||
// - Scenario 1, `self.write_u8(0xFF)`: we have already zero-extended
|
||||
// the input to 0x0000_0000_0000_00FF. We now left-shift it five
|
||||
// bytes, giving 0x0000_FF00_0000_0000. We then bitwise-OR that value
|
||||
// into `self.tail`, resulting in 0x0000_FFEE_DDCC_BBAA.
|
||||
// (Zero-extension of the original input is critical in this scenario
|
||||
// because we don't want the high two bytes of `self.tail` to be
|
||||
// touched by the bitwise-OR.) `self.tail` is not yet full, so we
|
||||
// return early, after updating `self.ntail` to 6.
|
||||
//
|
||||
// - Scenario 2, `self.write_u32(0xIIHH_GGFF)`: we have already
|
||||
// zero-extended the input to 0x0000_0000_IIHH_GGFF. We now
|
||||
// left-shift it five bytes, giving 0xHHGG_FF00_0000_0000. We then
|
||||
// bitwise-OR that value into `self.tail`, resulting in
|
||||
// 0xHHGG_FFEE_DDCC_BBAA. `self.tail` is now full, and we can use it
|
||||
// to update `self.state`. (As mentioned above, this assumes a
|
||||
// little-endian machine; on a big-endian machine we would have
|
||||
// byte-swapped 0xIIHH_GGFF in the caller, giving 0xFFGG_HHII, and we
|
||||
// would then end up bitwise-ORing 0xGGHH_II00_0000_0000 into
|
||||
// `self.tail`).
|
||||
//
|
||||
self.tail |= x << (8 * self.ntail);
|
||||
if size < needed {
|
||||
self.ntail += size;
|
||||
if count == 8 {
|
||||
ptr::copy_nonoverlapping(src, dst, 8);
|
||||
return;
|
||||
}
|
||||
|
||||
// `self.tail` is full, process it.
|
||||
self.state.v3 ^= self.tail;
|
||||
Sip24Rounds::c_rounds(&mut self.state);
|
||||
self.state.v0 ^= self.tail;
|
||||
let mut i = 0;
|
||||
if i + 3 < count {
|
||||
ptr::copy_nonoverlapping(src.add(i), dst.add(i), 4);
|
||||
i += 4;
|
||||
}
|
||||
|
||||
// Continuing scenario 2: we have one byte left over from the input. We
|
||||
// set `self.ntail` to 1 and `self.tail` to `0x0000_0000_IIHH_GGFF >>
|
||||
// 8*3`, which is 0x0000_0000_0000_00II. (Or on a big-endian machine
|
||||
// the prior byte-swapping would leave us with 0x0000_0000_0000_00FF.)
|
||||
if i + 1 < count {
|
||||
ptr::copy_nonoverlapping(src.add(i), dst.add(i), 2);
|
||||
i += 2
|
||||
}
|
||||
|
||||
if i < count {
|
||||
*dst.add(i) = *src.add(i);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
debug_assert_eq!(i, count);
|
||||
}
|
||||
|
||||
// # Implementation
|
||||
//
|
||||
// This implementation uses buffering to reduce the hashing cost for inputs
|
||||
// consisting of many small integers. Buffering simplifies the integration of
|
||||
// integer input--the integer write function typically just appends to the
|
||||
// buffer with a statically sized write, updates metadata, and returns.
|
||||
//
|
||||
// Buffering also prevents alternating between writes that do and do not trigger
|
||||
// the hashing process. Only when the entire buffer is full do we transition
|
||||
// into hashing. This allows us to keep the hash state in registers for longer,
|
||||
// instead of loading and storing it before and after processing each element.
|
||||
//
|
||||
// When a write fills the buffer, a buffer processing function is invoked to
|
||||
// hash all of the buffered input. The buffer processing functions are marked
|
||||
// `#[inline(never)]` so that they aren't inlined into the append functions,
|
||||
// which ensures the more frequently called append functions remain inlineable
|
||||
// and don't include register pushing/popping that would only be made necessary
|
||||
// by inclusion of the complex buffer processing path which uses those
|
||||
// registers.
|
||||
//
|
||||
// The buffer includes a "spill"--an extra element at the end--which simplifies
|
||||
// the integer write buffer processing path. The value that fills the buffer can
|
||||
// be written with a statically sized write that may spill over into the spill.
|
||||
// After the buffer is processed, the part of the value that spilled over can be
|
||||
// written from the spill to the beginning of the buffer with another statically
|
||||
// sized write. This write may copy more bytes than actually spilled over, but
|
||||
// we maintain the metadata such that any extra copied bytes will be ignored by
|
||||
// subsequent processing. Due to the static sizes, this scheme performs better
|
||||
// than copying the exact number of bytes needed into the end and beginning of
|
||||
// the buffer.
|
||||
//
|
||||
// The buffer is uninitialized, which improves performance, but may preclude
|
||||
// efficient implementation of alternative approaches. The improvement is not so
|
||||
// large that an alternative approach should be disregarded because it cannot be
|
||||
// efficiently implemented with an uninitialized buffer. On the other hand, an
|
||||
// uninitialized buffer may become more important should a larger one be used.
|
||||
//
|
||||
// # Platform Dependence
|
||||
//
|
||||
// The SipHash algorithm operates on byte sequences. It parses the input stream
|
||||
// as 8-byte little-endian integers. Therefore, given the same byte sequence, it
|
||||
// produces the same result on big- and little-endian hardware.
|
||||
//
|
||||
// However, the Hasher trait has methods which operate on multi-byte integers.
|
||||
// How they are converted into byte sequences can be endian-dependent (by using
|
||||
// native byte order) or independent (by consistently using either LE or BE byte
|
||||
// order). It can also be `isize` and `usize` size dependent (by using the
|
||||
// native size), or independent (by converting to a common size), supposing the
|
||||
// values can be represented in 32 bits.
|
||||
//
|
||||
// In order to make `SipHasher128` consistent with `SipHasher` in libstd, we
|
||||
// choose to do the integer to byte sequence conversion in the platform-
|
||||
// dependent way. Clients can achieve platform-independent hashing by widening
|
||||
// `isize` and `usize` integers to 64 bits on 32-bit systems and byte-swapping
|
||||
// integers on big-endian systems before passing them to the writing functions.
|
||||
// This causes the input byte sequence to look identical on big- and little-
|
||||
// endian systems (supposing `isize` and `usize` values can be represented in 32
|
||||
// bits), which ensures platform-independent results.
|
||||
impl SipHasher128 {
|
||||
#[inline]
|
||||
pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher128 {
|
||||
let mut hasher = SipHasher128 {
|
||||
nbuf: 0,
|
||||
buf: MaybeUninit::uninit_array(),
|
||||
state: State {
|
||||
v0: key0 ^ 0x736f6d6570736575,
|
||||
// The XOR with 0xee is only done on 128-bit algorithm version.
|
||||
v1: key1 ^ (0x646f72616e646f6d ^ 0xee),
|
||||
v2: key0 ^ 0x6c7967656e657261,
|
||||
v3: key1 ^ 0x7465646279746573,
|
||||
},
|
||||
processed: 0,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
// Initialize spill because we read from it in `short_write_process_buffer`.
|
||||
*hasher.buf.get_unchecked_mut(BUFFER_SPILL_INDEX) = MaybeUninit::zeroed();
|
||||
}
|
||||
|
||||
hasher
|
||||
}
|
||||
|
||||
// A specialized write function for values with size <= 8.
|
||||
#[inline]
|
||||
fn short_write<T>(&mut self, x: T) {
|
||||
let size = mem::size_of::<T>();
|
||||
let nbuf = self.nbuf;
|
||||
debug_assert!(size <= 8);
|
||||
debug_assert!(nbuf < BUFFER_SIZE);
|
||||
debug_assert!(nbuf + size < BUFFER_WITH_SPILL_SIZE);
|
||||
|
||||
if nbuf + size < BUFFER_SIZE {
|
||||
unsafe {
|
||||
// The memcpy call is optimized away because the size is known.
|
||||
let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
|
||||
ptr::copy_nonoverlapping(&x as *const _ as *const u8, dst, size);
|
||||
}
|
||||
|
||||
self.nbuf = nbuf + size;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe { self.short_write_process_buffer(x) }
|
||||
}
|
||||
|
||||
// A specialized write function for values with size <= 8 that should only
|
||||
// be called when the write would cause the buffer to fill.
|
||||
//
|
||||
// The `if` is needed to avoid shifting by 64 bits, which Rust
|
||||
// complains about.
|
||||
self.ntail = size - needed;
|
||||
self.tail = if needed < 8 { x >> (8 * needed) } else { 0 };
|
||||
// SAFETY: the write of `x` into `self.buf` starting at byte offset
|
||||
// `self.nbuf` must cause `self.buf` to become fully initialized (and not
|
||||
// overflow) if it wasn't already.
|
||||
#[inline(never)]
|
||||
unsafe fn short_write_process_buffer<T>(&mut self, x: T) {
|
||||
let size = mem::size_of::<T>();
|
||||
let nbuf = self.nbuf;
|
||||
debug_assert!(size <= 8);
|
||||
debug_assert!(nbuf < BUFFER_SIZE);
|
||||
debug_assert!(nbuf + size >= BUFFER_SIZE);
|
||||
debug_assert!(nbuf + size < BUFFER_WITH_SPILL_SIZE);
|
||||
|
||||
// Copy first part of input into end of buffer, possibly into spill
|
||||
// element. The memcpy call is optimized away because the size is known.
|
||||
let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
|
||||
ptr::copy_nonoverlapping(&x as *const _ as *const u8, dst, size);
|
||||
|
||||
// Process buffer.
|
||||
for i in 0..BUFFER_CAPACITY {
|
||||
let elem = self.buf.get_unchecked(i).assume_init().to_le();
|
||||
self.state.v3 ^= elem;
|
||||
Sip24Rounds::c_rounds(&mut self.state);
|
||||
self.state.v0 ^= elem;
|
||||
}
|
||||
|
||||
// Copy remaining input into start of buffer by copying size - 1
|
||||
// elements from spill (at most size - 1 bytes could have overflowed
|
||||
// into the spill). The memcpy call is optimized away because the size
|
||||
// is known. And the whole copy is optimized away for size == 1.
|
||||
let src = self.buf.get_unchecked(BUFFER_SPILL_INDEX) as *const _ as *const u8;
|
||||
ptr::copy_nonoverlapping(src, self.buf.as_mut_ptr() as *mut u8, size - 1);
|
||||
|
||||
// This function should only be called when the write fills the buffer.
|
||||
// Therefore, when size == 1, the new `self.nbuf` must be zero. The size
|
||||
// is statically known, so the branch is optimized away.
|
||||
self.nbuf = if size == 1 { 0 } else { nbuf + size - BUFFER_SIZE };
|
||||
self.processed += BUFFER_SIZE;
|
||||
}
|
||||
|
||||
// A write function for byte slices.
|
||||
#[inline]
|
||||
fn slice_write(&mut self, msg: &[u8]) {
|
||||
let length = msg.len();
|
||||
let nbuf = self.nbuf;
|
||||
debug_assert!(nbuf < BUFFER_SIZE);
|
||||
|
||||
if nbuf + length < BUFFER_SIZE {
|
||||
unsafe {
|
||||
let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
|
||||
|
||||
if length <= 8 {
|
||||
copy_nonoverlapping_small(msg.as_ptr(), dst, length);
|
||||
} else {
|
||||
// This memcpy is *not* optimized away.
|
||||
ptr::copy_nonoverlapping(msg.as_ptr(), dst, length);
|
||||
}
|
||||
}
|
||||
|
||||
self.nbuf = nbuf + length;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe { self.slice_write_process_buffer(msg) }
|
||||
}
|
||||
|
||||
// A write function for byte slices that should only be called when the
|
||||
// write would cause the buffer to fill.
|
||||
//
|
||||
// SAFETY: `self.buf` must be initialized up to the byte offset `self.nbuf`,
|
||||
// and `msg` must contain enough bytes to initialize the rest of the element
|
||||
// containing the byte offset `self.nbuf`.
|
||||
#[inline(never)]
|
||||
unsafe fn slice_write_process_buffer(&mut self, msg: &[u8]) {
|
||||
let length = msg.len();
|
||||
let nbuf = self.nbuf;
|
||||
debug_assert!(nbuf < BUFFER_SIZE);
|
||||
debug_assert!(nbuf + length >= BUFFER_SIZE);
|
||||
|
||||
// Always copy first part of input into current element of buffer.
|
||||
// This function should only be called when the write fills the buffer,
|
||||
// so we know that there is enough input to fill the current element.
|
||||
let valid_in_elem = nbuf % ELEM_SIZE;
|
||||
let needed_in_elem = ELEM_SIZE - valid_in_elem;
|
||||
|
||||
let src = msg.as_ptr();
|
||||
let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
|
||||
copy_nonoverlapping_small(src, dst, needed_in_elem);
|
||||
|
||||
// Process buffer.
|
||||
|
||||
// Using `nbuf / ELEM_SIZE + 1` rather than `(nbuf + needed_in_elem) /
|
||||
// ELEM_SIZE` to show the compiler that this loop's upper bound is > 0.
|
||||
// We know that is true, because last step ensured we have a full
|
||||
// element in the buffer.
|
||||
let last = nbuf / ELEM_SIZE + 1;
|
||||
|
||||
for i in 0..last {
|
||||
let elem = self.buf.get_unchecked(i).assume_init().to_le();
|
||||
self.state.v3 ^= elem;
|
||||
Sip24Rounds::c_rounds(&mut self.state);
|
||||
self.state.v0 ^= elem;
|
||||
}
|
||||
|
||||
// Process the remaining element-sized chunks of input.
|
||||
let mut processed = needed_in_elem;
|
||||
let input_left = length - processed;
|
||||
let elems_left = input_left / ELEM_SIZE;
|
||||
let extra_bytes_left = input_left % ELEM_SIZE;
|
||||
|
||||
for _ in 0..elems_left {
|
||||
let elem = (msg.as_ptr().add(processed) as *const u64).read_unaligned().to_le();
|
||||
self.state.v3 ^= elem;
|
||||
Sip24Rounds::c_rounds(&mut self.state);
|
||||
self.state.v0 ^= elem;
|
||||
processed += ELEM_SIZE;
|
||||
}
|
||||
|
||||
// Copy remaining input into start of buffer.
|
||||
let src = msg.as_ptr().add(processed);
|
||||
let dst = self.buf.as_mut_ptr() as *mut u8;
|
||||
copy_nonoverlapping_small(src, dst, extra_bytes_left);
|
||||
|
||||
self.nbuf = extra_bytes_left;
|
||||
self.processed += nbuf + processed;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn finish128(mut self) -> (u64, u64) {
|
||||
let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail;
|
||||
debug_assert!(self.nbuf < BUFFER_SIZE);
|
||||
|
||||
self.state.v3 ^= b;
|
||||
Sip24Rounds::c_rounds(&mut self.state);
|
||||
self.state.v0 ^= b;
|
||||
// Process full elements in buffer.
|
||||
let last = self.nbuf / ELEM_SIZE;
|
||||
|
||||
self.state.v2 ^= 0xee;
|
||||
Sip24Rounds::d_rounds(&mut self.state);
|
||||
let _0 = self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3;
|
||||
// Since we're consuming self, avoid updating members for a potential
|
||||
// performance gain.
|
||||
let mut state = self.state;
|
||||
|
||||
for i in 0..last {
|
||||
let elem = unsafe { self.buf.get_unchecked(i).assume_init().to_le() };
|
||||
state.v3 ^= elem;
|
||||
Sip24Rounds::c_rounds(&mut state);
|
||||
state.v0 ^= elem;
|
||||
}
|
||||
|
||||
// Get remaining partial element.
|
||||
let elem = if self.nbuf % ELEM_SIZE != 0 {
|
||||
unsafe {
|
||||
// Ensure element is initialized by writing zero bytes. At most
|
||||
// `ELEM_SIZE - 1` are required given the above check. It's safe
|
||||
// to write this many because we have the spill and we maintain
|
||||
// `self.nbuf` such that this write will start before the spill.
|
||||
let dst = (self.buf.as_mut_ptr() as *mut u8).add(self.nbuf);
|
||||
ptr::write_bytes(dst, 0, ELEM_SIZE - 1);
|
||||
self.buf.get_unchecked(last).assume_init().to_le()
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// Finalize the hash.
|
||||
let length = self.processed + self.nbuf;
|
||||
let b: u64 = ((length as u64 & 0xff) << 56) | elem;
|
||||
|
||||
state.v3 ^= b;
|
||||
Sip24Rounds::c_rounds(&mut state);
|
||||
state.v0 ^= b;
|
||||
|
||||
state.v2 ^= 0xee;
|
||||
Sip24Rounds::d_rounds(&mut state);
|
||||
let _0 = state.v0 ^ state.v1 ^ state.v2 ^ state.v3;
|
||||
|
||||
state.v1 ^= 0xdd;
|
||||
Sip24Rounds::d_rounds(&mut state);
|
||||
let _1 = state.v0 ^ state.v1 ^ state.v2 ^ state.v3;
|
||||
|
||||
self.state.v1 ^= 0xdd;
|
||||
Sip24Rounds::d_rounds(&mut self.state);
|
||||
let _1 = self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3;
|
||||
(_0, _1)
|
||||
}
|
||||
}
|
||||
|
@ -230,92 +412,57 @@ impl SipHasher128 {
|
|||
impl Hasher for SipHasher128 {
|
||||
#[inline]
|
||||
fn write_u8(&mut self, i: u8) {
|
||||
self.short_write(i, i as u64);
|
||||
self.short_write(i);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u16(&mut self, i: u16) {
|
||||
self.short_write(i, i.to_le() as u64);
|
||||
self.short_write(i);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u32(&mut self, i: u32) {
|
||||
self.short_write(i, i.to_le() as u64);
|
||||
self.short_write(i);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u64(&mut self, i: u64) {
|
||||
self.short_write(i, i.to_le() as u64);
|
||||
self.short_write(i);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_usize(&mut self, i: usize) {
|
||||
self.short_write(i, i.to_le() as u64);
|
||||
self.short_write(i);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_i8(&mut self, i: i8) {
|
||||
self.short_write(i, i as u8 as u64);
|
||||
self.short_write(i as u8);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_i16(&mut self, i: i16) {
|
||||
self.short_write(i, (i as u16).to_le() as u64);
|
||||
self.short_write(i as u16);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_i32(&mut self, i: i32) {
|
||||
self.short_write(i, (i as u32).to_le() as u64);
|
||||
self.short_write(i as u32);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_i64(&mut self, i: i64) {
|
||||
self.short_write(i, (i as u64).to_le() as u64);
|
||||
self.short_write(i as u64);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_isize(&mut self, i: isize) {
|
||||
self.short_write(i, (i as usize).to_le() as u64);
|
||||
self.short_write(i as usize);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write(&mut self, msg: &[u8]) {
|
||||
let length = msg.len();
|
||||
self.length += length;
|
||||
|
||||
let mut needed = 0;
|
||||
|
||||
if self.ntail != 0 {
|
||||
needed = 8 - self.ntail;
|
||||
self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << (8 * self.ntail);
|
||||
if length < needed {
|
||||
self.ntail += length;
|
||||
return;
|
||||
} else {
|
||||
self.state.v3 ^= self.tail;
|
||||
Sip24Rounds::c_rounds(&mut self.state);
|
||||
self.state.v0 ^= self.tail;
|
||||
self.ntail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Buffered tail is now flushed, process new input.
|
||||
let len = length - needed;
|
||||
let left = len & 0x7;
|
||||
|
||||
let mut i = needed;
|
||||
while i < len - left {
|
||||
let mi = unsafe { load_int_le!(msg, i, u64) };
|
||||
|
||||
self.state.v3 ^= mi;
|
||||
Sip24Rounds::c_rounds(&mut self.state);
|
||||
self.state.v0 ^= mi;
|
||||
|
||||
i += 8;
|
||||
}
|
||||
|
||||
self.tail = unsafe { u8to64_le(msg, i, left) };
|
||||
self.ntail = left;
|
||||
self.slice_write(msg);
|
||||
}
|
||||
|
||||
fn finish(&self) -> u64 {
|
||||
|
|
|
@ -450,3 +450,48 @@ fn test_short_write_works() {
|
|||
|
||||
assert_eq!(h1_hash, h2_hash);
|
||||
}
|
||||
|
||||
macro_rules! test_fill_buffer {
|
||||
($type:ty, $write_method:ident) => {{
|
||||
// Test filling and overfilling the buffer from all possible offsets
|
||||
// for a given integer type and its corresponding write method.
|
||||
const SIZE: usize = std::mem::size_of::<$type>();
|
||||
let input = [42; BUFFER_SIZE];
|
||||
let x = 0x01234567_89ABCDEF_76543210_FEDCBA98_u128 as $type;
|
||||
let x_bytes = &x.to_ne_bytes();
|
||||
|
||||
for i in 1..=SIZE {
|
||||
let s = &input[..BUFFER_SIZE - i];
|
||||
|
||||
let mut h1 = SipHasher128::new_with_keys(7, 13);
|
||||
h1.write(s);
|
||||
h1.$write_method(x);
|
||||
|
||||
let mut h2 = SipHasher128::new_with_keys(7, 13);
|
||||
h2.write(s);
|
||||
h2.write(x_bytes);
|
||||
|
||||
let h1_hash = h1.finish128();
|
||||
let h2_hash = h2.finish128();
|
||||
|
||||
assert_eq!(h1_hash, h2_hash);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fill_buffer() {
|
||||
test_fill_buffer!(u8, write_u8);
|
||||
test_fill_buffer!(u16, write_u16);
|
||||
test_fill_buffer!(u32, write_u32);
|
||||
test_fill_buffer!(u64, write_u64);
|
||||
test_fill_buffer!(u128, write_u128);
|
||||
test_fill_buffer!(usize, write_usize);
|
||||
|
||||
test_fill_buffer!(i8, write_i8);
|
||||
test_fill_buffer!(i16, write_i16);
|
||||
test_fill_buffer!(i32, write_i32);
|
||||
test_fill_buffer!(i64, write_i64);
|
||||
test_fill_buffer!(i128, write_i128);
|
||||
test_fill_buffer!(isize, write_isize);
|
||||
}
|
||||
|
|
|
@ -9,4 +9,4 @@ llvm_asm!("nop" "nop");
|
|||
Considering that this would be a long explanation, we instead recommend you
|
||||
take a look at the [`llvm_asm`] chapter of the Unstable book:
|
||||
|
||||
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
|
|
|
@ -10,4 +10,4 @@ llvm_asm!("nop" : "r"(a));
|
|||
Considering that this would be a long explanation, we instead recommend you
|
||||
take a look at the [`llvm_asm`] chapter of the Unstable book:
|
||||
|
||||
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
|
|
|
@ -13,4 +13,4 @@ llvm_asm!("xor %eax, %eax"
|
|||
Considering that this would be a long explanation, we instead recommend you
|
||||
take a look at the [`llvm_asm`] chapter of the Unstable book:
|
||||
|
||||
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
|
|
|
@ -13,4 +13,4 @@ llvm_asm!("xor %eax, %eax"
|
|||
Considering that this would be a long explanation, we instead recommend you
|
||||
take a look at the [`llvm_asm`] chapter of the Unstable book:
|
||||
|
||||
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
|
|
|
@ -13,4 +13,4 @@ llvm_asm!("mov $$0x200, %eax"
|
|||
Considering that this would be a long explanation, we instead recommend you
|
||||
take a look at the [`llvm_asm`] chapter of the Unstable book:
|
||||
|
||||
[llvm_asm]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
use rustc_ast::attr::HasAttrs;
|
||||
use rustc_ast::mut_visit::*;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{DelimToken, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{DelimSpan, LazyTokenStreamInner, Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::{self as ast, AttrItem, Attribute, MetaItem};
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::map_in_place::MapInPlace;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{error_code, struct_span_err, Applicability, Handler};
|
||||
use rustc_feature::{Feature, Features, State as FeatureState};
|
||||
use rustc_feature::{
|
||||
|
@ -289,7 +292,37 @@ impl<'a> StripUnconfigured<'a> {
|
|||
expanded_attrs
|
||||
.into_iter()
|
||||
.flat_map(|(item, span)| {
|
||||
let attr = attr::mk_attr_from_item(attr.style, item, span);
|
||||
let orig_tokens =
|
||||
attr.tokens.as_ref().unwrap_or_else(|| panic!("Missing tokens for {:?}", attr));
|
||||
|
||||
// We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
|
||||
// and producing an attribute of the form `#[attr]`. We
|
||||
// have captured tokens for `attr` itself, but we need to
|
||||
// synthesize tokens for the wrapper `#` and `[]`, which
|
||||
// we do below.
|
||||
|
||||
// Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
|
||||
// for `attr` when we expand it to `#[attr]`
|
||||
let pound_token = orig_tokens.into_token_stream().trees().next().unwrap();
|
||||
if !matches!(pound_token, TokenTree::Token(Token { kind: TokenKind::Pound, .. })) {
|
||||
panic!("Bad tokens for attribute {:?}", attr);
|
||||
}
|
||||
// We don't really have a good span to use for the syntheized `[]`
|
||||
// in `#[attr]`, so just use the span of the `#` token.
|
||||
let bracket_group = TokenTree::Delimited(
|
||||
DelimSpan::from_single(pound_token.span()),
|
||||
DelimToken::Bracket,
|
||||
item.tokens
|
||||
.clone()
|
||||
.unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
|
||||
.into_token_stream(),
|
||||
);
|
||||
|
||||
let mut attr = attr::mk_attr_from_item(attr.style, item, span);
|
||||
attr.tokens = Some(Lrc::new(LazyTokenStreamInner::Ready(TokenStream::new(vec![
|
||||
(pound_token, Spacing::Alone),
|
||||
(bracket_group, Spacing::Alone),
|
||||
]))));
|
||||
self.process_cfg_attr(attr)
|
||||
})
|
||||
.collect()
|
||||
|
|
|
@ -1357,7 +1357,8 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
|
|||
// we'll expand attributes on expressions separately
|
||||
if !stmt.is_expr() {
|
||||
let (attr, derives, after_derive) = if stmt.is_item() {
|
||||
self.classify_item(&mut stmt)
|
||||
// FIXME: Handle custom attributes on statements (#15701)
|
||||
(None, vec![], false)
|
||||
} else {
|
||||
// ignore derives on non-item statements so it falls through
|
||||
// to the unused-attributes lint
|
||||
|
@ -1785,6 +1786,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
|
|||
span: at.span,
|
||||
id: at.id,
|
||||
style: at.style,
|
||||
tokens: None,
|
||||
};
|
||||
} else {
|
||||
noop_visit_attribute(at, self)
|
||||
|
|
|
@ -288,7 +288,8 @@ fn generic_extension<'cx>(
|
|||
// Replace all the tokens for the corresponding positions in the macro, to maintain
|
||||
// proper positions in error reporting, while maintaining the macro_backtrace.
|
||||
if rhs_spans.len() == tts.len() {
|
||||
tts = tts.map_enumerated(|i, mut tt| {
|
||||
tts = tts.map_enumerated(|i, tt| {
|
||||
let mut tt = tt.clone();
|
||||
let mut sp = rhs_spans[i];
|
||||
sp = sp.with_ctxt(tt.span().ctxt());
|
||||
tt.set_span(sp);
|
||||
|
|
|
@ -210,6 +210,11 @@ declare_features! (
|
|||
/// it is not on path for eventual stabilization).
|
||||
(active, no_niche, "1.42.0", None, None),
|
||||
|
||||
/// Allows using `#[rustc_allow_const_fn_unstable]`.
|
||||
/// This is an attribute on `const fn` for the same
|
||||
/// purpose as `#[allow_internal_unstable]`.
|
||||
(active, rustc_allow_const_fn_unstable, "1.49.0", Some(69399), None),
|
||||
|
||||
// no-tracking-issue-end
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
@ -598,6 +603,9 @@ declare_features! (
|
|||
/// Allows `#[instruction_set(_)]` attribute
|
||||
(active, isa_attribute, "1.48.0", Some(74727), None),
|
||||
|
||||
/// Allow anonymous constants from an inline `const` block
|
||||
(active, inline_const, "1.49.0", Some(76001), None),
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// feature-group-end: actual feature gates
|
||||
// -------------------------------------------------------------------------
|
||||
|
@ -618,6 +626,8 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
|
|||
sym::const_trait_bound_opt_out,
|
||||
sym::lazy_normalization_consts,
|
||||
sym::specialization,
|
||||
sym::inline_const,
|
||||
sym::repr128,
|
||||
];
|
||||
|
||||
/// Some features are not allowed to be used together at the same time, if
|
||||
|
|
|
@ -379,6 +379,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
allow_internal_unstable, AssumedUsed, template!(Word, List: "feat1, feat2, ..."),
|
||||
"allow_internal_unstable side-steps feature gating and stability checks",
|
||||
),
|
||||
gated!(
|
||||
rustc_allow_const_fn_unstable, AssumedUsed, template!(Word, List: "feat1, feat2, ..."),
|
||||
"rustc_allow_const_fn_unstable side-steps feature gating and stability checks"
|
||||
),
|
||||
gated!(
|
||||
allow_internal_unsafe, Normal, template!(Word),
|
||||
"allow_internal_unsafe side-steps the unsafe_code lint",
|
||||
|
@ -598,7 +602,7 @@ pub fn is_builtin_attr_name(name: Symbol) -> bool {
|
|||
BUILTIN_ATTRIBUTE_MAP.get(&name).is_some()
|
||||
}
|
||||
|
||||
pub static BUILTIN_ATTRIBUTE_MAP: SyncLazy<FxHashMap<Symbol, &'static BuiltinAttribute>> =
|
||||
pub static BUILTIN_ATTRIBUTE_MAP: SyncLazy<FxHashMap<Symbol, &BuiltinAttribute>> =
|
||||
SyncLazy::new(|| {
|
||||
let mut map = FxHashMap::default();
|
||||
for attr in BUILTIN_ATTRIBUTES.iter() {
|
||||
|
|
|
@ -486,6 +486,8 @@ impl Generics<'hir> {
|
|||
#[derive(HashStable_Generic)]
|
||||
pub enum SyntheticTyParamKind {
|
||||
ImplTrait,
|
||||
// Created by the `#[rustc_synthetic]` attribute.
|
||||
FromAttr,
|
||||
}
|
||||
|
||||
/// A where-clause in a definition.
|
||||
|
@ -1099,11 +1101,11 @@ pub enum StmtKind<'hir> {
|
|||
Semi(&'hir Expr<'hir>),
|
||||
}
|
||||
|
||||
impl StmtKind<'hir> {
|
||||
pub fn attrs(&self) -> &'hir [Attribute] {
|
||||
impl<'hir> StmtKind<'hir> {
|
||||
pub fn attrs(&self, get_item: impl FnOnce(ItemId) -> &'hir Item<'hir>) -> &'hir [Attribute] {
|
||||
match *self {
|
||||
StmtKind::Local(ref l) => &l.attrs,
|
||||
StmtKind::Item(_) => &[],
|
||||
StmtKind::Item(ref item_id) => &get_item(*item_id).attrs,
|
||||
StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => &e.attrs,
|
||||
}
|
||||
}
|
||||
|
@ -1361,6 +1363,7 @@ impl Expr<'_> {
|
|||
pub fn precedence(&self) -> ExprPrecedence {
|
||||
match self.kind {
|
||||
ExprKind::Box(_) => ExprPrecedence::Box,
|
||||
ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock,
|
||||
ExprKind::Array(_) => ExprPrecedence::Array,
|
||||
ExprKind::Call(..) => ExprPrecedence::Call,
|
||||
ExprKind::MethodCall(..) => ExprPrecedence::MethodCall,
|
||||
|
@ -1446,6 +1449,7 @@ impl Expr<'_> {
|
|||
| ExprKind::LlvmInlineAsm(..)
|
||||
| ExprKind::AssignOp(..)
|
||||
| ExprKind::Lit(_)
|
||||
| ExprKind::ConstBlock(..)
|
||||
| ExprKind::Unary(..)
|
||||
| ExprKind::Box(..)
|
||||
| ExprKind::AddrOf(..)
|
||||
|
@ -1501,6 +1505,8 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool {
|
|||
pub enum ExprKind<'hir> {
|
||||
/// A `box x` expression.
|
||||
Box(&'hir Expr<'hir>),
|
||||
/// Allow anonymous constants from an inline `const` block
|
||||
ConstBlock(AnonConst),
|
||||
/// An array (e.g., `[a, b, c, d]`).
|
||||
Array(&'hir [Expr<'hir>]),
|
||||
/// A function call.
|
||||
|
@ -2377,15 +2383,6 @@ impl VisibilityKind<'_> {
|
|||
VisibilityKind::Crate(..) | VisibilityKind::Restricted { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn descr(&self) -> &'static str {
|
||||
match *self {
|
||||
VisibilityKind::Public => "public",
|
||||
VisibilityKind::Inherited => "private",
|
||||
VisibilityKind::Crate(..) => "crate-visible",
|
||||
VisibilityKind::Restricted { .. } => "restricted",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, HashStable_Generic)]
|
||||
|
|
|
@ -1068,6 +1068,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
|
|||
ExprKind::Array(subexpressions) => {
|
||||
walk_list!(visitor, visit_expr, subexpressions);
|
||||
}
|
||||
ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const),
|
||||
ExprKind::Repeat(ref element, ref count) => {
|
||||
visitor.visit_expr(element);
|
||||
visitor.visit_anon_const(count)
|
||||
|
|
|
@ -263,6 +263,7 @@ language_item_table! {
|
|||
// is required to define it somewhere. Additionally, there are restrictions on crates that use
|
||||
// a weak lang item, but do not have it defined.
|
||||
Panic, sym::panic, panic_fn, Target::Fn;
|
||||
PanicStr, sym::panic_str, panic_str, Target::Fn;
|
||||
PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn;
|
||||
PanicInfo, sym::panic_info, panic_info, Target::Struct;
|
||||
PanicLocation, sym::panic_location, panic_location, Target::Struct;
|
||||
|
|
|
@ -1135,6 +1135,13 @@ impl<'a> State<'a> {
|
|||
self.end()
|
||||
}
|
||||
|
||||
fn print_expr_anon_const(&mut self, anon_const: &hir::AnonConst) {
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.s.word_space("const");
|
||||
self.print_anon_const(anon_const);
|
||||
self.end()
|
||||
}
|
||||
|
||||
fn print_expr_repeat(&mut self, element: &hir::Expr<'_>, count: &hir::AnonConst) {
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.s.word("[");
|
||||
|
@ -1287,6 +1294,9 @@ impl<'a> State<'a> {
|
|||
hir::ExprKind::Array(ref exprs) => {
|
||||
self.print_expr_vec(exprs);
|
||||
}
|
||||
hir::ExprKind::ConstBlock(ref anon_const) => {
|
||||
self.print_expr_anon_const(anon_const);
|
||||
}
|
||||
hir::ExprKind::Repeat(ref element, ref count) => {
|
||||
self.print_expr_repeat(&element, count);
|
||||
}
|
||||
|
|
|
@ -619,6 +619,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
scrut_hir_id,
|
||||
opt_suggest_box_span,
|
||||
arm_span,
|
||||
scrut_span,
|
||||
..
|
||||
}) => match source {
|
||||
hir::MatchSource::IfLetDesugar { .. } => {
|
||||
|
@ -664,18 +665,29 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
Some(ty::error::ExpectedFound { expected, .. }) => expected,
|
||||
_ => last_ty,
|
||||
});
|
||||
let msg = "`match` arms have incompatible types";
|
||||
err.span_label(cause.span, msg);
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
let mut any_multiline_arm = source_map.is_multiline(arm_span);
|
||||
if prior_arms.len() <= 4 {
|
||||
for sp in prior_arms {
|
||||
any_multiline_arm |= source_map.is_multiline(*sp);
|
||||
err.span_label(*sp, format!("this is found to be of type `{}`", t));
|
||||
}
|
||||
} else if let Some(sp) = prior_arms.last() {
|
||||
any_multiline_arm |= source_map.is_multiline(*sp);
|
||||
err.span_label(
|
||||
*sp,
|
||||
format!("this and all prior arms are found to be of type `{}`", t),
|
||||
);
|
||||
}
|
||||
let outer_error_span = if any_multiline_arm {
|
||||
// Cover just `match` and the scrutinee expression, not
|
||||
// the entire match body, to reduce diagram noise.
|
||||
cause.span.shrink_to_lo().to(scrut_span)
|
||||
} else {
|
||||
cause.span
|
||||
};
|
||||
let msg = "`match` arms have incompatible types";
|
||||
err.span_label(outer_error_span, msg);
|
||||
if let Some(sp) = semi_span {
|
||||
err.span_suggestion_short(
|
||||
sp,
|
||||
|
|
|
@ -91,17 +91,6 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> {
|
|||
if let (None, Some(ty)) =
|
||||
(self.found_local_pattern, self.node_ty_contains_target(local.hir_id))
|
||||
{
|
||||
// FIXME: There's a trade-off here - we can either check that our target span
|
||||
// is contained in `local.span` or not. If we choose to check containment
|
||||
// we can avoid some spurious suggestions (see #72690), but we lose
|
||||
// the ability to report on things like:
|
||||
//
|
||||
// ```
|
||||
// let x = vec![];
|
||||
// ```
|
||||
//
|
||||
// because the target span will be in the macro expansion of `vec![]`.
|
||||
// At present we choose not to check containment.
|
||||
self.found_local_pattern = Some(&*local.pat);
|
||||
self.found_node_ty = Some(ty);
|
||||
}
|
||||
|
@ -113,12 +102,10 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> {
|
|||
if let (None, Some(ty)) =
|
||||
(self.found_arg_pattern, self.node_ty_contains_target(param.hir_id))
|
||||
{
|
||||
if self.target_span.contains(param.pat.span) {
|
||||
self.found_arg_pattern = Some(&*param.pat);
|
||||
self.found_node_ty = Some(ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
intravisit::walk_body(self, body);
|
||||
}
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> {
|
|||
|
||||
impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> {
|
||||
type Lifted = FreeRegionMap<'tcx>;
|
||||
fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<FreeRegionMap<'tcx>> {
|
||||
self.relation.maybe_map(|&fr| tcx.lift(&fr)).map(|relation| FreeRegionMap { relation })
|
||||
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<FreeRegionMap<'tcx>> {
|
||||
self.relation.maybe_map(|&fr| tcx.lift(fr)).map(|relation| FreeRegionMap { relation })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -636,7 +636,7 @@ where
|
|||
if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) {
|
||||
// Fast path for the common case.
|
||||
self.relate(a, b)?;
|
||||
return Ok(ty::Binder::bind(a));
|
||||
return Ok(ty::Binder::dummy(a));
|
||||
}
|
||||
|
||||
if self.ambient_covariance() {
|
||||
|
|
|
@ -2,12 +2,12 @@ use super::ObjectSafetyViolation;
|
|||
|
||||
use crate::infer::InferCtxt;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
|
||||
use rustc_errors::{struct_span_err, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{MultiSpan, Span};
|
||||
use std::fmt;
|
||||
|
||||
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
|
@ -54,10 +54,11 @@ pub fn report_object_safety_error(
|
|||
"the trait `{}` cannot be made into an object",
|
||||
trait_str
|
||||
);
|
||||
err.span_label(span, format!("the trait `{}` cannot be made into an object", trait_str));
|
||||
err.span_label(span, format!("`{}` cannot be made into an object", trait_str));
|
||||
|
||||
let mut reported_violations = FxHashSet::default();
|
||||
let mut had_span_label = false;
|
||||
let mut multi_span = vec![];
|
||||
let mut messages = vec![];
|
||||
for violation in violations {
|
||||
if let ObjectSafetyViolation::SizedSelf(sp) = &violation {
|
||||
if !sp.is_empty() {
|
||||
|
@ -71,31 +72,37 @@ pub fn report_object_safety_error(
|
|||
let msg = if trait_span.is_none() || spans.is_empty() {
|
||||
format!("the trait cannot be made into an object because {}", violation.error_msg())
|
||||
} else {
|
||||
had_span_label = true;
|
||||
format!("...because {}", violation.error_msg())
|
||||
};
|
||||
if spans.is_empty() {
|
||||
err.note(&msg);
|
||||
} else {
|
||||
for span in spans {
|
||||
err.span_label(span, &msg);
|
||||
multi_span.push(span);
|
||||
messages.push(msg.clone());
|
||||
}
|
||||
}
|
||||
match (trait_span, violation.solution()) {
|
||||
(Some(_), Some((note, None))) => {
|
||||
err.help(¬e);
|
||||
}
|
||||
(Some(_), Some((note, Some((sugg, span))))) => {
|
||||
err.span_suggestion(span, ¬e, sugg, Applicability::MachineApplicable);
|
||||
}
|
||||
if trait_span.is_some() {
|
||||
// Only provide the help if its a local trait, otherwise it's not actionable.
|
||||
_ => {}
|
||||
violation.solution(&mut err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(trait_span), true) = (trait_span, had_span_label) {
|
||||
err.span_label(trait_span, "this trait cannot be made into an object...");
|
||||
let has_multi_span = !multi_span.is_empty();
|
||||
let mut note_span = MultiSpan::from_spans(multi_span.clone());
|
||||
if let (Some(trait_span), true) = (trait_span, has_multi_span) {
|
||||
note_span
|
||||
.push_span_label(trait_span, "this trait cannot be made into an object...".to_string());
|
||||
}
|
||||
for (span, msg) in multi_span.into_iter().zip(messages.into_iter()) {
|
||||
note_span.push_span_label(span, msg);
|
||||
}
|
||||
err.span_note(
|
||||
note_span,
|
||||
"for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \
|
||||
to be resolvable dynamically; for more information visit \
|
||||
<https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
|
||||
);
|
||||
|
||||
if tcx.sess.trait_methods_not_found.borrow().contains(&span) {
|
||||
// Avoid emitting error caused by non-existing method (#58734)
|
||||
|
|
|
@ -126,14 +126,15 @@ impl Elaborator<'tcx> {
|
|||
fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) {
|
||||
let tcx = self.visited.tcx;
|
||||
|
||||
match obligation.predicate.skip_binders() {
|
||||
let bound_predicate = obligation.predicate.bound_atom();
|
||||
match bound_predicate.skip_binder() {
|
||||
ty::PredicateAtom::Trait(data, _) => {
|
||||
// Get predicates declared on the trait.
|
||||
let predicates = tcx.super_predicates_of(data.def_id());
|
||||
|
||||
let obligations = predicates.predicates.iter().map(|&(pred, _)| {
|
||||
predicate_obligation(
|
||||
pred.subst_supertrait(tcx, &ty::Binder::bind(data.trait_ref)),
|
||||
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
|
||||
obligation.param_env,
|
||||
obligation.cause.clone(),
|
||||
)
|
||||
|
|
|
@ -2,8 +2,9 @@ use crate::interface::{Compiler, Result};
|
|||
use crate::proc_macro_decls;
|
||||
use crate::util;
|
||||
|
||||
use rustc_ast::mut_visit::MutVisitor;
|
||||
use rustc_ast::{self as ast, visit};
|
||||
use rustc_ast::mut_visit::{self, MutVisitor};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{self as ast, token, visit};
|
||||
use rustc_codegen_ssa::back::link::emit_metadata;
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
use rustc_data_structures::sync::{par_iter, Lrc, OnceCell, ParallelIterator, WorkerLocal};
|
||||
|
@ -36,6 +37,7 @@ use rustc_span::symbol::Symbol;
|
|||
use rustc_span::{FileName, RealFileName};
|
||||
use rustc_trait_selection::traits;
|
||||
use rustc_typeck as typeck;
|
||||
use smallvec::SmallVec;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use rustc_serialize::json;
|
||||
|
@ -50,6 +52,64 @@ use std::path::PathBuf;
|
|||
use std::rc::Rc;
|
||||
use std::{env, fs, iter, mem};
|
||||
|
||||
/// Remove alls `LazyTokenStreams` from an AST struct
|
||||
/// Normally, this is done during AST lowering. However,
|
||||
/// printing the AST JSON requires us to serialize
|
||||
/// the entire AST, and we don't want to serialize
|
||||
/// a `LazyTokenStream`.
|
||||
struct TokenStripper;
|
||||
impl mut_visit::MutVisitor for TokenStripper {
|
||||
fn flat_map_item(&mut self, mut i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
|
||||
i.tokens = None;
|
||||
mut_visit::noop_flat_map_item(i, self)
|
||||
}
|
||||
fn visit_block(&mut self, b: &mut P<ast::Block>) {
|
||||
b.tokens = None;
|
||||
mut_visit::noop_visit_block(b, self);
|
||||
}
|
||||
fn flat_map_stmt(&mut self, mut stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
|
||||
stmt.tokens = None;
|
||||
mut_visit::noop_flat_map_stmt(stmt, self)
|
||||
}
|
||||
fn visit_pat(&mut self, p: &mut P<ast::Pat>) {
|
||||
p.tokens = None;
|
||||
mut_visit::noop_visit_pat(p, self);
|
||||
}
|
||||
fn visit_ty(&mut self, ty: &mut P<ast::Ty>) {
|
||||
ty.tokens = None;
|
||||
mut_visit::noop_visit_ty(ty, self);
|
||||
}
|
||||
fn visit_attribute(&mut self, attr: &mut ast::Attribute) {
|
||||
attr.tokens = None;
|
||||
if let ast::AttrKind::Normal(ast::AttrItem { tokens, .. }) = &mut attr.kind {
|
||||
*tokens = None;
|
||||
}
|
||||
mut_visit::noop_visit_attribute(attr, self);
|
||||
}
|
||||
|
||||
fn visit_interpolated(&mut self, nt: &mut token::Nonterminal) {
|
||||
if let token::Nonterminal::NtMeta(meta) = nt {
|
||||
meta.tokens = None;
|
||||
}
|
||||
// Handles all of the other cases
|
||||
mut_visit::noop_visit_interpolated(nt, self);
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, p: &mut ast::Path) {
|
||||
p.tokens = None;
|
||||
mut_visit::noop_visit_path(p, self);
|
||||
}
|
||||
fn visit_vis(&mut self, vis: &mut ast::Visibility) {
|
||||
vis.tokens = None;
|
||||
mut_visit::noop_visit_vis(vis, self);
|
||||
}
|
||||
fn visit_expr(&mut self, e: &mut P<ast::Expr>) {
|
||||
e.tokens = None;
|
||||
mut_visit::noop_visit_expr(e, self);
|
||||
}
|
||||
fn visit_mac(&mut self, _mac: &mut ast::MacCall) {}
|
||||
}
|
||||
|
||||
pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> {
|
||||
let krate = sess.time("parse_crate", || match input {
|
||||
Input::File(file) => parse_crate_from_file(file, &sess.parse_sess),
|
||||
|
@ -59,6 +119,10 @@ pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> {
|
|||
})?;
|
||||
|
||||
if sess.opts.debugging_opts.ast_json_noexpand {
|
||||
// Set any `token` fields to `None` before
|
||||
// we display the AST.
|
||||
let mut krate = krate.clone();
|
||||
TokenStripper.visit_crate(&mut krate);
|
||||
println!("{}", json::as_json(&krate));
|
||||
}
|
||||
|
||||
|
@ -379,6 +443,10 @@ fn configure_and_expand_inner<'a>(
|
|||
}
|
||||
|
||||
if sess.opts.debugging_opts.ast_json {
|
||||
// Set any `token` fields to `None` before
|
||||
// we display the AST.
|
||||
let mut krate = krate.clone();
|
||||
TokenStripper.visit_crate(&mut krate);
|
||||
println!("{}", json::as_json(&krate));
|
||||
}
|
||||
|
||||
|
|
|
@ -968,7 +968,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
|
|||
while let Some(attr) = attrs.next() {
|
||||
if attr.is_doc_comment() {
|
||||
sugared_span =
|
||||
Some(sugared_span.map_or_else(|| attr.span, |span| span.with_hi(attr.span.hi())));
|
||||
Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi())));
|
||||
}
|
||||
|
||||
if attrs.peek().map(|next_attr| next_attr.is_doc_comment()).unwrap_or_default() {
|
||||
|
@ -994,7 +994,8 @@ impl EarlyLintPass for UnusedDocComment {
|
|||
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
|
||||
let kind = match stmt.kind {
|
||||
ast::StmtKind::Local(..) => "statements",
|
||||
ast::StmtKind::Item(..) => "inner items",
|
||||
// Disabled pending discussion in #78306
|
||||
ast::StmtKind::Item(..) => return,
|
||||
// expressions will be reported by `check_expr`.
|
||||
ast::StmtKind::Empty
|
||||
| ast::StmtKind::Semi(_)
|
||||
|
@ -2288,12 +2289,20 @@ impl EarlyLintPass for IncompleteFeatures {
|
|||
n, n,
|
||||
));
|
||||
}
|
||||
if HAS_MIN_FEATURES.contains(&name) {
|
||||
builder.help(&format!(
|
||||
"consider using `min_{}` instead, which is more stable and complete",
|
||||
name,
|
||||
));
|
||||
}
|
||||
builder.emit();
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const HAS_MIN_FEATURES: &[Symbol] = &[sym::const_generics, sym::specialization];
|
||||
|
||||
declare_lint! {
|
||||
/// The `invalid_value` lint detects creating a value that is not valid,
|
||||
/// such as a NULL reference.
|
||||
|
|
|
@ -18,6 +18,7 @@ use crate::context::{EarlyContext, LintContext, LintStore};
|
|||
use crate::passes::{EarlyLintPass, EarlyLintPassObject};
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::visit as ast_visit;
|
||||
use rustc_attr::HasAttrs;
|
||||
use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::Ident;
|
||||
|
@ -119,8 +120,22 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
|
|||
}
|
||||
|
||||
fn visit_stmt(&mut self, s: &'a ast::Stmt) {
|
||||
run_early_pass!(self, check_stmt, s);
|
||||
self.check_id(s.id);
|
||||
// Add the statement's lint attributes to our
|
||||
// current state when checking the statement itself.
|
||||
// This allows us to handle attributes like
|
||||
// `#[allow(unused_doc_comments)]`, which apply to
|
||||
// sibling attributes on the same target
|
||||
//
|
||||
// Note that statements get their attributes from
|
||||
// the AST struct that they wrap (e.g. an item)
|
||||
self.with_lint_attrs(s.id, s.attrs(), |cx| {
|
||||
run_early_pass!(cx, check_stmt, s);
|
||||
cx.check_id(s.id);
|
||||
});
|
||||
// The visitor for the AST struct wrapped
|
||||
// by the statement (e.g. `Item`) will call
|
||||
// `with_lint_attrs`, so do this walk
|
||||
// outside of the above `with_lint_attrs` call
|
||||
ast_visit::walk_stmt(self, s);
|
||||
}
|
||||
|
||||
|
@ -195,6 +210,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
|
|||
run_early_pass!(self, check_expr_post, e);
|
||||
}
|
||||
|
||||
fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) {
|
||||
run_early_pass!(self, check_generic_arg, arg);
|
||||
ast_visit::walk_generic_arg(self, arg);
|
||||
}
|
||||
|
||||
fn visit_generic_param(&mut self, param: &'a ast::GenericParam) {
|
||||
run_early_pass!(self, check_generic_param, param);
|
||||
ast_visit::walk_generic_param(self, param);
|
||||
|
|
|
@ -174,12 +174,13 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
|
|||
}
|
||||
|
||||
fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
|
||||
// statement attributes are actually just attributes on one of
|
||||
// - item
|
||||
// - local
|
||||
// - expression
|
||||
// so we keep track of lint levels there
|
||||
lint_callback!(self, check_stmt, s);
|
||||
let get_item = |id: hir::ItemId| self.context.tcx.hir().item(id.id);
|
||||
let attrs = &s.kind.attrs(get_item);
|
||||
// See `EarlyContextAndPass::visit_stmt` for an explanation
|
||||
// of why we call `walk_stmt` outside of `with_lint_attrs`
|
||||
self.with_lint_attrs(s.hir_id, attrs, |cx| {
|
||||
lint_callback!(cx, check_stmt, s);
|
||||
});
|
||||
hir_visit::walk_stmt(self, s);
|
||||
}
|
||||
|
||||
|
|
|
@ -562,6 +562,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
|
||||
// We will call `with_lint_attrs` when we walk
|
||||
// the `StmtKind`. The outer statement itself doesn't
|
||||
// define the lint levels.
|
||||
intravisit::walk_stmt(self, e);
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
|
||||
self.with_lint_attrs(e.hir_id, &e.attrs, |builder| {
|
||||
intravisit::walk_expr(builder, e);
|
||||
|
|
|
@ -33,6 +33,7 @@ macro_rules! late_lint_methods {
|
|||
fn check_expr(a: &$hir hir::Expr<$hir>);
|
||||
fn check_expr_post(a: &$hir hir::Expr<$hir>);
|
||||
fn check_ty(a: &$hir hir::Ty<$hir>);
|
||||
fn check_generic_arg(a: &$hir hir::GenericArg<$hir>);
|
||||
fn check_generic_param(a: &$hir hir::GenericParam<$hir>);
|
||||
fn check_generics(a: &$hir hir::Generics<$hir>);
|
||||
fn check_where_predicate(a: &$hir hir::WherePredicate<$hir>);
|
||||
|
@ -176,6 +177,7 @@ macro_rules! early_lint_methods {
|
|||
fn check_expr(a: &ast::Expr);
|
||||
fn check_expr_post(a: &ast::Expr);
|
||||
fn check_ty(a: &ast::Ty);
|
||||
fn check_generic_arg(a: &ast::GenericArg);
|
||||
fn check_generic_param(a: &ast::GenericParam);
|
||||
fn check_generics(a: &ast::Generics);
|
||||
fn check_where_predicate(a: &ast::WherePredicate);
|
||||
|
|
|
@ -145,9 +145,9 @@ fn lint_overflowing_range_endpoint<'tcx>(
|
|||
// We need to preserve the literal's suffix,
|
||||
// as it may determine typing information.
|
||||
let suffix = match lit.node {
|
||||
LitKind::Int(_, LitIntType::Signed(s)) => s.name_str().to_string(),
|
||||
LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str().to_string(),
|
||||
LitKind::Int(_, LitIntType::Unsuffixed) => "".to_string(),
|
||||
LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
|
||||
LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
|
||||
LitKind::Int(_, LitIntType::Unsuffixed) => "",
|
||||
_ => bug!(),
|
||||
};
|
||||
let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
|
||||
|
@ -170,24 +170,25 @@ fn lint_overflowing_range_endpoint<'tcx>(
|
|||
// warnings are consistent between 32- and 64-bit platforms.
|
||||
fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) {
|
||||
match int_ty {
|
||||
ast::IntTy::Isize => (i64::MIN as i128, i64::MAX as i128),
|
||||
ast::IntTy::I8 => (i8::MIN as i64 as i128, i8::MAX as i128),
|
||||
ast::IntTy::I16 => (i16::MIN as i64 as i128, i16::MAX as i128),
|
||||
ast::IntTy::I32 => (i32::MIN as i64 as i128, i32::MAX as i128),
|
||||
ast::IntTy::I64 => (i64::MIN as i128, i64::MAX as i128),
|
||||
ast::IntTy::I128 => (i128::MIN as i128, i128::MAX),
|
||||
ast::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()),
|
||||
ast::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()),
|
||||
ast::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()),
|
||||
ast::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()),
|
||||
ast::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()),
|
||||
ast::IntTy::I128 => (i128::MIN, i128::MAX),
|
||||
}
|
||||
}
|
||||
|
||||
fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) {
|
||||
match uint_ty {
|
||||
ast::UintTy::Usize => (u64::MIN as u128, u64::MAX as u128),
|
||||
ast::UintTy::U8 => (u8::MIN as u128, u8::MAX as u128),
|
||||
ast::UintTy::U16 => (u16::MIN as u128, u16::MAX as u128),
|
||||
ast::UintTy::U32 => (u32::MIN as u128, u32::MAX as u128),
|
||||
ast::UintTy::U64 => (u64::MIN as u128, u64::MAX as u128),
|
||||
ast::UintTy::U128 => (u128::MIN, u128::MAX),
|
||||
}
|
||||
let max = match uint_ty {
|
||||
ast::UintTy::Usize => u64::MAX.into(),
|
||||
ast::UintTy::U8 => u8::MAX.into(),
|
||||
ast::UintTy::U16 => u16::MAX.into(),
|
||||
ast::UintTy::U32 => u32::MAX.into(),
|
||||
ast::UintTy::U64 => u64::MAX.into(),
|
||||
ast::UintTy::U128 => u128::MAX,
|
||||
};
|
||||
(0, max)
|
||||
}
|
||||
|
||||
fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String> {
|
||||
|
|
|
@ -751,13 +751,20 @@ impl UnusedDelimLint for UnusedParens {
|
|||
if !Self::is_expr_delims_necessary(inner, followed_by_block)
|
||||
&& value.attrs.is_empty()
|
||||
&& !value.span.from_expansion()
|
||||
&& (ctx != UnusedDelimsCtx::LetScrutineeExpr
|
||||
|| match inner.kind {
|
||||
ast::ExprKind::Binary(
|
||||
rustc_span::source_map::Spanned { node, .. },
|
||||
_,
|
||||
_,
|
||||
) if node.lazy() => false,
|
||||
_ => true,
|
||||
})
|
||||
{
|
||||
self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos)
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Let(_, ref expr) => {
|
||||
// FIXME(#60336): Properly handle `let true = (false && true)`
|
||||
// actually needing the parenthesis.
|
||||
self.check_unused_delims_expr(
|
||||
cx,
|
||||
expr,
|
||||
|
@ -839,10 +846,6 @@ impl EarlyLintPass for UnusedParens {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_anon_const(&mut self, cx: &EarlyContext<'_>, c: &ast::AnonConst) {
|
||||
self.check_unused_delims_expr(cx, &c.value, UnusedDelimsCtx::AnonConst, false, None, None);
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
|
||||
if let StmtKind::Local(ref local) = s.kind {
|
||||
self.check_unused_parens_pat(cx, &local.pat, false, false);
|
||||
|
@ -965,13 +968,6 @@ impl UnusedDelimLint for UnusedBraces {
|
|||
if !Self::is_expr_delims_necessary(expr, followed_by_block)
|
||||
&& (ctx != UnusedDelimsCtx::AnonConst
|
||||
|| matches!(expr.kind, ast::ExprKind::Lit(_)))
|
||||
// array length expressions are checked during `check_anon_const` and `check_ty`,
|
||||
// once as `ArrayLenExpr` and once as `AnonConst`.
|
||||
//
|
||||
// As we do not want to lint this twice, we do not emit an error for
|
||||
// `ArrayLenExpr` if `AnonConst` would do the same.
|
||||
&& (ctx != UnusedDelimsCtx::ArrayLenExpr
|
||||
|| !matches!(expr.kind, ast::ExprKind::Lit(_)))
|
||||
&& !cx.sess().source_map().is_multiline(value.span)
|
||||
&& value.attrs.is_empty()
|
||||
&& !value.span.from_expansion()
|
||||
|
@ -999,21 +995,54 @@ impl UnusedDelimLint for UnusedBraces {
|
|||
}
|
||||
|
||||
impl EarlyLintPass for UnusedBraces {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
<Self as UnusedDelimLint>::check_expr(self, cx, e)
|
||||
}
|
||||
|
||||
fn check_anon_const(&mut self, cx: &EarlyContext<'_>, c: &ast::AnonConst) {
|
||||
self.check_unused_delims_expr(cx, &c.value, UnusedDelimsCtx::AnonConst, false, None, None);
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
|
||||
<Self as UnusedDelimLint>::check_stmt(self, cx, s)
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
<Self as UnusedDelimLint>::check_expr(self, cx, e);
|
||||
|
||||
if let ExprKind::Repeat(_, ref anon_const) = e.kind {
|
||||
self.check_unused_delims_expr(
|
||||
cx,
|
||||
&anon_const.value,
|
||||
UnusedDelimsCtx::AnonConst,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
|
||||
if let ast::GenericArg::Const(ct) = arg {
|
||||
self.check_unused_delims_expr(
|
||||
cx,
|
||||
&ct.value,
|
||||
UnusedDelimsCtx::AnonConst,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
|
||||
if let Some(anon_const) = &v.disr_expr {
|
||||
self.check_unused_delims_expr(
|
||||
cx,
|
||||
&anon_const.value,
|
||||
UnusedDelimsCtx::AnonConst,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
|
||||
if let &ast::TyKind::Paren(ref r) = &ty.kind {
|
||||
if let ast::TyKind::Array(_, ref len) = r.kind {
|
||||
match ty.kind {
|
||||
ast::TyKind::Array(_, ref len) => {
|
||||
self.check_unused_delims_expr(
|
||||
cx,
|
||||
&len.value,
|
||||
|
@ -1023,6 +1052,19 @@ impl EarlyLintPass for UnusedBraces {
|
|||
None,
|
||||
);
|
||||
}
|
||||
|
||||
ast::TyKind::Typeof(ref anon_const) => {
|
||||
self.check_unused_delims_expr(
|
||||
cx,
|
||||
&anon_const.value,
|
||||
UnusedDelimsCtx::AnonConst,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use syn::{self, parse_quote};
|
|||
|
||||
pub fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
|
||||
s.add_bounds(synstructure::AddBounds::Generics);
|
||||
s.bind_with(|_| synstructure::BindStyle::Move);
|
||||
|
||||
let tcx: syn::Lifetime = parse_quote!('tcx);
|
||||
let newtcx: syn::GenericParam = parse_quote!('__lifted);
|
||||
|
@ -43,8 +44,8 @@ pub fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStre
|
|||
quote! {
|
||||
type Lifted = #lifted;
|
||||
|
||||
fn lift_to_tcx(&self, __tcx: ::rustc_middle::ty::TyCtxt<'__lifted>) -> Option<#lifted> {
|
||||
Some(match *self { #body })
|
||||
fn lift_to_tcx(self, __tcx: ::rustc_middle::ty::TyCtxt<'__lifted>) -> Option<#lifted> {
|
||||
Some(match self { #body })
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
@ -190,7 +190,11 @@ impl<T: Parse> Parse for List<T> {
|
|||
}
|
||||
|
||||
/// A named group containing queries.
|
||||
///
|
||||
/// For now, the name is not used any more, but the capability remains interesting for future
|
||||
/// developments of the query system.
|
||||
struct Group {
|
||||
#[allow(unused)]
|
||||
name: Ident,
|
||||
queries: List<Query>,
|
||||
}
|
||||
|
@ -417,12 +421,9 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
let mut query_stream = quote! {};
|
||||
let mut query_description_stream = quote! {};
|
||||
let mut dep_node_def_stream = quote! {};
|
||||
let mut dep_node_force_stream = quote! {};
|
||||
let mut try_load_from_on_disk_cache_stream = quote! {};
|
||||
let mut cached_queries = quote! {};
|
||||
|
||||
for group in groups.0 {
|
||||
let mut group_stream = quote! {};
|
||||
for mut query in group.queries.0 {
|
||||
let modifiers = process_modifiers(&mut query);
|
||||
let name = &query.name;
|
||||
|
@ -437,22 +438,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
cached_queries.extend(quote! {
|
||||
#name,
|
||||
});
|
||||
|
||||
try_load_from_on_disk_cache_stream.extend(quote! {
|
||||
::rustc_middle::dep_graph::DepKind::#name => {
|
||||
if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
|
||||
debug_assert!($tcx.dep_graph
|
||||
.node_color($dep_node)
|
||||
.map(|c| c.is_green())
|
||||
.unwrap_or(false));
|
||||
|
||||
let key = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node).unwrap();
|
||||
if queries::#name::cache_on_disk($tcx, &key, None) {
|
||||
let _ = $tcx.#name(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut attributes = Vec::new();
|
||||
|
@ -485,9 +470,9 @@ 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! {
|
||||
query_stream.extend(quote! {
|
||||
#(#doc_comments)*
|
||||
[#attribute_stream] fn #name: #name(#arg) #result,
|
||||
[#attribute_stream] fn #name(#arg) #result,
|
||||
});
|
||||
|
||||
// Create a dep node for the query
|
||||
|
@ -495,37 +480,10 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
[#attribute_stream] #name(#arg),
|
||||
});
|
||||
|
||||
// Add a match arm to force the query given the dep node
|
||||
dep_node_force_stream.extend(quote! {
|
||||
::rustc_middle::dep_graph::DepKind::#name => {
|
||||
if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
|
||||
if let Some(key) = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node) {
|
||||
force_query::<crate::ty::query::queries::#name<'_>, _>(
|
||||
$tcx,
|
||||
key,
|
||||
DUMMY_SP,
|
||||
*$dep_node
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_query_description_impl(&query, modifiers, &mut query_description_stream);
|
||||
}
|
||||
let name = &group.name;
|
||||
query_stream.extend(quote! {
|
||||
#name { #group_stream },
|
||||
});
|
||||
}
|
||||
|
||||
dep_node_force_stream.extend(quote! {
|
||||
::rustc_middle::dep_graph::DepKind::Null => {
|
||||
bug!("Cannot force dep node: {:?}", $dep_node)
|
||||
}
|
||||
});
|
||||
|
||||
TokenStream::from(quote! {
|
||||
macro_rules! rustc_query_append {
|
||||
([$($macro:tt)*][$($other:tt)*]) => {
|
||||
|
@ -546,15 +504,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
);
|
||||
}
|
||||
}
|
||||
macro_rules! rustc_dep_node_force {
|
||||
([$dep_node:expr, $tcx:expr] $($other:tt)*) => {
|
||||
match $dep_node.kind {
|
||||
$($other)*
|
||||
|
||||
#dep_node_force_stream
|
||||
}
|
||||
}
|
||||
}
|
||||
macro_rules! rustc_cached_queries {
|
||||
($($macro:tt)*) => {
|
||||
$($macro)*(#cached_queries);
|
||||
|
@ -562,14 +511,5 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
|
||||
#query_description_stream
|
||||
|
||||
macro_rules! rustc_dep_node_try_load_from_on_disk_cache {
|
||||
($dep_node:expr, $tcx:expr) => {
|
||||
match $dep_node.kind {
|
||||
#try_load_from_on_disk_cache_stream
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use rustc_ast as ast;
|
|||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
|
||||
use rustc_middle::hir::exports::Export;
|
||||
|
@ -487,6 +488,10 @@ impl CrateStore for CStore {
|
|||
self.get_crate_data(def.krate).def_key(def.index)
|
||||
}
|
||||
|
||||
fn def_kind(&self, def: DefId) -> DefKind {
|
||||
self.get_crate_data(def.krate).def_kind(def.index)
|
||||
}
|
||||
|
||||
fn def_path(&self, def: DefId) -> DefPath {
|
||||
self.get_crate_data(def.krate).def_path(def.index)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt};
|
|||
use rustc_serialize::{opaque, Encodable, Encoder};
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_span::hygiene::{ExpnDataEncodeMode, HygieneEncodeContext};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||
use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
|
@ -436,8 +435,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
|
||||
fn encode_info_for_items(&mut self) {
|
||||
let krate = self.tcx.hir().krate();
|
||||
let vis = Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public };
|
||||
self.encode_info_for_mod(hir::CRATE_HIR_ID, &krate.item.module, &krate.item.attrs, &vis);
|
||||
self.encode_info_for_mod(hir::CRATE_HIR_ID, &krate.item.module, &krate.item.attrs);
|
||||
|
||||
// Proc-macro crates only export proc-macro items, which are looked
|
||||
// up using `proc_macro_data`
|
||||
|
@ -739,12 +737,8 @@ impl EncodeContext<'a, 'tcx> {
|
|||
is_non_exhaustive: variant.is_field_list_non_exhaustive(),
|
||||
};
|
||||
|
||||
let enum_id = tcx.hir().local_def_id_to_hir_id(def.did.expect_local());
|
||||
let enum_vis = &tcx.hir().expect_item(enum_id).vis;
|
||||
|
||||
record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data)));
|
||||
record!(self.tables.visibility[def_id] <-
|
||||
ty::Visibility::from_hir(enum_vis, enum_id, self.tcx));
|
||||
record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
|
||||
record!(self.tables.span[def_id] <- self.tcx.def_span(def_id));
|
||||
record!(self.tables.attributes[def_id] <- &self.tcx.get_attrs(def_id)[..]);
|
||||
record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id));
|
||||
|
@ -785,17 +779,8 @@ impl EncodeContext<'a, 'tcx> {
|
|||
is_non_exhaustive: variant.is_field_list_non_exhaustive(),
|
||||
};
|
||||
|
||||
// Variant constructors have the same visibility as the parent enums, unless marked as
|
||||
// non-exhaustive, in which case they are lowered to `pub(crate)`.
|
||||
let enum_id = tcx.hir().local_def_id_to_hir_id(def.did.expect_local());
|
||||
let enum_vis = &tcx.hir().expect_item(enum_id).vis;
|
||||
let mut ctor_vis = ty::Visibility::from_hir(enum_vis, enum_id, tcx);
|
||||
if variant.is_field_list_non_exhaustive() && ctor_vis == ty::Visibility::Public {
|
||||
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
|
||||
}
|
||||
|
||||
record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data)));
|
||||
record!(self.tables.visibility[def_id] <- ctor_vis);
|
||||
record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
|
||||
record!(self.tables.span[def_id] <- self.tcx.def_span(def_id));
|
||||
self.encode_stability(def_id);
|
||||
self.encode_deprecation(def_id);
|
||||
|
@ -811,13 +796,7 @@ impl EncodeContext<'a, 'tcx> {
|
|||
self.encode_promoted_mir(def_id.expect_local());
|
||||
}
|
||||
|
||||
fn encode_info_for_mod(
|
||||
&mut self,
|
||||
id: hir::HirId,
|
||||
md: &hir::Mod<'_>,
|
||||
attrs: &[ast::Attribute],
|
||||
vis: &hir::Visibility<'_>,
|
||||
) {
|
||||
fn encode_info_for_mod(&mut self, id: hir::HirId, md: &hir::Mod<'_>, attrs: &[ast::Attribute]) {
|
||||
let tcx = self.tcx;
|
||||
let local_def_id = tcx.hir().local_def_id(id);
|
||||
let def_id = local_def_id.to_def_id();
|
||||
|
@ -850,7 +829,7 @@ impl EncodeContext<'a, 'tcx> {
|
|||
};
|
||||
|
||||
record!(self.tables.kind[def_id] <- EntryKind::Mod(self.lazy(data)));
|
||||
record!(self.tables.visibility[def_id] <- ty::Visibility::from_hir(vis, id, self.tcx));
|
||||
record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
|
||||
record!(self.tables.span[def_id] <- self.tcx.def_span(def_id));
|
||||
record!(self.tables.attributes[def_id] <- attrs);
|
||||
if self.is_proc_macro {
|
||||
|
@ -881,7 +860,7 @@ impl EncodeContext<'a, 'tcx> {
|
|||
let variant_data = tcx.hir().expect_variant_data(variant_id);
|
||||
|
||||
record!(self.tables.kind[def_id] <- EntryKind::Field);
|
||||
record!(self.tables.visibility[def_id] <- field.vis);
|
||||
record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
|
||||
record!(self.tables.span[def_id] <- self.tcx.def_span(def_id));
|
||||
record!(self.tables.attributes[def_id] <- variant_data.fields()[field_index].attrs);
|
||||
record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id));
|
||||
|
@ -906,25 +885,8 @@ impl EncodeContext<'a, 'tcx> {
|
|||
is_non_exhaustive: variant.is_field_list_non_exhaustive(),
|
||||
};
|
||||
|
||||
let struct_id = tcx.hir().local_def_id_to_hir_id(adt_def.did.expect_local());
|
||||
let struct_vis = &tcx.hir().expect_item(struct_id).vis;
|
||||
let mut ctor_vis = ty::Visibility::from_hir(struct_vis, struct_id, tcx);
|
||||
for field in &variant.fields {
|
||||
if ctor_vis.is_at_least(field.vis, tcx) {
|
||||
ctor_vis = field.vis;
|
||||
}
|
||||
}
|
||||
|
||||
// If the structure is marked as non_exhaustive then lower the visibility
|
||||
// to within the crate.
|
||||
if adt_def.non_enum_variant().is_field_list_non_exhaustive()
|
||||
&& ctor_vis == ty::Visibility::Public
|
||||
{
|
||||
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
|
||||
}
|
||||
|
||||
record!(self.tables.kind[def_id] <- EntryKind::Struct(self.lazy(data), adt_def.repr));
|
||||
record!(self.tables.visibility[def_id] <- ctor_vis);
|
||||
record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
|
||||
record!(self.tables.span[def_id] <- self.tcx.def_span(def_id));
|
||||
record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id));
|
||||
self.encode_stability(def_id);
|
||||
|
@ -1030,7 +992,7 @@ impl EncodeContext<'a, 'tcx> {
|
|||
EntryKind::AssocType(container)
|
||||
}
|
||||
});
|
||||
record!(self.tables.visibility[def_id] <- trait_item.vis);
|
||||
record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
|
||||
record!(self.tables.span[def_id] <- ast_item.span);
|
||||
record!(self.tables.attributes[def_id] <- ast_item.attrs);
|
||||
self.encode_ident_span(def_id, ast_item.ident);
|
||||
|
@ -1112,7 +1074,7 @@ impl EncodeContext<'a, 'tcx> {
|
|||
}
|
||||
ty::AssocKind::Type => EntryKind::AssocType(container)
|
||||
});
|
||||
record!(self.tables.visibility[def_id] <- impl_item.vis);
|
||||
record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
|
||||
record!(self.tables.span[def_id] <- ast_item.span);
|
||||
record!(self.tables.attributes[def_id] <- ast_item.attrs);
|
||||
self.encode_ident_span(def_id, impl_item.ident);
|
||||
|
@ -1261,7 +1223,7 @@ impl EncodeContext<'a, 'tcx> {
|
|||
EntryKind::Fn(self.lazy(data))
|
||||
}
|
||||
hir::ItemKind::Mod(ref m) => {
|
||||
return self.encode_info_for_mod(item.hir_id, m, &item.attrs, &item.vis);
|
||||
return self.encode_info_for_mod(item.hir_id, m, &item.attrs);
|
||||
}
|
||||
hir::ItemKind::ForeignMod(_) => EntryKind::ForeignMod,
|
||||
hir::ItemKind::GlobalAsm(..) => EntryKind::GlobalAsm,
|
||||
|
@ -1352,8 +1314,7 @@ impl EncodeContext<'a, 'tcx> {
|
|||
hir::ItemKind::ExternCrate(_) |
|
||||
hir::ItemKind::Use(..) => bug!("cannot encode info for item {:?}", item),
|
||||
});
|
||||
record!(self.tables.visibility[def_id] <-
|
||||
ty::Visibility::from_hir(&item.vis, item.hir_id, tcx));
|
||||
record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
|
||||
record!(self.tables.span[def_id] <- self.tcx.def_span(def_id));
|
||||
record!(self.tables.attributes[def_id] <- item.attrs);
|
||||
record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id));
|
||||
|
@ -1470,7 +1431,7 @@ impl EncodeContext<'a, 'tcx> {
|
|||
fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef<'_>) {
|
||||
let def_id = self.tcx.hir().local_def_id(macro_def.hir_id).to_def_id();
|
||||
record!(self.tables.kind[def_id] <- EntryKind::MacroDef(self.lazy(macro_def.ast.clone())));
|
||||
record!(self.tables.visibility[def_id] <- ty::Visibility::Public);
|
||||
record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
|
||||
record!(self.tables.span[def_id] <- macro_def.span);
|
||||
record!(self.tables.attributes[def_id] <- macro_def.attrs);
|
||||
self.encode_ident_span(def_id, macro_def.ident);
|
||||
|
@ -1480,7 +1441,6 @@ impl EncodeContext<'a, 'tcx> {
|
|||
|
||||
fn encode_info_for_generic_param(&mut self, def_id: DefId, kind: EntryKind, encode_type: bool) {
|
||||
record!(self.tables.kind[def_id] <- kind);
|
||||
record!(self.tables.visibility[def_id] <- ty::Visibility::Public);
|
||||
record!(self.tables.span[def_id] <- self.tcx.def_span(def_id));
|
||||
if encode_type {
|
||||
self.encode_item_type(def_id);
|
||||
|
@ -1505,7 +1465,6 @@ impl EncodeContext<'a, 'tcx> {
|
|||
|
||||
_ => bug!("closure that is neither generator nor closure"),
|
||||
});
|
||||
record!(self.tables.visibility[def_id.to_def_id()] <- ty::Visibility::Public);
|
||||
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());
|
||||
|
@ -1525,7 +1484,6 @@ impl EncodeContext<'a, 'tcx> {
|
|||
let qualifs = self.tcx.mir_const_qualif(def_id);
|
||||
|
||||
record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::AnonConst(qualifs, const_data));
|
||||
record!(self.tables.visibility[def_id.to_def_id()] <- ty::Visibility::Public);
|
||||
record!(self.tables.span[def_id.to_def_id()] <- self.tcx.def_span(def_id));
|
||||
self.encode_item_type(def_id.to_def_id());
|
||||
self.encode_generics(def_id.to_def_id());
|
||||
|
@ -1762,8 +1720,7 @@ impl EncodeContext<'a, 'tcx> {
|
|||
hir::ForeignItemKind::Static(_, hir::Mutability::Not) => EntryKind::ForeignImmStatic,
|
||||
hir::ForeignItemKind::Type => EntryKind::ForeignType,
|
||||
});
|
||||
record!(self.tables.visibility[def_id] <-
|
||||
ty::Visibility::from_hir(&nitem.vis, nitem.hir_id, self.tcx));
|
||||
record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id));
|
||||
record!(self.tables.span[def_id] <- nitem.span);
|
||||
record!(self.tables.attributes[def_id] <- nitem.attrs);
|
||||
self.encode_ident_span(def_id, nitem.ident);
|
||||
|
|
|
@ -28,5 +28,5 @@ rustc_ast = { path = "../rustc_ast" }
|
|||
rustc_span = { path = "../rustc_span" }
|
||||
chalk-ir = "0.32.0"
|
||||
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
|
||||
measureme = "0.7.1"
|
||||
measureme = "9.0.0"
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
|
|
|
@ -360,9 +360,27 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
|
|||
}
|
||||
|
||||
fn visit_generic_param(&mut self, param: &'hir GenericParam<'hir>) {
|
||||
if let hir::GenericParamKind::Type {
|
||||
synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
|
||||
..
|
||||
} = param.kind
|
||||
{
|
||||
debug_assert_eq!(
|
||||
param.hir_id.owner,
|
||||
self.definitions.opt_hir_id_to_local_def_id(param.hir_id).unwrap()
|
||||
);
|
||||
self.with_dep_node_owner(param.hir_id.owner, param, |this, hash| {
|
||||
this.insert_with_hash(param.span, param.hir_id, Node::GenericParam(param), hash);
|
||||
|
||||
this.with_parent(param.hir_id, |this| {
|
||||
intravisit::walk_generic_param(this, param);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
self.insert(param.span, param.hir_id, Node::GenericParam(param));
|
||||
intravisit::walk_generic_param(self, param);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) {
|
||||
debug_assert_eq!(
|
||||
|
|
|
@ -816,7 +816,7 @@ impl<'hir> Map<'hir> {
|
|||
Some(Node::Variant(ref v)) => Some(&v.attrs[..]),
|
||||
Some(Node::Field(ref f)) => Some(&f.attrs[..]),
|
||||
Some(Node::Expr(ref e)) => Some(&*e.attrs),
|
||||
Some(Node::Stmt(ref s)) => Some(s.kind.attrs()),
|
||||
Some(Node::Stmt(ref s)) => Some(s.kind.attrs(|id| self.item(id.id))),
|
||||
Some(Node::Arm(ref a)) => Some(&*a.attrs),
|
||||
Some(Node::GenericParam(param)) => Some(¶m.attrs[..]),
|
||||
// Unit/tuple structs/variants take the attributes straight from
|
||||
|
|
|
@ -103,7 +103,7 @@ impl<'tcx> Place<'tcx> {
|
|||
|
||||
/// Returns the type of this `Place` after all projections have been applied.
|
||||
pub fn ty(&self) -> Ty<'tcx> {
|
||||
self.projections.last().map_or_else(|| self.base_ty, |proj| proj.ty)
|
||||
self.projections.last().map_or(self.base_ty, |proj| proj.ty)
|
||||
}
|
||||
|
||||
/// Returns the type of this `Place` immediately before `projection_index`th projection
|
||||
|
|
|
@ -40,11 +40,12 @@ impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> {
|
|||
debug_assert!(!attr.ident().map_or(false, |ident| self.is_ignored_attr(ident.name)));
|
||||
debug_assert!(!attr.is_doc_comment());
|
||||
|
||||
let ast::Attribute { kind, id: _, style, span } = attr;
|
||||
let ast::Attribute { kind, id: _, style, span, tokens } = attr;
|
||||
if let ast::AttrKind::Normal(item) = kind {
|
||||
item.hash_stable(self, hasher);
|
||||
style.hash_stable(self, hasher);
|
||||
span.hash_stable(self, hasher);
|
||||
tokens.as_ref().expect_none("Tokens should have been removed during lowering!");
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
|
|
@ -175,19 +175,15 @@ impl<'tcx> UnifyKey for ty::ConstVid<'tcx> {
|
|||
impl<'tcx> UnifyValue for ConstVarValue<'tcx> {
|
||||
type Error = (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>);
|
||||
|
||||
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
|
||||
let (val, span) = match (value1.val, value2.val) {
|
||||
fn unify_values(&value1: &Self, &value2: &Self) -> Result<Self, Self::Error> {
|
||||
Ok(match (value1.val, value2.val) {
|
||||
(ConstVariableValue::Known { .. }, ConstVariableValue::Known { .. }) => {
|
||||
bug!("equating two const variables, both of which have known values")
|
||||
}
|
||||
|
||||
// If one side is known, prefer that one.
|
||||
(ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => {
|
||||
(value1.val, value1.origin.span)
|
||||
}
|
||||
(ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => {
|
||||
(value2.val, value2.origin.span)
|
||||
}
|
||||
(ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => value1,
|
||||
(ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => value2,
|
||||
|
||||
// If both sides are *unknown*, it hardly matters, does it?
|
||||
(
|
||||
|
@ -200,16 +196,11 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> {
|
|||
// universe is the minimum of the two universes, because that is
|
||||
// the one which contains the fewest names in scope.
|
||||
let universe = cmp::min(universe1, universe2);
|
||||
(ConstVariableValue::Unknown { universe }, value1.origin.span)
|
||||
ConstVarValue {
|
||||
val: ConstVariableValue::Unknown { universe },
|
||||
origin: value1.origin,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ConstVarValue {
|
||||
origin: ConstVariableOrigin {
|
||||
kind: ConstVariableOriginKind::ConstInference,
|
||||
span: span,
|
||||
},
|
||||
val,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ macro_rules! CloneLiftImpls {
|
|||
$(
|
||||
impl<$tcx> $crate::ty::Lift<$tcx> for $ty {
|
||||
type Lifted = Self;
|
||||
fn lift_to_tcx(&self, _: $crate::ty::TyCtxt<$tcx>) -> Option<Self> {
|
||||
Some(Clone::clone(self))
|
||||
fn lift_to_tcx(self, _: $crate::ty::TyCtxt<$tcx>) -> Option<Self> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
)+
|
||||
|
|
|
@ -8,6 +8,7 @@ use rustc_ast as ast;
|
|||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::{self, MetadataRef};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
|
||||
use rustc_macros::HashStable;
|
||||
|
@ -185,6 +186,7 @@ pub trait CrateStore {
|
|||
|
||||
// resolve
|
||||
fn def_key(&self, def: DefId) -> DefKey;
|
||||
fn def_kind(&self, def: DefId) -> DefKind;
|
||||
fn def_path(&self, def: DefId) -> DefPath;
|
||||
fn def_path_hash(&self, def: DefId) -> DefPathHash;
|
||||
fn all_def_path_hashes_and_def_ids(&self, cnum: CrateNum) -> Vec<(DefPathHash, DefId)>;
|
||||
|
|
|
@ -256,24 +256,12 @@ pub enum EvalResult {
|
|||
}
|
||||
|
||||
// See issue #38412.
|
||||
fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, mut def_id: DefId) -> bool {
|
||||
// Check if `def_id` is a trait method.
|
||||
match tcx.def_kind(def_id) {
|
||||
DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
|
||||
if let ty::TraitContainer(trait_def_id) = tcx.associated_item(def_id).container {
|
||||
// Trait methods do not declare visibility (even
|
||||
// for visibility info in cstore). Use containing
|
||||
// trait instead, so methods of `pub` traits are
|
||||
// themselves considered `pub`.
|
||||
def_id = trait_def_id;
|
||||
fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
if tcx.def_kind(def_id) == DefKind::TyParam {
|
||||
// Have no visibility, considered public for the purpose of this check.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let visibility = tcx.visibility(def_id);
|
||||
|
||||
match visibility {
|
||||
match tcx.visibility(def_id) {
|
||||
// Must check stability for `pub` items.
|
||||
ty::Visibility::Public => false,
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ impl<'tcx> MirSource<'tcx> {
|
|||
/// The lowered representation of a single function.
|
||||
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable)]
|
||||
pub struct Body<'tcx> {
|
||||
/// A list of basic blocks. References to basic block use a newtyped index type `BasicBlock`
|
||||
/// A list of basic blocks. References to basic block use a newtyped index type [`BasicBlock`]
|
||||
/// that indexes into this vector.
|
||||
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
|
||||
|
@ -821,9 +821,6 @@ pub struct LocalDecl<'tcx> {
|
|||
/// flag drop flags to avoid triggering this check as they are introduced
|
||||
/// after typeck.
|
||||
///
|
||||
/// Unsafety checking will also ignore dereferences of these locals,
|
||||
/// so they can be used for raw pointers only used in a desugaring.
|
||||
///
|
||||
/// This should be sound because the drop flags are fully algebraic, and
|
||||
/// therefore don't affect the OIBIT or outlives properties of the
|
||||
/// generator.
|
||||
|
@ -1010,13 +1007,13 @@ impl<'tcx> LocalDecl<'tcx> {
|
|||
}
|
||||
|
||||
/// Returns `Some` if this is a reference to a static item that is used to
|
||||
/// access that static
|
||||
/// access that static.
|
||||
pub fn is_ref_to_static(&self) -> bool {
|
||||
matches!(self.local_info, Some(box LocalInfo::StaticRef { .. }))
|
||||
}
|
||||
|
||||
/// Returns `Some` if this is a reference to a static item that is used to
|
||||
/// access that static
|
||||
/// Returns `Some` if this is a reference to a thread-local static item that is used to
|
||||
/// access that static.
|
||||
pub fn is_ref_to_thread_local(&self) -> bool {
|
||||
match self.local_info {
|
||||
Some(box LocalInfo::StaticRef { is_thread_local, .. }) => is_thread_local,
|
||||
|
@ -1106,6 +1103,9 @@ rustc_index::newtype_index! {
|
|||
/// 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.
|
||||
///
|
||||
/// Note that this type is just an index into [`Body.basic_blocks`](Body::basic_blocks);
|
||||
/// the actual data that a basic block holds is in [`BasicBlockData`].
|
||||
///
|
||||
/// Read more about basic blocks in the [rustc-dev-guide][guide-mir].
|
||||
///
|
||||
/// [CFG]: https://rustc-dev-guide.rust-lang.org/appendix/background.html#cfg
|
||||
|
@ -2210,7 +2210,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
|
||||
let name = ty::tls::with(|tcx| {
|
||||
let mut name = String::new();
|
||||
let substs = tcx.lift(&substs).expect("could not lift for printing");
|
||||
let substs = tcx.lift(substs).expect("could not lift for printing");
|
||||
FmtPrinter::new(tcx, &mut name, Namespace::ValueNS)
|
||||
.print_def_path(variant_def.def_id, substs)?;
|
||||
Ok(name)
|
||||
|
@ -2233,7 +2233,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
if let Some(def_id) = def_id.as_local() {
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
let name = if tcx.sess.opts.debugging_opts.span_free_formats {
|
||||
let substs = tcx.lift(&substs).unwrap();
|
||||
let substs = tcx.lift(substs).unwrap();
|
||||
format!(
|
||||
"[closure@{}]",
|
||||
tcx.def_path_str_with_substs(def_id.to_def_id(), substs),
|
||||
|
@ -2527,7 +2527,7 @@ fn pretty_print_const(
|
|||
) -> fmt::Result {
|
||||
use crate::ty::print::PrettyPrinter;
|
||||
ty::tls::with(|tcx| {
|
||||
let literal = tcx.lift(&c).unwrap();
|
||||
let literal = tcx.lift(c).unwrap();
|
||||
let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS);
|
||||
cx.print_alloc_ids = true;
|
||||
cx.pretty_print_const(literal, print_types)?;
|
||||
|
|
|
@ -152,10 +152,14 @@ impl<'tcx> Rvalue<'tcx> {
|
|||
tcx.mk_ty(ty::Array(operand.ty(local_decls, tcx), count))
|
||||
}
|
||||
Rvalue::ThreadLocalRef(did) => {
|
||||
let static_ty = tcx.type_of(did);
|
||||
if tcx.is_mutable_static(did) {
|
||||
tcx.mk_mut_ptr(tcx.type_of(did))
|
||||
tcx.mk_mut_ptr(static_ty)
|
||||
} else if tcx.is_foreign_item(did) {
|
||||
tcx.mk_imm_ptr(static_ty)
|
||||
} else {
|
||||
tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.type_of(did))
|
||||
// FIXME: These things don't *really* have 'static lifetime.
|
||||
tcx.mk_imm_ref(tcx.lifetimes.re_static, static_ty)
|
||||
}
|
||||
}
|
||||
Rvalue::Ref(reg, bk, ref place) => {
|
||||
|
|
|
@ -535,7 +535,7 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
Goto { .. } => vec!["".into()],
|
||||
SwitchInt { ref targets, switch_ty, .. } => ty::tls::with(|tcx| {
|
||||
let param_env = ty::ParamEnv::empty();
|
||||
let switch_ty = tcx.lift(&switch_ty).unwrap();
|
||||
let switch_ty = tcx.lift(switch_ty).unwrap();
|
||||
let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
|
||||
targets
|
||||
.values
|
|
@ -1267,6 +1267,7 @@ rustc_queries! {
|
|||
|
||||
TypeChecking {
|
||||
query visibility(def_id: DefId) -> ty::Visibility {
|
||||
eval_always
|
||||
desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::mir::interpret::ErrorHandled;
|
|||
use crate::ty::subst::SubstsRef;
|
||||
use crate::ty::{self, AdtKind, Ty, TyCtxt};
|
||||
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
@ -342,6 +343,7 @@ static_assert_size!(ObligationCauseCode<'_>, 32);
|
|||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
|
||||
pub struct MatchExpressionArmCause<'tcx> {
|
||||
pub arm_span: Span,
|
||||
pub scrut_span: Span,
|
||||
pub semi_span: Option<Span>,
|
||||
pub source: hir::MatchSource,
|
||||
pub prior_arms: Vec<Span>,
|
||||
|
@ -646,13 +648,13 @@ impl ObjectSafetyViolation {
|
|||
ObjectSafetyViolation::SizedSelf(_) => "it requires `Self: Sized`".into(),
|
||||
ObjectSafetyViolation::SupertraitSelf(ref spans) => {
|
||||
if spans.iter().any(|sp| *sp != DUMMY_SP) {
|
||||
"it uses `Self` as a type parameter in this".into()
|
||||
"it uses `Self` as a type parameter".into()
|
||||
} else {
|
||||
"it cannot use `Self` as a type parameter in a supertrait or `where`-clause"
|
||||
.into()
|
||||
}
|
||||
}
|
||||
ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => {
|
||||
ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_, _, _), _) => {
|
||||
format!("associated function `{}` has no `self` parameter", name).into()
|
||||
}
|
||||
ObjectSafetyViolation::Method(
|
||||
|
@ -686,32 +688,65 @@ impl ObjectSafetyViolation {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn solution(&self) -> Option<(String, Option<(String, Span)>)> {
|
||||
Some(match *self {
|
||||
ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => {
|
||||
return None;
|
||||
}
|
||||
ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(sugg), _) => (
|
||||
format!(
|
||||
"consider turning `{}` into a method by giving it a `&self` argument or \
|
||||
constraining it so it does not apply to trait objects",
|
||||
pub fn solution(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||
match *self {
|
||||
ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => {}
|
||||
ObjectSafetyViolation::Method(
|
||||
name,
|
||||
MethodViolationCode::StaticMethod(sugg, self_span, has_args),
|
||||
_,
|
||||
) => {
|
||||
err.span_suggestion(
|
||||
self_span,
|
||||
&format!(
|
||||
"consider turning `{}` into a method by giving it a `&self` argument",
|
||||
name
|
||||
),
|
||||
sugg.map(|(sugg, sp)| (sugg.to_string(), sp)),
|
||||
format!("&self{}", if has_args { ", " } else { "" }),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
match sugg {
|
||||
Some((sugg, span)) => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
&format!(
|
||||
"alternatively, consider constraining `{}` so it does not apply to \
|
||||
trait objects",
|
||||
name
|
||||
),
|
||||
sugg.to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
err.help(&format!(
|
||||
"consider turning `{}` into a method by giving it a `&self` \
|
||||
argument or constraining it so it does not apply to trait objects",
|
||||
name
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
ObjectSafetyViolation::Method(
|
||||
name,
|
||||
MethodViolationCode::UndispatchableReceiver,
|
||||
span,
|
||||
) => (
|
||||
format!("consider changing method `{}`'s `self` parameter to be `&self`", name),
|
||||
Some(("&Self".to_string(), span)),
|
||||
) => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
&format!(
|
||||
"consider changing method `{}`'s `self` parameter to be `&self`",
|
||||
name
|
||||
),
|
||||
"&Self".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
ObjectSafetyViolation::AssocConst(name, _)
|
||||
| ObjectSafetyViolation::Method(name, ..) => {
|
||||
(format!("consider moving `{}` to another trait", name), None)
|
||||
err.help(&format!("consider moving `{}` to another trait", name));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn spans(&self) -> SmallVec<[Span; 1]> {
|
||||
|
@ -735,7 +770,7 @@ impl ObjectSafetyViolation {
|
|||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
|
||||
pub enum MethodViolationCode {
|
||||
/// e.g., `fn foo()`
|
||||
StaticMethod(Option<(&'static str, Span)>),
|
||||
StaticMethod(Option<(&'static str, Span)>, Span, bool /* has args */),
|
||||
|
||||
/// e.g., `fn foo(&self, x: Self)`
|
||||
ReferencesSelfInput(usize),
|
||||
|
|
|
@ -127,7 +127,10 @@ pub enum SelectionCandidate<'tcx> {
|
|||
|
||||
TraitAliasCandidate(DefId),
|
||||
|
||||
ObjectCandidate,
|
||||
/// Matching `dyn Trait` with a supertrait of `Trait`. The index is the
|
||||
/// position in the iterator returned by
|
||||
/// `rustc_infer::traits::util::supertraits`.
|
||||
ObjectCandidate(usize),
|
||||
|
||||
BuiltinObjectCandidate,
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ use crate::ty::{
|
|||
ExistentialPredicate, FloatVar, FloatVid, GenericParamDefKind, InferConst, InferTy, IntVar,
|
||||
IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate, PredicateInner, PredicateKind,
|
||||
ProjectionTy, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar,
|
||||
TyVid, TypeAndMut,
|
||||
TyVid, TypeAndMut, Visibility,
|
||||
};
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
|
@ -134,7 +134,7 @@ impl<'tcx> CtxtInterners<'tcx> {
|
|||
fn intern_predicate(&self, kind: PredicateKind<'tcx>) -> &'tcx PredicateInner<'tcx> {
|
||||
self.predicate
|
||||
.intern(kind, |kind| {
|
||||
let flags = super::flags::FlagComputation::for_predicate(&kind);
|
||||
let flags = super::flags::FlagComputation::for_predicate(kind);
|
||||
|
||||
let predicate_struct = PredicateInner {
|
||||
kind,
|
||||
|
@ -911,6 +911,9 @@ pub struct GlobalCtxt<'tcx> {
|
|||
/// Common consts, pre-interned for your convenience.
|
||||
pub consts: CommonConsts<'tcx>,
|
||||
|
||||
/// Visibilities produced by resolver.
|
||||
pub visibilities: FxHashMap<LocalDefId, Visibility>,
|
||||
|
||||
/// Resolutions of `extern crate` items produced by resolver.
|
||||
extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
|
||||
|
||||
|
@ -1057,7 +1060,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn lift<T: ?Sized + Lift<'tcx>>(self, value: &T) -> Option<T::Lifted> {
|
||||
pub fn lift<T: Lift<'tcx>>(self, value: T) -> Option<T::Lifted> {
|
||||
value.lift_to_tcx(self)
|
||||
}
|
||||
|
||||
|
@ -1124,6 +1127,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
types: common_types,
|
||||
lifetimes: common_lifetimes,
|
||||
consts: common_consts,
|
||||
visibilities: resolutions.visibilities,
|
||||
extern_crate_map: resolutions.extern_crate_map,
|
||||
trait_map,
|
||||
export_map: resolutions.export_map,
|
||||
|
@ -1565,16 +1569,16 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
/// e.g., `()` or `u8`, was interned in a different context.
|
||||
pub trait Lift<'tcx>: fmt::Debug {
|
||||
type Lifted: fmt::Debug + 'tcx;
|
||||
fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted>;
|
||||
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted>;
|
||||
}
|
||||
|
||||
macro_rules! nop_lift {
|
||||
($set:ident; $ty:ty => $lifted:ty) => {
|
||||
impl<'a, 'tcx> Lift<'tcx> for $ty {
|
||||
type Lifted = $lifted;
|
||||
fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
|
||||
if tcx.interners.$set.contains_pointer_to(&Interned(*self)) {
|
||||
Some(unsafe { mem::transmute(*self) })
|
||||
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
|
||||
if tcx.interners.$set.contains_pointer_to(&Interned(self)) {
|
||||
Some(unsafe { mem::transmute(self) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -1587,12 +1591,12 @@ macro_rules! nop_list_lift {
|
|||
($set:ident; $ty:ty => $lifted:ty) => {
|
||||
impl<'a, 'tcx> Lift<'tcx> for &'a List<$ty> {
|
||||
type Lifted = &'tcx List<$lifted>;
|
||||
fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
|
||||
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
|
||||
if self.is_empty() {
|
||||
return Some(List::empty());
|
||||
}
|
||||
if tcx.interners.$set.contains_pointer_to(&Interned(*self)) {
|
||||
Some(unsafe { mem::transmute(*self) })
|
||||
if tcx.interners.$set.contains_pointer_to(&Interned(self)) {
|
||||
Some(unsafe { mem::transmute(self) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -2032,13 +2036,13 @@ direct_interners! {
|
|||
|
||||
macro_rules! slice_interners {
|
||||
($($field:ident: $method:ident($ty:ty)),+ $(,)?) => (
|
||||
$(impl<'tcx> TyCtxt<'tcx> {
|
||||
pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> {
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
$(pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> {
|
||||
self.interners.$field.intern_ref(v, || {
|
||||
Interned(List::from_arena(&*self.arena, v))
|
||||
}).0
|
||||
}
|
||||
})+
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -229,7 +229,7 @@ impl<'tcx> ty::TyS<'tcx> {
|
|||
ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(),
|
||||
ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(),
|
||||
ty::Array(t, n) => {
|
||||
let n = tcx.lift(&n).unwrap();
|
||||
let n = tcx.lift(n).unwrap();
|
||||
match n.try_eval_usize(tcx, ty::ParamEnv::empty()) {
|
||||
_ if t.is_simple_ty() => format!("array `{}`", self).into(),
|
||||
Some(n) => format!("array of {} element{}", n, pluralize!(n)).into(),
|
||||
|
|
|
@ -22,7 +22,7 @@ impl FlagComputation {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn for_predicate(kind: &ty::PredicateKind<'_>) -> FlagComputation {
|
||||
pub fn for_predicate(kind: ty::PredicateKind<'_>) -> FlagComputation {
|
||||
let mut result = FlagComputation::new();
|
||||
result.add_predicate_kind(kind);
|
||||
result
|
||||
|
@ -53,7 +53,14 @@ impl FlagComputation {
|
|||
|
||||
/// Adds the flags/depth from a set of types that appear within the current type, but within a
|
||||
/// region binder.
|
||||
fn add_bound_computation(&mut self, computation: FlagComputation) {
|
||||
fn bound_computation<T, F>(&mut self, value: ty::Binder<T>, f: F)
|
||||
where
|
||||
F: FnOnce(&mut Self, T),
|
||||
{
|
||||
let mut computation = FlagComputation::new();
|
||||
|
||||
f(&mut computation, value.skip_binder());
|
||||
|
||||
self.add_flags(computation.flags);
|
||||
|
||||
// The types that contributed to `computation` occurred within
|
||||
|
@ -101,9 +108,7 @@ impl FlagComputation {
|
|||
}
|
||||
|
||||
&ty::GeneratorWitness(ts) => {
|
||||
let mut computation = FlagComputation::new();
|
||||
computation.add_tys(ts.skip_binder());
|
||||
self.add_bound_computation(computation);
|
||||
self.bound_computation(ts, |flags, ts| flags.add_tys(ts));
|
||||
}
|
||||
|
||||
&ty::Closure(_, substs) => {
|
||||
|
@ -154,20 +159,21 @@ impl FlagComputation {
|
|||
self.add_substs(substs);
|
||||
}
|
||||
|
||||
&ty::Dynamic(ref obj, r) => {
|
||||
let mut computation = FlagComputation::new();
|
||||
for predicate in obj.skip_binder().iter() {
|
||||
&ty::Dynamic(obj, r) => {
|
||||
self.bound_computation(obj, |computation, obj| {
|
||||
for predicate in obj.iter() {
|
||||
match predicate {
|
||||
ty::ExistentialPredicate::Trait(tr) => computation.add_substs(tr.substs),
|
||||
ty::ExistentialPredicate::Trait(tr) => {
|
||||
computation.add_substs(tr.substs)
|
||||
}
|
||||
ty::ExistentialPredicate::Projection(p) => {
|
||||
let mut proj_computation = FlagComputation::new();
|
||||
proj_computation.add_existential_projection(&p);
|
||||
self.add_bound_computation(proj_computation);
|
||||
computation.add_existential_projection(&p);
|
||||
}
|
||||
ty::ExistentialPredicate::AutoTrait(_) => {}
|
||||
}
|
||||
}
|
||||
self.add_bound_computation(computation);
|
||||
});
|
||||
|
||||
self.add_region(r);
|
||||
}
|
||||
|
||||
|
@ -195,22 +201,21 @@ impl FlagComputation {
|
|||
self.add_substs(substs);
|
||||
}
|
||||
|
||||
&ty::FnPtr(f) => {
|
||||
self.add_fn_sig(f);
|
||||
}
|
||||
&ty::FnPtr(fn_sig) => self.bound_computation(fn_sig, |computation, fn_sig| {
|
||||
computation.add_tys(fn_sig.inputs());
|
||||
computation.add_ty(fn_sig.output());
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_predicate_kind(&mut self, kind: &ty::PredicateKind<'_>) {
|
||||
fn add_predicate_kind(&mut self, kind: ty::PredicateKind<'_>) {
|
||||
match kind {
|
||||
ty::PredicateKind::ForAll(binder) => {
|
||||
let mut computation = FlagComputation::new();
|
||||
|
||||
computation.add_predicate_atom(binder.skip_binder());
|
||||
|
||||
self.add_bound_computation(computation);
|
||||
self.bound_computation(binder, |computation, atom| {
|
||||
computation.add_predicate_atom(atom)
|
||||
});
|
||||
}
|
||||
&ty::PredicateKind::Atom(atom) => self.add_predicate_atom(atom),
|
||||
ty::PredicateKind::Atom(atom) => self.add_predicate_atom(atom),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,15 +271,6 @@ impl FlagComputation {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_fn_sig(&mut self, fn_sig: ty::PolyFnSig<'_>) {
|
||||
let mut computation = FlagComputation::new();
|
||||
|
||||
computation.add_tys(fn_sig.skip_binder().inputs());
|
||||
computation.add_ty(fn_sig.skip_binder().output());
|
||||
|
||||
self.add_bound_computation(computation);
|
||||
}
|
||||
|
||||
fn add_region(&mut self, r: ty::Region<'_>) {
|
||||
self.add_flags(r.type_flags());
|
||||
if let ty::ReLateBound(debruijn, _) = *r {
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
//!
|
||||
//! These methods return true to indicate that the visitor has found what it is
|
||||
//! looking for, and does not need to visit anything else.
|
||||
|
||||
use crate::ty::structural_impls::PredicateVisitor;
|
||||
use crate::ty::{self, flags::FlagComputation, Binder, Ty, TyCtxt, TypeFlags};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
@ -211,6 +209,10 @@ pub trait TypeVisitor<'tcx>: Sized {
|
|||
fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
|
||||
c.super_visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> bool {
|
||||
p.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -868,9 +870,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor {
|
|||
_ => ct.super_visit_with(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> PredicateVisitor<'tcx> for HasEscapingVarsVisitor {
|
||||
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool {
|
||||
predicate.inner.outer_exclusive_binder > self.outer_index
|
||||
}
|
||||
|
@ -903,9 +903,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
|
|||
debug!("HasTypeFlagsVisitor: c={:?} c.flags={:?} self.flags={:?}", c, flags, self.flags);
|
||||
flags.intersects(self.flags)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> PredicateVisitor<'tcx> for HasTypeFlagsVisitor {
|
||||
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool {
|
||||
debug!(
|
||||
"HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}",
|
||||
|
@ -914,6 +912,7 @@ impl<'tcx> PredicateVisitor<'tcx> for HasTypeFlagsVisitor {
|
|||
predicate.inner.flags.intersects(self.flags)
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects all the late-bound regions at the innermost binding level
|
||||
/// into a hash set.
|
||||
struct LateBoundRegionsCollector {
|
||||
|
|
|
@ -258,7 +258,7 @@ impl<'tcx> InstanceDef<'tcx> {
|
|||
impl<'tcx> fmt::Display for Instance<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
let substs = tcx.lift(&self.substs).expect("could not lift for printing");
|
||||
let substs = tcx.lift(self.substs).expect("could not lift for printing");
|
||||
FmtPrinter::new(tcx, &mut *f, Namespace::ValueNS)
|
||||
.print_def_path(self.def_id(), substs)?;
|
||||
Ok(())
|
||||
|
|
|
@ -125,6 +125,7 @@ mod sty;
|
|||
pub struct ResolverOutputs {
|
||||
pub definitions: rustc_hir::definitions::Definitions,
|
||||
pub cstore: Box<CrateStoreDyn>,
|
||||
pub visibilities: FxHashMap<LocalDefId, Visibility>,
|
||||
pub extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
|
||||
pub maybe_unused_trait_imports: FxHashSet<LocalDefId>,
|
||||
pub maybe_unused_extern_crates: Vec<(LocalDefId, Span)>,
|
||||
|
@ -1056,9 +1057,21 @@ impl<'tcx> Predicate<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts this to a `Binder<PredicateAtom<'tcx>>`. If the value was an
|
||||
/// `Atom`, then it is not allowed to contain escaping bound vars.
|
||||
pub fn bound_atom(self) -> Binder<PredicateAtom<'tcx>> {
|
||||
match self.kind() {
|
||||
&PredicateKind::ForAll(binder) => binder,
|
||||
&PredicateKind::Atom(atom) => {
|
||||
debug_assert!(!atom.has_escaping_bound_vars());
|
||||
Binder::dummy(atom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows using a `Binder<PredicateAtom<'tcx>>` even if the given predicate previously
|
||||
/// contained unbound variables by shifting these variables outwards.
|
||||
pub fn bound_atom(self, tcx: TyCtxt<'tcx>) -> Binder<PredicateAtom<'tcx>> {
|
||||
pub fn bound_atom_with_opt_escaping(self, tcx: TyCtxt<'tcx>) -> Binder<PredicateAtom<'tcx>> {
|
||||
match self.kind() {
|
||||
&PredicateKind::ForAll(binder) => binder,
|
||||
&PredicateKind::Atom(atom) => Binder::wrap_nonbinding(tcx, atom),
|
||||
|
@ -2436,8 +2449,10 @@ impl<'tcx> AdtDef {
|
|||
self.variants.iter().flat_map(|v| v.fields.iter())
|
||||
}
|
||||
|
||||
/// Whether the ADT lacks fields. Note that this includes uninhabited enums,
|
||||
/// e.g., `enum Void {}` is considered payload free as well.
|
||||
pub fn is_payloadfree(&self) -> bool {
|
||||
!self.variants.is_empty() && self.variants.iter().all(|v| v.fields.is_empty())
|
||||
self.variants.iter().all(|v| v.fields.is_empty())
|
||||
}
|
||||
|
||||
/// Return a `VariantDef` given a variant id.
|
||||
|
|
|
@ -618,10 +618,9 @@ pub trait PrettyPrinter<'tcx>:
|
|||
// may contain unbound variables. We therefore do this manually.
|
||||
//
|
||||
// FIXME(lcnr): Find out why exactly this is the case :)
|
||||
if let ty::PredicateAtom::Trait(pred, _) =
|
||||
predicate.bound_atom(self.tcx()).skip_binder()
|
||||
{
|
||||
let trait_ref = ty::Binder::bind(pred.trait_ref);
|
||||
let bound_predicate = predicate.bound_atom_with_opt_escaping(self.tcx());
|
||||
if let ty::PredicateAtom::Trait(pred, _) = bound_predicate.skip_binder() {
|
||||
let trait_ref = bound_predicate.rebind(pred.trait_ref);
|
||||
// Don't print +Sized, but rather +?Sized if absent.
|
||||
if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() {
|
||||
is_sized = true;
|
||||
|
@ -1849,7 +1848,7 @@ macro_rules! forward_display_to_print {
|
|||
$(impl fmt::Display for $ty {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
tcx.lift(self)
|
||||
tcx.lift(*self)
|
||||
.expect("could not lift for printing")
|
||||
.print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?;
|
||||
Ok(())
|
||||
|
|
|
@ -34,7 +34,6 @@ use crate::ty::util::AlwaysRequiresDrop;
|
|||
use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::profiling::ProfileCategory::*;
|
||||
use rustc_data_structures::stable_hasher::StableVec;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
@ -169,7 +168,9 @@ pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool
|
|||
return false;
|
||||
}
|
||||
|
||||
rustc_dep_node_force!([dep_node, tcx]
|
||||
macro_rules! force_from_dep_node {
|
||||
($($(#[$attr:meta])* [$($modifiers:tt)*] $name:ident($K:ty),)*) => {
|
||||
match dep_node.kind {
|
||||
// These are inputs that are expected to be pre-allocated and that
|
||||
// should therefore always be red or green already.
|
||||
DepKind::CrateMetadata |
|
||||
|
@ -179,16 +180,59 @@ pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool
|
|||
|
||||
// We don't have enough information to reconstruct the query key of
|
||||
// these.
|
||||
DepKind::CompileCodegenUnit => {
|
||||
DepKind::CompileCodegenUnit |
|
||||
|
||||
// Forcing this makes no sense.
|
||||
DepKind::Null => {
|
||||
bug!("force_from_dep_node: encountered {:?}", dep_node)
|
||||
}
|
||||
|
||||
$(DepKind::$name => {
|
||||
debug_assert!(<$K as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key());
|
||||
|
||||
if let Some(key) = <$K as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node) {
|
||||
force_query::<queries::$name<'_>, _>(
|
||||
tcx,
|
||||
key,
|
||||
DUMMY_SP,
|
||||
*dep_node
|
||||
);
|
||||
return true;
|
||||
}
|
||||
})*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rustc_dep_node_append! { [force_from_dep_node!][] }
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) {
|
||||
rustc_dep_node_try_load_from_on_disk_cache!(dep_node, tcx)
|
||||
macro_rules! try_load_from_on_disk_cache {
|
||||
($($name:ident,)*) => {
|
||||
match dep_node.kind {
|
||||
$(DepKind::$name => {
|
||||
if <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
|
||||
debug_assert!(tcx.dep_graph
|
||||
.node_color(dep_node)
|
||||
.map(|c| c.is_green())
|
||||
.unwrap_or(false));
|
||||
|
||||
let key = <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node).unwrap();
|
||||
if queries::$name::cache_on_disk(tcx, &key, None) {
|
||||
let _ = tcx.$name(key);
|
||||
}
|
||||
}
|
||||
})*
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rustc_cached_queries!(try_load_from_on_disk_cache!);
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
|
|
|
@ -40,7 +40,8 @@ impl QueryContext for TyCtxt<'tcx> {
|
|||
|
||||
fn try_collect_active_jobs(
|
||||
&self,
|
||||
) -> Option<FxHashMap<QueryJobId<Self::DepKind>, QueryJobInfo<Self>>> {
|
||||
) -> Option<FxHashMap<QueryJobId<Self::DepKind>, QueryJobInfo<Self::DepKind, Self::Query>>>
|
||||
{
|
||||
self.queries.try_collect_active_jobs()
|
||||
}
|
||||
|
||||
|
@ -241,25 +242,15 @@ macro_rules! hash_result {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! define_queries {
|
||||
(<$tcx:tt> $($category:tt {
|
||||
$($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)*
|
||||
},)*) => {
|
||||
define_queries_inner! { <$tcx>
|
||||
$($( $(#[$attr])* category<$category> [$($modifiers)*] fn $name: $node($($K)*) -> $V,)*)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! query_helper_param_ty {
|
||||
(DefId) => { impl IntoQueryParam<DefId> };
|
||||
($K:ty) => { $K };
|
||||
}
|
||||
|
||||
macro_rules! define_queries_inner {
|
||||
macro_rules! define_queries {
|
||||
(<$tcx:tt>
|
||||
$($(#[$attr:meta])* category<$category:tt>
|
||||
[$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)*) => {
|
||||
$($(#[$attr:meta])*
|
||||
[$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
|
||||
|
||||
use std::mem;
|
||||
use crate::{
|
||||
|
@ -267,7 +258,6 @@ macro_rules! define_queries_inner {
|
|||
rustc_data_structures::stable_hasher::StableHasher,
|
||||
ich::StableHashingContext
|
||||
};
|
||||
use rustc_data_structures::profiling::ProfileCategory;
|
||||
|
||||
define_queries_struct! {
|
||||
tcx: $tcx,
|
||||
|
@ -353,7 +343,7 @@ macro_rules! define_queries_inner {
|
|||
$(pub type $name<$tcx> = $V;)*
|
||||
}
|
||||
|
||||
$(impl<$tcx> QueryConfig<TyCtxt<$tcx>> for queries::$name<$tcx> {
|
||||
$(impl<$tcx> QueryConfig for queries::$name<$tcx> {
|
||||
type Key = $($K)*;
|
||||
type Value = $V;
|
||||
type Stored = <
|
||||
|
@ -361,18 +351,17 @@ macro_rules! define_queries_inner {
|
|||
as QueryStorage
|
||||
>::Stored;
|
||||
const NAME: &'static str = stringify!($name);
|
||||
const CATEGORY: ProfileCategory = $category;
|
||||
}
|
||||
|
||||
impl<$tcx> QueryAccessors<TyCtxt<$tcx>> for queries::$name<$tcx> {
|
||||
const ANON: bool = is_anon!([$($modifiers)*]);
|
||||
const EVAL_ALWAYS: bool = is_eval_always!([$($modifiers)*]);
|
||||
const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$node;
|
||||
const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$name;
|
||||
|
||||
type Cache = query_storage!([$($modifiers)*][$($K)*, $V]);
|
||||
|
||||
#[inline(always)]
|
||||
fn query_state<'a>(tcx: TyCtxt<$tcx>) -> &'a QueryState<TyCtxt<$tcx>, Self::Cache> {
|
||||
fn query_state<'a>(tcx: TyCtxt<$tcx>) -> &'a QueryState<crate::dep_graph::DepKind, <TyCtxt<$tcx> as QueryContext>::Query, Self::Cache> {
|
||||
&tcx.queries.$name
|
||||
}
|
||||
|
||||
|
@ -454,7 +443,7 @@ macro_rules! define_queries_inner {
|
|||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn $name(self, key: query_helper_param_ty!($($K)*))
|
||||
-> <queries::$name<$tcx> as QueryConfig<TyCtxt<$tcx>>>::Stored
|
||||
-> <queries::$name<$tcx> as QueryConfig>::Stored
|
||||
{
|
||||
self.at(DUMMY_SP).$name(key.into_query_param())
|
||||
})*
|
||||
|
@ -493,7 +482,7 @@ macro_rules! define_queries_inner {
|
|||
$($(#[$attr])*
|
||||
#[inline(always)]
|
||||
pub fn $name(self, key: query_helper_param_ty!($($K)*))
|
||||
-> <queries::$name<$tcx> as QueryConfig<TyCtxt<$tcx>>>::Stored
|
||||
-> <queries::$name<$tcx> as QueryConfig>::Stored
|
||||
{
|
||||
get_query::<queries::$name<'_>, _>(self.tcx, self.span, key.into_query_param())
|
||||
})*
|
||||
|
@ -527,7 +516,8 @@ macro_rules! define_queries_struct {
|
|||
fallback_extern_providers: Box<Providers>,
|
||||
|
||||
$($(#[$attr])* $name: QueryState<
|
||||
TyCtxt<$tcx>,
|
||||
crate::dep_graph::DepKind,
|
||||
<TyCtxt<$tcx> as QueryContext>::Query,
|
||||
<queries::$name<$tcx> as QueryAccessors<TyCtxt<'tcx>>>::Cache,
|
||||
>,)*
|
||||
}
|
||||
|
@ -548,7 +538,7 @@ macro_rules! define_queries_struct {
|
|||
|
||||
pub(crate) fn try_collect_active_jobs(
|
||||
&self
|
||||
) -> Option<FxHashMap<QueryJobId<crate::dep_graph::DepKind>, QueryJobInfo<TyCtxt<'tcx>>>> {
|
||||
) -> Option<FxHashMap<QueryJobId<crate::dep_graph::DepKind>, QueryJobInfo<crate::dep_graph::DepKind, <TyCtxt<$tcx> as QueryContext>::Query>>> {
|
||||
let mut jobs = FxHashMap::default();
|
||||
|
||||
$(
|
||||
|
|
|
@ -5,8 +5,7 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_data_structures::profiling::SelfProfiler;
|
||||
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_query_system::query::QueryCache;
|
||||
use rustc_query_system::query::QueryState;
|
||||
use rustc_query_system::query::{QueryCache, QueryContext, QueryState};
|
||||
use std::fmt::Debug;
|
||||
use std::io::Write;
|
||||
|
||||
|
@ -231,7 +230,7 @@ where
|
|||
pub(super) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query_name: &'static str,
|
||||
query_state: &QueryState<TyCtxt<'tcx>, C>,
|
||||
query_state: &QueryState<crate::dep_graph::DepKind, <TyCtxt<'tcx> as QueryContext>::Query, C>,
|
||||
string_cache: &mut QueryKeyStringCache,
|
||||
) where
|
||||
C: QueryCache,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue