Remove `token::Lit` from `ast::MetaItemLit`.
Currently `ast::MetaItemLit` represents the literal kind twice. This PR removes that redundancy. Best reviewed one commit at a time.
r? `@petrochenkov`
- name: dist-x86_64-apple
env:
SCRIPT: "./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin"
- RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
+ RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false --set rust.lto=thin"
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
MACOSX_DEPLOYMENT_TARGET: 10.7
SELECT_XCODE: /Applications/Xcode_13.4.1.app
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
DIST_REQUIRE_ALL_TOOLS: 1
- os: macos-latest
+ os: macos-12-xl
- name: dist-apple-various
env:
SCRIPT: "./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim"
NO_LLVM_ASSERTIONS: 1
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
- os: macos-latest
+ os: macos-12-xl
- name: dist-x86_64-apple-alt
env:
SCRIPT: "./x.py dist bootstrap --include-default-paths"
NO_LLVM_ASSERTIONS: 1
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
- os: macos-latest
+ os: macos-12-xl
- name: x86_64-apple-1
env:
SCRIPT: "./x.py --stage 2 test --exclude src/test/ui --exclude src/test/rustdoc --exclude src/test/run-make-fulldeps"
NO_LLVM_ASSERTIONS: 1
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
- os: macos-latest
+ os: macos-12-xl
- name: x86_64-apple-2
env:
SCRIPT: "./x.py --stage 2 test src/test/ui src/test/rustdoc src/test/run-make-fulldeps"
NO_LLVM_ASSERTIONS: 1
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
- os: macos-latest
+ os: macos-12-xl
- name: dist-aarch64-apple
env:
SCRIPT: "./x.py dist bootstrap --include-default-paths --stage 2"
NO_OVERFLOW_CHECKS: 1
DIST_REQUIRE_ALL_TOOLS: 1
JEMALLOC_SYS_WITH_LG_PAGE: 14
- os: macos-latest
+ os: macos-12-xl
- name: x86_64-msvc-1
env:
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-profiler"
os: windows-latest-xl
- name: dist-x86_64-msvc
env:
- RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler"
+ RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler --set rust.lto=thin"
SCRIPT: PGO_HOST=x86_64-pc-windows-msvc src/ci/pgo.sh python x.py dist bootstrap --include-default-paths
DIST_REQUIRE_ALL_TOOLS: 1
os: windows-latest-xl
[submodule "src/llvm-project"]
path = src/llvm-project
url = https://github.com/rust-lang/llvm-project.git
- branch = rustc/15.0-2022-08-09
+ branch = rustc/15.0-2022-12-07
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book
url = https://github.com/rust-embedded/book.git
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
+[[package]]
+name = "ar_archive_writer"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "276881980556fdadeb88aa1ffc667e4d2e8fe72531dfabcb7a82bb3c9ea9ba31"
+dependencies = [
+ "object",
+]
+
[[package]]
name = "array_tool"
version = "1.0.3"
"libc",
"num-integer",
"num-traits",
+ "serde",
"time",
"winapi",
]
"rustc-semver",
]
+[[package]]
+name = "collect-license-metadata"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "serde",
+ "serde_json",
+ "spdx-rs",
+]
+
[[package]]
name = "color-eyre"
version = "0.6.2"
[[package]]
name = "compiler_builtins"
-version = "0.1.84"
+version = "0.1.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "989b2c1ca6e90ad06fdc69d1d1862fa28d27a977be6d92ae2fa762cf61fe0b10"
+checksum = "13e81c6cd7ab79f51a0c927d22858d61ad12bd0b3865f0b13ece02a4486aeabb"
dependencies = [
"cc",
"rustc-std-workspace-core",
[[package]]
name = "cpufeatures"
-version = "0.2.1"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
dependencies = [
"libc",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
-[[package]]
-name = "difference"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
-
[[package]]
name = "digest"
version = "0.10.2"
"syn",
]
+[[package]]
+name = "dissimilar"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c97b9233581d84b8e1e689cdd3a47b6f69770084fc246e86a7f78b0d9c1d4a5"
+
[[package]]
name = "dlmalloc"
version = "0.2.3"
[[package]]
name = "expect-test"
-version = "1.0.1"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ceb96f3eaa0d4e8769c52dacfd4eb60183b817ed2f176171b3c691d5022b0f2e"
+checksum = "1d4661aca38d826eb7c72fe128e4238220616de4c0cc00db7bfc38e2e1364dd3"
dependencies = [
- "difference",
+ "dissimilar",
"once_cell",
]
"termcolor",
]
+[[package]]
+name = "generate-copyright"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "generic-array"
version = "0.14.4"
[[package]]
name = "libc"
-version = "0.2.135"
+version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
+checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
dependencies = [
"rustc-std-workspace-core",
]
name = "rustc_codegen_ssa"
version = "0.0.0"
dependencies = [
+ "ar_archive_writer",
"bitflags",
"cc",
"itertools",
name = "rustc_parse_format"
version = "0.0.0"
dependencies = [
+ "rustc_data_structures",
"rustc_lexer",
]
"rustc_feature",
"rustc_fs_util",
"rustc_hir",
+ "rustc_index",
"rustc_lint_defs",
"rustc_macros",
"rustc_serialize",
"rustc_span",
"rustc_target",
"smallvec",
+ "termize",
"tracing",
"winapi",
]
"rustc_span",
"rustc_target",
"tracing",
+ "twox-hash",
]
[[package]]
[[package]]
name = "rustix"
-version = "0.36.3"
+version = "0.36.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b1fbb4dfc4eb1d390c02df47760bb19a84bb80b301ecc947ab5406394d8223e"
+checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
dependencies = [
"bitflags",
"errno",
"winapi",
]
+[[package]]
+name = "spdx-expression"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d7ac03c67c572d85049d6db815e20a4a19b41b3d5cca732ac582342021ad77"
+dependencies = [
+ "nom",
+ "serde",
+ "thiserror",
+ "tracing",
+]
+
+[[package]]
+name = "spdx-rs"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3c02f6eb7e7b4100c272f685a9ccaccaab302324e8c7ec3e2ee72340fb29ff3"
+dependencies = [
+ "chrono",
+ "log",
+ "nom",
+ "serde",
+ "spdx-expression",
+ "strum",
+ "strum_macros",
+ "thiserror",
+ "uuid",
+]
+
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+[[package]]
+name = "strum"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
+
+[[package]]
+name = "strum_macros"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
[[package]]
name = "syn"
version = "1.0.102"
"tracing-subscriber",
]
+[[package]]
+name = "twox-hash"
+version = "1.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
+dependencies = [
+ "cfg-if 1.0.0",
+ "rand 0.8.5",
+ "static_assertions",
+]
+
[[package]]
name = "type-map"
version = "0.4.0"
[[package]]
name = "ui_test"
-version = "0.4.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf4559da3fe6b481f8674a29379677cb9606cd6f75fc254a2c9834c55638503d"
+checksum = "54ddb6f31025943e2f9d59237f433711c461a43d9415974c3eb3a4902edc1c1f"
dependencies = [
"bstr 1.0.1",
"cargo_metadata 0.15.0",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
+[[package]]
+name = "uuid"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
+dependencies = [
+ "getrandom 0.2.0",
+]
+
[[package]]
name = "valuable"
version = "0.1.0"
"src/tools/bump-stage0",
"src/tools/replace-version-placeholder",
"src/tools/lld-wrapper",
+ "src/tools/collect-license-metadata",
+ "src/tools/generate-copyright",
]
exclude = [
--- /dev/null
+---- LLVM Exceptions to the Apache 2.0 License ----
+
+ As an exception, if, as a result of your compiling your source code, portions
+ of this Software are embedded into an Object form of such source code, you
+ may redistribute such embedded portions in such Object form without complying
+ with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
+
+ In addition, if you combine or link compiled forms of this Software with
+ software that is licensed under the GPLv2 ("Combined Software") and if a
+ court of competent jurisdiction determines that the patent provision (Section
+ 3), the indemnity provision (Section 9) or other Section of the License
+ conflicts with the conditions of the GPLv2, you may retroactively and
+ prospectively choose to deem waived or otherwise exclude such Section(s) of
+ the License, but only in their entirety and only with respect to the Combined
+ Software.
if !always_sized { StructKind::MaybeUnsized } else { StructKind::AlwaysSized }
};
- let mut st = self.univariant(dl, &variants[v], &repr, kind)?;
+ let mut st = self.univariant(dl, &variants[v], repr, kind)?;
st.variants = Variants::Single { index: v };
if is_unsafe_cell {
let mut variant_layouts = variants
.iter_enumerated()
.map(|(j, v)| {
- let mut st = self.univariant(dl, v, &repr, StructKind::AlwaysSized)?;
+ let mut st = self.univariant(dl, v, repr, StructKind::AlwaysSized)?;
st.variants = Variants::Single { index: j };
align = align.max(st.align);
.map(|(i, field_layouts)| {
let mut st = self.univariant(
dl,
- &field_layouts,
- &repr,
+ field_layouts,
+ repr,
StructKind::Prefixed(min_ity.size(), prefix_align),
)?;
st.variants = Variants::Single { index: i };
// Try to use a ScalarPair for all tagged enums.
let mut common_prim = None;
let mut common_prim_initialized_in_all_variants = true;
- for (field_layouts, layout_variant) in iter::zip(&*variants, &layout_variants) {
+ for (field_layouts, layout_variant) in iter::zip(variants, &layout_variants) {
let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
panic!();
};
#![cfg_attr(feature = "nightly", feature(step_trait, rustc_attrs, min_specialization))]
-use std::convert::{TryFrom, TryInto};
use std::fmt;
#[cfg(feature = "nightly")]
use std::iter::Step;
use std::str::FromStr;
use bitflags::bitflags;
+#[cfg(feature = "nightly")]
+use rustc_data_structures::stable_hasher::StableOrd;
use rustc_index::vec::{Idx, IndexVec};
#[cfg(feature = "nightly")]
use rustc_macros::HashStable_Generic;
raw: u64,
}
+// Safety: Ord is implement as just comparing numerical values and numerical values
+// are not changed by (de-)serialization.
+#[cfg(feature = "nightly")]
+unsafe impl StableOrd for Size {}
+
// This is debug-printed a lot in larger structs, don't waste too much space there
impl fmt::Debug for Size {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use crate::{Float, FloatConvert, ParseError, Round, Status, StatusAnd};
use core::cmp::{self, Ordering};
-use core::convert::TryFrom;
use core::fmt::{self, Write};
use core::marker::PhantomData;
use core::mem;
use std::alloc::Layout;
use std::cell::{Cell, RefCell};
use std::cmp;
-use std::marker::{PhantomData, Send};
+use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::ptr::{self, NonNull};
use std::slice;
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
-use std::convert::TryFrom;
use std::fmt;
use std::mem;
use thin_vec::{thin_vec, ThinVec};
pub fn peel_parens(&self) -> &Expr {
let mut expr = self;
while let ExprKind::Paren(inner) = &expr.kind {
- expr = &inner;
+ expr = inner;
}
expr
}
pub movability: Movability,
pub fn_decl: P<FnDecl>,
pub body: P<Expr>,
- /// The span of the argument block `|...|`.
+ /// The span of the declaration block: 'move |...| -> ...'
pub fn_decl_span: Span,
+ /// The span of the argument block `|...|`
+ pub fn_arg_span: Span,
}
/// Limit types of a range (inclusive or exclusive)
pub fn peel_refs(&self) -> &Self {
let mut final_ty = self;
while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind {
- final_ty = &ty;
+ final_ty = ty;
}
final_ty
}
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum UseTreeKind {
/// `use prefix` or `use prefix as rename`
- ///
- /// The extra `NodeId`s are for HIR lowering, when additional statements are created for each
- /// namespace.
- Simple(Option<Ident>, NodeId, NodeId),
+ Simple(Option<Ident>),
/// `use prefix::{...}`
Nested(Vec<(UseTree, NodeId)>),
/// `use prefix::*`
impl UseTree {
pub fn ident(&self) -> Ident {
match self.kind {
- UseTreeKind::Simple(Some(rename), ..) => rename,
- UseTreeKind::Simple(None, ..) => {
+ UseTreeKind::Simple(Some(rename)) => rename,
+ UseTreeKind::Simple(None) => {
self.prefix.segments.last().expect("empty prefix in a simple import").ident
}
_ => panic!("`UseTree::ident` can only be used on a simple import"),
let UseTree { prefix, kind, span } = use_tree;
vis.visit_path(prefix);
match kind {
- UseTreeKind::Simple(rename, id1, id2) => {
- visit_opt(rename, |rename| vis.visit_ident(rename));
- vis.visit_id(id1);
- vis.visit_id(id2);
- }
+ UseTreeKind::Simple(rename) => visit_opt(rename, |rename| vis.visit_ident(rename)),
UseTreeKind::Nested(items) => {
for (tree, id) in items {
vis.visit_use_tree(tree);
return; // Avoid visiting the span for the second time.
}
token::Interpolated(nt) => {
- let mut nt = Lrc::make_mut(nt);
- visit_nonterminal(&mut nt, vis);
+ visit_nonterminal(Lrc::make_mut(nt), vis);
}
_ => {}
}
fn_decl,
body,
fn_decl_span,
+ fn_arg_span: _,
}) => {
vis.visit_closure_binder(binder);
vis.visit_asyncness(asyncness);
//! Moreover, a switch to, e.g., `P<'a, T>` would be easy and mostly automated.
use std::fmt::{self, Debug, Display};
-use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
use std::{slice, vec};
match (self, other) {
(TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind,
(TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => {
- delim == delim2 && tts.eq_unspanned(&tts2)
+ delim == delim2 && tts.eq_unspanned(tts2)
}
_ => false,
}
}
}
-impl iter::FromIterator<TokenTree> for TokenStream {
+impl FromIterator<TokenTree> for TokenStream {
fn from_iter<I: IntoIterator<Item = TokenTree>>(iter: I) -> Self {
TokenStream::new(iter.into_iter().collect::<Vec<TokenTree>>())
}
let mut t1 = self.trees();
let mut t2 = other.trees();
for (t1, t2) in iter::zip(&mut t1, &mut t2) {
- if !t1.eq_unspanned(&t2) {
+ if !t1.eq_unspanned(t2) {
return false;
}
}
token::Interpolated(nt) => TokenTree::Delimited(
DelimSpan::from_single(token.span),
Delimiter::Invisible,
- TokenStream::from_nonterminal_ast(&nt).flattened(),
+ TokenStream::from_nonterminal_ast(nt).flattened(),
),
_ => TokenTree::Token(token.clone(), spacing),
}
fn try_glue_to_last(vec: &mut Vec<TokenTree>, tt: &TokenTree) -> bool {
if let Some(TokenTree::Token(last_tok, Spacing::Joint)) = vec.last()
&& let TokenTree::Token(tok, spacing) = tt
- && let Some(glued_tok) = last_tok.glue(&tok)
+ && let Some(glued_tok) = last_tok.glue(tok)
{
// ...then overwrite the last token tree in `vec` with the
// glued token, and skip the first token tree from `stream`.
| ast::ExprKind::Loop(..)
| ast::ExprKind::ForLoop(..)
| ast::ExprKind::TryBlock(..)
+ | ast::ExprKind::ConstBlock(..)
)
}
} else {
&mut lines
};
- if let Some(horizontal) = get_horizontal_trim(&lines, kind) {
+ if let Some(horizontal) = get_horizontal_trim(lines, kind) {
changes = true;
// remove a "[ \t]*\*" block from each line, if possible
for line in lines.iter_mut() {
fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str {
let len = s.len();
- match all_whitespace(&s, col) {
+ match all_whitespace(s, col) {
Some(col) => {
if col < len {
&s[col..]
// new symbol because the string in the LitKind is different to the
// string in the token.
let s = symbol.as_str();
- let symbol = if s.contains(&['\\', '\r']) {
+ let symbol = if s.contains(['\\', '\r']) {
let mut buf = String::with_capacity(s.len());
let mut error = Ok(());
// Force-inlining here is aggressive but the closure is
// called on every char in the string, so it can be
// hot in programs with many long strings.
unescape_literal(
- &s,
+ s,
Mode::Str,
&mut #[inline(always)]
|_, unescaped_char| match unescaped_char {
if s.contains('\r') {
let mut buf = String::with_capacity(s.len());
let mut error = Ok(());
- unescape_literal(&s, Mode::RawStr, &mut |_, unescaped_char| {
+ unescape_literal(s, Mode::RawStr, &mut |_, unescaped_char| {
match unescaped_char {
Ok(c) => buf.push(c),
Err(err) => {
let s = symbol.as_str();
let mut buf = Vec::with_capacity(s.len());
let mut error = Ok(());
- unescape_literal(&s, Mode::ByteStr, &mut |_, c| match c {
+ unescape_literal(s, Mode::ByteStr, &mut |_, c| match c {
Ok(c) => buf.push(byte_from_char(c)),
Err(err) => {
if err.is_fatal() {
let bytes = if s.contains('\r') {
let mut buf = Vec::with_capacity(s.len());
let mut error = Ok(());
- unescape_literal(&s, Mode::RawByteStr, &mut |_, c| match c {
+ unescape_literal(s, Mode::RawByteStr, &mut |_, c| match c {
Ok(c) => buf.push(byte_from_char(c)),
Err(err) => {
if err.is_fatal() {
| ast::ExprKind::AssignOp(_, lhs, rhs)
| ast::ExprKind::Binary(_, lhs, rhs) => {
// X { y: 1 } + X { y: 2 }
- contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs)
+ contains_exterior_struct_lit(lhs) || contains_exterior_struct_lit(rhs)
}
ast::ExprKind::Await(x)
| ast::ExprKind::Unary(_, x)
| ast::ExprKind::Field(x, _)
| ast::ExprKind::Index(x, _) => {
// &X { y: 1 }, X { y: 1 }.y
- contains_exterior_struct_lit(&x)
+ contains_exterior_struct_lit(x)
}
ast::ExprKind::MethodCall(box ast::MethodCall { receiver, .. }) => {
// X { y: 1 }.bar(...)
- contains_exterior_struct_lit(&receiver)
+ contains_exterior_struct_lit(receiver)
}
_ => false,
// U+2069 - E2 81 A9
let mut bytes = s.as_bytes();
loop {
- match core::slice::memchr::memchr(0xE2, &bytes) {
+ match core::slice::memchr::memchr(0xE2, bytes) {
Some(idx) => {
// bytes are valid UTF-8 -> E2 must be followed by two bytes
let ch = &bytes[idx..idx + 3];
pub fn walk_use_tree<'a, V: Visitor<'a>>(visitor: &mut V, use_tree: &'a UseTree, id: NodeId) {
visitor.visit_path(&use_tree.prefix, id);
match &use_tree.kind {
- UseTreeKind::Simple(rename, ..) => {
+ UseTreeKind::Simple(rename) => {
// The extra IDs are handled during HIR lowering.
if let &Some(rename) = rename {
visitor.visit_ident(rename);
fn_decl,
body,
fn_decl_span: _,
+ fn_arg_span: _,
}) => {
visitor.visit_fn(FnKind::Closure(binder, fn_decl, body), expression.span, expression.id)
}
// Wrap the expression in an AnonConst.
let parent_def_id = self.current_hir_id_owner;
let node_id = self.next_node_id();
- self.create_def(parent_def_id.def_id, node_id, DefPathData::AnonConst);
+ self.create_def(
+ parent_def_id.def_id,
+ node_id,
+ DefPathData::AnonConst,
+ *op_sp,
+ );
let anon_const = AnonConst { id: node_id, value: P(expr) };
hir::InlineAsmOperand::SymFn {
anon_const: self.lower_anon_const(&anon_const),
use rustc_hir::definitions::DefPathData;
use rustc_session::errors::report_lit_error;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
-use rustc_span::symbol::{sym, Ident};
+use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::DUMMY_SP;
use thin_vec::thin_vec;
),
ExprKind::Async(capture_clause, closure_node_id, block) => self.make_async_expr(
*capture_clause,
+ None,
*closure_node_id,
None,
e.span,
fn_decl,
body,
fn_decl_span,
+ fn_arg_span,
}) => {
if let Async::Yes { closure_id, .. } = asyncness {
self.lower_expr_async_closure(
fn_decl,
body,
*fn_decl_span,
+ *fn_arg_span,
)
} else {
self.lower_expr_closure(
fn_decl,
body,
*fn_decl_span,
+ *fn_arg_span,
)
}
}
let node_id = self.next_node_id();
// Add a definition for the in-band const def.
- self.create_def(parent_def_id.def_id, node_id, DefPathData::AnonConst);
+ self.create_def(parent_def_id.def_id, node_id, DefPathData::AnonConst, f.span);
let anon_const = AnonConst { id: node_id, value: arg };
generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const)));
let lhs = self.lower_cond(lhs);
let rhs = self.lower_cond(rhs);
- self.arena.alloc(self.expr(
- cond.span,
- hir::ExprKind::Binary(op, lhs, rhs),
- AttrVec::new(),
- ))
+ self.arena.alloc(self.expr(cond.span, hir::ExprKind::Binary(op, lhs, rhs)))
}
ExprKind::Let(..) => self.lower_expr(cond),
_ => {
let cond = self.lower_expr(cond);
let reason = DesugaringKind::CondTemporary;
let span_block = self.mark_span_with_reason(reason, cond.span, None);
- self.expr_drop_temps(span_block, cond, AttrVec::new())
+ self.expr_drop_temps(span_block, cond)
}
}
}
) -> hir::ExprKind<'hir> {
let lowered_cond = self.with_loop_condition_scope(|t| t.lower_cond(cond));
let then = self.lower_block_expr(body);
- let expr_break = self.expr_break(span, AttrVec::new());
+ let expr_break = self.expr_break(span);
let stmt_break = self.stmt_expr(span, expr_break);
let else_blk = self.block_all(span, arena_vec![self; stmt_break], None);
- let else_expr = self.arena.alloc(self.expr_block(else_blk, AttrVec::new()));
+ let else_expr = self.arena.alloc(self.expr_block(else_blk));
let if_kind = hir::ExprKind::If(lowered_cond, self.arena.alloc(then), Some(else_expr));
- let if_expr = self.expr(span, if_kind, AttrVec::new());
+ let if_expr = self.expr(span, if_kind);
let block = self.block_expr(self.arena.alloc(if_expr));
let span = self.lower_span(span.with_hi(cond.span.hi()));
let opt_label = self.lower_label(opt_label);
expr: &'hir hir::Expr<'hir>,
overall_span: Span,
) -> &'hir hir::Expr<'hir> {
- let constructor = self.arena.alloc(self.expr_lang_item_path(
- method_span,
- lang_item,
- AttrVec::new(),
- None,
- ));
+ let constructor = self.arena.alloc(self.expr_lang_item_path(method_span, lang_item, None));
self.expr_call(overall_span, constructor, std::slice::from_ref(expr))
}
pub(super) fn make_async_expr(
&mut self,
capture_clause: CaptureBy,
+ outer_hir_id: Option<hir::HirId>,
closure_node_id: NodeId,
ret_ty: Option<hir::FnRetTy<'hir>>,
span: Span,
) -> hir::ExprKind<'hir> {
let output = ret_ty.unwrap_or_else(|| hir::FnRetTy::DefaultReturn(self.lower_span(span)));
- // Resume argument type: `ResumeTy`
- let unstable_span =
- self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
- let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span, None);
+ // Resume argument type, which should be `&mut Context<'_>`.
+ // NOTE: Using the `'static` lifetime here is technically cheating.
+ // The `Future::poll` argument really is `&'a mut Context<'b>`, but we cannot
+ // express the fact that we are not storing it across yield-points yet,
+ // and we would thus run into lifetime errors.
+ // See <https://github.com/rust-lang/rust/issues/68923>.
+ // Our lowering makes sure we are not mis-using the `_task_context` input type
+ // in the sense that we are indeed not using it across yield points. We
+ // get a fresh `&mut Context` for each resume / call of `Future::poll`.
+ // This "cheating" was previously done with a `ResumeTy` that contained a raw
+ // pointer, and a `get_context` accessor that pulled the `Context` lifetimes
+ // out of thin air.
+ let context_lifetime_ident = Ident::with_dummy_span(kw::StaticLifetime);
+ let context_lifetime = self.arena.alloc(hir::Lifetime {
+ hir_id: self.next_id(),
+ ident: context_lifetime_ident,
+ res: hir::LifetimeName::Static,
+ });
+ let context_path =
+ hir::QPath::LangItem(hir::LangItem::Context, self.lower_span(span), None);
+ let context_ty = hir::MutTy {
+ ty: self.arena.alloc(hir::Ty {
+ hir_id: self.next_id(),
+ kind: hir::TyKind::Path(context_path),
+ span: self.lower_span(span),
+ }),
+ mutbl: hir::Mutability::Mut,
+ };
let input_ty = hir::Ty {
hir_id: self.next_id(),
- kind: hir::TyKind::Path(resume_ty),
- span: unstable_span,
+ kind: hir::TyKind::Rptr(context_lifetime, context_ty),
+ span: self.lower_span(span),
};
// The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`.
fn_decl,
body,
fn_decl_span: self.lower_span(span),
+ fn_arg_span: None,
movability: Some(hir::Movability::Static),
});
hir::ExprKind::Closure(c)
};
- let parent_has_track_caller = self
- .attrs
- .values()
- .find(|attrs| attrs.into_iter().find(|attr| attr.has_name(sym::track_caller)).is_some())
- .is_some();
+
+ let track_caller = outer_hir_id
+ .and_then(|id| self.attrs.get(&id.local_id))
+ .map_or(false, |attrs| attrs.into_iter().any(|attr| attr.has_name(sym::track_caller)));
+
+ let hir_id = self.lower_node_id(closure_node_id);
let unstable_span =
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
-
- let hir_id = if parent_has_track_caller {
- let generator_hir_id = self.lower_node_id(closure_node_id);
+ if track_caller {
self.lower_attrs(
- generator_hir_id,
+ hir_id,
&[Attribute {
kind: AttrKind::Normal(ptr::P(NormalAttr {
item: AttrItem {
span: unstable_span,
}],
);
- generator_hir_id
- } else {
- self.lower_node_id(closure_node_id)
- };
+ }
let generator = hir::Expr { hir_id, kind: generator_kind, span: self.lower_span(span) };
// E0700 in src/test/ui/self/self_lifetime-async.rs
// `future::identity_future`:
- let identity_future = self.expr_lang_item_path(
- unstable_span,
- hir::LangItem::IdentityFuture,
- AttrVec::new(),
- None,
- );
+ let identity_future =
+ self.expr_lang_item_path(unstable_span, hir::LangItem::IdentityFuture, None);
// `future::identity_future(generator)`:
hir::ExprKind::Call(self.arena.alloc(identity_future), arena_vec![self; generator])
/// mut __awaitee => loop {
/// match unsafe { ::std::future::Future::poll(
/// <::std::pin::Pin>::new_unchecked(&mut __awaitee),
- /// ::std::future::get_context(task_context),
+ /// task_context,
/// ) } {
/// ::std::task::Poll::Ready(result) => break result,
/// ::std::task::Poll::Pending => {}
// unsafe {
// ::std::future::Future::poll(
// ::std::pin::Pin::new_unchecked(&mut __awaitee),
- // ::std::future::get_context(task_context),
+ // task_context,
// )
// }
let poll_expr = {
arena_vec![self; ref_mut_awaitee],
Some(expr_hir_id),
);
- let get_context = self.expr_call_lang_item_fn_mut(
- gen_future_span,
- hir::LangItem::GetContext,
- arena_vec![self; task_context],
- Some(expr_hir_id),
- );
let call = self.expr_call_lang_item_fn(
span,
hir::LangItem::FuturePoll,
- arena_vec![self; new_unchecked, get_context],
+ arena_vec![self; new_unchecked, task_context],
Some(expr_hir_id),
);
self.arena.alloc(self.expr_unsafe(call))
let break_x = self.with_loop_scope(loop_node_id, move |this| {
let expr_break =
hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr));
- this.arena.alloc(this.expr(gen_future_span, expr_break, AttrVec::new()))
+ this.arena.alloc(this.expr(gen_future_span, expr_break))
});
self.arm(ready_pat, break_x)
};
let yield_expr = self.expr(
span,
hir::ExprKind::Yield(unit, hir::YieldSource::Await { expr: Some(expr_hir_id) }),
- AttrVec::new(),
);
let yield_expr = self.arena.alloc(yield_expr);
if let Some(task_context_hid) = self.task_context {
let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
- let assign = self.expr(
- span,
- hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span)),
- AttrVec::new(),
- );
+ let assign =
+ self.expr(span, hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span)));
self.stmt_expr(span, assign)
} else {
// Use of `await` outside of an async context. Return `yield_expr` so that we can
decl: &FnDecl,
body: &Expr,
fn_decl_span: Span,
+ fn_arg_span: Span,
) -> hir::ExprKind<'hir> {
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
fn_decl,
body: body_id,
fn_decl_span: self.lower_span(fn_decl_span),
+ fn_arg_span: Some(self.lower_span(fn_arg_span)),
movability: generator_option,
});
decl: &FnDecl,
body: &Expr,
fn_decl_span: Span,
+ fn_arg_span: Span,
) -> hir::ExprKind<'hir> {
if let &ClosureBinder::For { span, .. } = binder {
self.tcx.sess.emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
let async_body = this.make_async_expr(
capture_clause,
+ // FIXME(nbdd0121): This should also use a proper HIR id so `#[track_caller]`
+ // can be applied on async closures as well.
+ None,
inner_closure_id,
async_ret_ty,
body.span,
hir::AsyncGeneratorKind::Closure,
|this| this.with_new_scopes(|this| this.lower_expr_mut(body)),
);
- this.expr(fn_decl_span, async_body, AttrVec::new())
+ this.expr(fn_decl_span, async_body)
});
body_id
});
fn_decl,
body,
fn_decl_span: self.lower_span(fn_decl_span),
+ fn_arg_span: Some(self.lower_span(fn_arg_span)),
movability: None,
});
hir::ExprKind::Closure(c)
let ident = self.expr_ident(lhs.span, ident, binding);
let assign =
hir::ExprKind::Assign(self.lower_expr(lhs), ident, self.lower_span(eq_sign_span));
- let expr = self.expr(lhs.span, assign, AttrVec::new());
+ let expr = self.expr(lhs.span, assign);
assignments.push(self.stmt_expr(lhs.span, expr));
pat
}
let e2 = self.lower_expr_mut(e2);
let fn_path =
hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, self.lower_span(span), None);
- let fn_expr =
- self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path), AttrVec::new()));
+ let fn_expr = self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path)));
hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2])
}
// `None => break`
let none_arm = {
- let break_expr =
- self.with_loop_scope(e.id, |this| this.expr_break_alloc(for_span, AttrVec::new()));
+ let break_expr = self.with_loop_scope(e.id, |this| this.expr_break_alloc(for_span));
let pat = self.pat_none(for_span);
self.arm(pat, break_expr)
};
let some_arm = {
let some_pat = self.pat_some(pat_span, pat);
let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false));
- let body_expr = self.arena.alloc(self.expr_block(body_block, AttrVec::new()));
+ let body_expr = self.arena.alloc(self.expr_block(body_block));
self.arm(some_pat, body_expr)
};
// 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(for_span, match_expr, e.attrs.clone())
+ let expr = self.expr_drop_temps_mut(for_span, match_expr);
+ self.lower_attrs(expr.hir_id, &e.attrs);
+ expr
}
/// Desugar `ExprKind::Try` from: `<expr>?` into:
let continue_arm = {
let val_ident = Ident::with_dummy_span(sym::val);
let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
- let val_expr = self.arena.alloc(self.expr_ident_with_attrs(
- span,
- val_ident,
- val_pat_nid,
- attrs.clone(),
- ));
+ let val_expr = self.expr_ident(span, val_ident, val_pat_nid);
+ self.lower_attrs(val_expr.hir_id, &attrs);
let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
self.arm(continue_pat, val_expr)
};
hir::Destination { label: None, target_id },
Some(from_residual_expr),
),
- attrs,
))
} else {
- self.arena.alloc(self.expr(
- try_span,
- hir::ExprKind::Ret(Some(from_residual_expr)),
- attrs,
- ))
+ self.arena.alloc(self.expr(try_span, hir::ExprKind::Ret(Some(from_residual_expr))))
};
+ self.lower_attrs(ret_expr.hir_id, &attrs);
let break_pat = self.pat_cf_break(try_span, residual_local);
self.arm(break_pat, ret_expr)
&mut self,
span: Span,
expr: &'hir hir::Expr<'hir>,
- attrs: AttrVec,
) -> &'hir hir::Expr<'hir> {
- self.arena.alloc(self.expr_drop_temps_mut(span, expr, attrs))
+ self.arena.alloc(self.expr_drop_temps_mut(span, expr))
}
pub(super) fn expr_drop_temps_mut(
&mut self,
span: Span,
expr: &'hir hir::Expr<'hir>,
- attrs: AttrVec,
) -> hir::Expr<'hir> {
- self.expr(span, hir::ExprKind::DropTemps(expr), attrs)
+ self.expr(span, hir::ExprKind::DropTemps(expr))
}
fn expr_match(
arms: &'hir [hir::Arm<'hir>],
source: hir::MatchSource,
) -> hir::Expr<'hir> {
- self.expr(span, hir::ExprKind::Match(arg, arms, source), AttrVec::new())
+ self.expr(span, hir::ExprKind::Match(arg, arms, source))
}
- fn expr_break(&mut self, span: Span, attrs: AttrVec) -> hir::Expr<'hir> {
+ fn expr_break(&mut self, span: Span) -> hir::Expr<'hir> {
let expr_break = hir::ExprKind::Break(self.lower_loop_destination(None), None);
- self.expr(span, expr_break, attrs)
+ self.expr(span, expr_break)
}
- fn expr_break_alloc(&mut self, span: Span, attrs: AttrVec) -> &'hir hir::Expr<'hir> {
- let expr_break = self.expr_break(span, attrs);
+ fn expr_break_alloc(&mut self, span: Span) -> &'hir hir::Expr<'hir> {
+ let expr_break = self.expr_break(span);
self.arena.alloc(expr_break)
}
fn expr_mut_addr_of(&mut self, span: Span, e: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {
- self.expr(
- span,
- hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e),
- AttrVec::new(),
- )
+ self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e))
}
fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> {
- self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[]), AttrVec::new()))
+ self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[])))
}
fn expr_call_mut(
e: &'hir hir::Expr<'hir>,
args: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> {
- self.expr(span, hir::ExprKind::Call(e, args), AttrVec::new())
+ self.expr(span, hir::ExprKind::Call(e, args))
}
fn expr_call(
args: &'hir [hir::Expr<'hir>],
hir_id: Option<hir::HirId>,
) -> hir::Expr<'hir> {
- let path =
- self.arena.alloc(self.expr_lang_item_path(span, lang_item, AttrVec::new(), hir_id));
+ let path = self.arena.alloc(self.expr_lang_item_path(span, lang_item, hir_id));
self.expr_call_mut(span, path, args)
}
&mut self,
span: Span,
lang_item: hir::LangItem,
- attrs: AttrVec,
hir_id: Option<hir::HirId>,
) -> hir::Expr<'hir> {
self.expr(
span,
hir::ExprKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span), hir_id)),
- attrs,
)
}
}
pub(super) fn expr_ident_mut(
- &mut self,
- sp: Span,
- ident: Ident,
- binding: hir::HirId,
- ) -> hir::Expr<'hir> {
- self.expr_ident_with_attrs(sp, ident, binding, AttrVec::new())
- }
-
- fn expr_ident_with_attrs(
&mut self,
span: Span,
ident: Ident,
binding: hir::HirId,
- attrs: AttrVec,
) -> hir::Expr<'hir> {
let hir_id = self.next_id();
let res = Res::Local(binding);
}),
));
- self.expr(span, expr_path, attrs)
+ self.expr(span, expr_path)
}
fn expr_unsafe(&mut self, expr: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {
}),
None,
),
- AttrVec::new(),
)
}
fn expr_block_empty(&mut self, span: Span) -> &'hir hir::Expr<'hir> {
let blk = self.block_all(span, &[], None);
- let expr = self.expr_block(blk, AttrVec::new());
+ let expr = self.expr_block(blk);
self.arena.alloc(expr)
}
- pub(super) fn expr_block(
- &mut self,
- b: &'hir hir::Block<'hir>,
- attrs: AttrVec,
- ) -> hir::Expr<'hir> {
- self.expr(b.span, hir::ExprKind::Block(b, None), attrs)
+ pub(super) fn expr_block(&mut self, b: &'hir hir::Block<'hir>) -> hir::Expr<'hir> {
+ self.expr(b.span, hir::ExprKind::Block(b, None))
}
- pub(super) fn expr(
- &mut self,
- span: Span,
- kind: hir::ExprKind<'hir>,
- attrs: AttrVec,
- ) -> hir::Expr<'hir> {
+ pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> {
let hir_id = self.next_id();
- self.lower_attrs(hir_id, &attrs);
hir::Expr { hir_id, kind, span: self.lower_span(span) }
}
use rustc_span::{Span, Symbol};
use rustc_target::spec::abi;
use smallvec::{smallvec, SmallVec};
-use std::iter;
use thin_vec::ThinVec;
pub(super) struct ItemLowerer<'a, 'hir> {
let mut node_ids =
smallvec![hir::ItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }];
if let ItemKind::Use(use_tree) = &i.kind {
- self.lower_item_id_use_tree(use_tree, i.id, &mut node_ids);
+ self.lower_item_id_use_tree(use_tree, &mut node_ids);
}
node_ids
}
- fn lower_item_id_use_tree(
- &mut self,
- tree: &UseTree,
- base_id: NodeId,
- vec: &mut SmallVec<[hir::ItemId; 1]>,
- ) {
+ fn lower_item_id_use_tree(&mut self, tree: &UseTree, vec: &mut SmallVec<[hir::ItemId; 1]>) {
match &tree.kind {
UseTreeKind::Nested(nested_vec) => {
for &(ref nested, id) in nested_vec {
vec.push(hir::ItemId {
owner_id: hir::OwnerId { def_id: self.local_def_id(id) },
});
- self.lower_item_id_use_tree(nested, id, vec);
- }
- }
- UseTreeKind::Glob => {}
- UseTreeKind::Simple(_, id1, id2) => {
- for (_, id) in
- iter::zip(self.expect_full_res_from_use(base_id).skip(1), [*id1, *id2])
- {
- vec.push(hir::ItemId {
- owner_id: hir::OwnerId { def_id: self.local_def_id(id) },
- });
+ self.lower_item_id_use_tree(nested, vec);
}
}
+ UseTreeKind::Simple(..) | UseTreeKind::Glob => {}
}
}
// only cares about the input argument patterns in the function
// declaration (decl), not the return types.
let asyncness = header.asyncness;
- let body_id =
- this.lower_maybe_async_body(span, &decl, asyncness, body.as_deref());
+ let body_id = this.lower_maybe_async_body(
+ span,
+ hir_id,
+ &decl,
+ asyncness,
+ body.as_deref(),
+ );
let mut itctx = ImplTraitContext::Universal;
let (generics, decl) = this.lower_generics(generics, id, &mut itctx, |this| {
let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect();
match tree.kind {
- UseTreeKind::Simple(rename, id1, id2) => {
+ UseTreeKind::Simple(rename) => {
*ident = tree.ident();
// First, apply the prefix to the path.
}
}
- let mut resolutions = self.expect_full_res_from_use(id).fuse();
- // We want to return *something* from this function, so hold onto the first item
- // for later.
- let ret_res = self.lower_res(resolutions.next().unwrap_or(Res::Err));
-
- // Here, we are looping over namespaces, if they exist for the definition
- // being imported. We only handle type and value namespaces because we
- // won't be dealing with macros in the rest of the compiler.
- // Essentially a single `use` which imports two names is desugared into
- // two imports.
- for new_node_id in [id1, id2] {
- let new_id = self.local_def_id(new_node_id);
- let Some(res) = resolutions.next() else {
- debug_assert!(self.children.iter().find(|(id, _)| id == &new_id).is_none());
- // Associate an HirId to both ids even if there is no resolution.
- self.children.push((
- new_id,
- hir::MaybeOwner::NonOwner(hir::HirId::make_owner(new_id))),
- );
- continue;
- };
- let ident = *ident;
- let mut path = path.clone();
- for seg in &mut path.segments {
- // Give the cloned segment the same resolution information
- // as the old one (this is needed for stability checking).
- let new_id = self.next_node_id();
- self.resolver.clone_res(seg.id, new_id);
- seg.id = new_id;
- }
- let span = path.span;
-
- self.with_hir_id_owner(new_node_id, |this| {
- let res = this.lower_res(res);
- let path = this.lower_path_extra(res, &path, ParamMode::Explicit);
- let kind = hir::ItemKind::Use(path, hir::UseKind::Single);
- if let Some(attrs) = attrs {
- this.attrs.insert(hir::ItemLocalId::new(0), attrs);
- }
-
- let item = hir::Item {
- owner_id: hir::OwnerId { def_id: new_id },
- ident: this.lower_ident(ident),
- kind,
- vis_span,
- span: this.lower_span(span),
- };
- hir::OwnerNode::Item(this.arena.alloc(item))
- });
- }
-
- let path = self.lower_path_extra(ret_res, &path, ParamMode::Explicit);
+ let res =
+ self.expect_full_res_from_use(id).map(|res| self.lower_res(res)).collect();
+ let path = self.lower_use_path(res, &path, ParamMode::Explicit);
hir::ItemKind::Use(path, hir::UseKind::Single)
}
UseTreeKind::Glob => {
- let path = self.lower_path(
- id,
- &Path { segments, span: path.span, tokens: None },
- ParamMode::Explicit,
- );
+ let res = self.expect_full_res(id);
+ let res = smallvec![self.lower_res(res)];
+ let path = Path { segments, span: path.span, tokens: None };
+ let path = self.lower_use_path(res, &path, ParamMode::Explicit);
hir::ItemKind::Use(path, hir::UseKind::Glob)
}
UseTreeKind::Nested(ref trees) => {
});
}
- let res = self.expect_full_res_from_use(id).next().unwrap_or(Res::Err);
- let res = self.lower_res(res);
- let path = self.lower_path_extra(res, &prefix, ParamMode::Explicit);
+ let res =
+ self.expect_full_res_from_use(id).map(|res| self.lower_res(res)).collect();
+ let path = self.lower_use_path(res, &prefix, ParamMode::Explicit);
hir::ItemKind::Use(path, hir::UseKind::ListStem)
}
}
fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
let hir_id = self.lower_node_id(i.id);
+ self.lower_attrs(hir_id, &i.attrs);
let trait_item_def_id = hir_id.expect_owner();
let (generics, kind, has_default) = match &i.kind {
AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => {
let asyncness = sig.header.asyncness;
let body_id =
- self.lower_maybe_async_body(i.span, &sig.decl, asyncness, Some(&body));
+ self.lower_maybe_async_body(i.span, hir_id, &sig.decl, asyncness, Some(&body));
let (generics, sig) = self.lower_method_sig(
generics,
sig,
AssocItemKind::MacCall(..) => panic!("macro item shouldn't exist at this point"),
};
- self.lower_attrs(hir_id, &i.attrs);
let item = hir::TraitItem {
owner_id: trait_item_def_id,
ident: self.lower_ident(i.ident),
/// Construct `ExprKind::Err` for the given `span`.
pub(crate) fn expr_err(&mut self, span: Span) -> hir::Expr<'hir> {
- self.expr(span, hir::ExprKind::Err, AttrVec::new())
+ self.expr(span, hir::ExprKind::Err)
}
fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
// Since `default impl` is not yet implemented, this is always true in impls.
let has_value = true;
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
+ let hir_id = self.lower_node_id(i.id);
+ self.lower_attrs(hir_id, &i.attrs);
let (generics, kind) = match &i.kind {
AssocItemKind::Const(_, ty, expr) => {
AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
self.current_item = Some(i.span);
let asyncness = sig.header.asyncness;
- let body_id =
- self.lower_maybe_async_body(i.span, &sig.decl, asyncness, body.as_deref());
+ let body_id = self.lower_maybe_async_body(
+ i.span,
+ hir_id,
+ &sig.decl,
+ asyncness,
+ body.as_deref(),
+ );
let (generics, sig) = self.lower_method_sig(
generics,
sig,
AssocItemKind::MacCall(..) => panic!("`TyMac` should have been expanded by now"),
};
- let hir_id = self.lower_node_id(i.id);
- self.lower_attrs(hir_id, &i.attrs);
let item = hir::ImplItem {
owner_id: hir_id.expect_owner(),
ident: self.lower_ident(i.ident),
fn lower_maybe_async_body(
&mut self,
span: Span,
+ fn_id: hir::HirId,
decl: &FnDecl,
asyncness: Async,
body: Option<&Block>,
let async_expr = this.make_async_expr(
CaptureBy::Value,
+ Some(fn_id),
closure_id,
None,
body.span,
// Transform into `drop-temps { <user-body> }`, an expression:
let desugared_span =
this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
- let user_body = this.expr_drop_temps(
- desugared_span,
- this.arena.alloc(user_body),
- AttrVec::new(),
- );
+ let user_body =
+ this.expr_drop_temps(desugared_span, this.arena.alloc(user_body));
// As noted above, create the final block like
//
Some(user_body),
);
- this.expr_block(body, AttrVec::new())
+ this.expr_block(body)
},
);
- (
- this.arena.alloc_from_iter(parameters),
- this.expr(body.span, async_expr, AttrVec::new()),
- )
+ (this.arena.alloc_from_iter(parameters), this.expr(body.span, async_expr))
})
}
},
/// Impl trait in type aliases.
TypeAliasesOpaqueTy,
+ /// `impl Trait` is unstably accepted in this position.
+ FeatureGated(ImplTraitPosition, Symbol),
/// `impl Trait` is not accepted in this position.
Disallowed(ImplTraitPosition),
}
}
// Drop AST to free memory
- std::mem::drop(ast_index);
- sess.time("drop_ast", || std::mem::drop(krate));
+ drop(ast_index);
+ sess.time("drop_ast", || drop(krate));
// Discard hygiene data, which isn't required after lowering to HIR.
if !sess.opts.unstable_opts.keep_hygiene_data {
parent: LocalDefId,
node_id: ast::NodeId,
data: DefPathData,
+ span: Span,
) -> LocalDefId {
debug_assert_ne!(node_id, ast::DUMMY_NODE_ID);
assert!(
self.tcx.hir().def_key(self.local_def_id(node_id)),
);
- let def_id = self.tcx.create_def(parent, data).def_id();
+ let def_id = self.tcx.at(span).create_def(parent, data).def_id();
debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
self.resolver.node_id_to_def_id.insert(node_id, def_id);
self.current_hir_id_owner.def_id,
param,
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+ ident.span,
);
debug!(?_def_id);
let parent_def_id = self.current_hir_id_owner;
let node_id = self.next_node_id();
+ let span = self.lower_span(ty.span);
// Add a definition for the in-band const def.
let def_id = self.create_def(
parent_def_id.def_id,
node_id,
DefPathData::AnonConst,
+ span,
);
- let span = self.lower_span(ty.span);
let path_expr = Expr {
id: ty.id,
kind: ExprKind::Path(qself.clone(), path.clone()),
itctx,
),
ImplTraitContext::Universal => {
+ let span = t.span;
self.create_def(
self.current_hir_id_owner.def_id,
*def_node_id,
DefPathData::ImplTrait,
+ span,
);
- let span = t.span;
let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span);
let (param, bounds, path) =
self.lower_generic_and_bounds(*def_node_id, span, ident, bounds);
}
path
}
- ImplTraitContext::Disallowed(
- position @ (ImplTraitPosition::TraitReturn | ImplTraitPosition::ImplReturn),
- ) => {
+ ImplTraitContext::FeatureGated(position, feature) => {
self.tcx
.sess
.create_feature_err(
MisplacedImplTrait {
span: t.span,
- position: DiagnosticArgFromDisplay(&position),
+ position: DiagnosticArgFromDisplay(position),
},
- sym::return_position_impl_trait_in_trait,
+ *feature,
)
.emit();
hir::TyKind::Err
ImplTraitContext::Disallowed(position) => {
self.tcx.sess.emit_err(MisplacedImplTrait {
span: t.span,
- position: DiagnosticArgFromDisplay(&position),
+ position: DiagnosticArgFromDisplay(position),
});
hir::TyKind::Err
}
self.current_hir_id_owner.def_id,
opaque_ty_node_id,
DefPathData::ImplTrait,
+ opaque_ty_span,
);
debug!(?opaque_ty_def_id);
parent_def_id,
node_id,
DefPathData::LifetimeNs(lifetime.ident.name),
+ lifetime.ident.span,
);
remapping.insert(old_def_id, new_def_id);
parent_def_id,
node_id,
DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+ lifetime.ident.span,
);
remapping.insert(old_def_id, new_def_id);
} else {
match &decl.output {
FnRetTy::Ty(ty) => {
- let mut context = if kind.return_impl_trait_allowed(self.tcx) {
+ let context = if kind.return_impl_trait_allowed(self.tcx) {
let fn_def_id = self.local_def_id(fn_node_id);
ImplTraitContext::ReturnPositionOpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
in_trait: matches!(kind, FnDeclKind::Trait),
}
} else {
- ImplTraitContext::Disallowed(match kind {
+ let position = match kind {
FnDeclKind::Fn | FnDeclKind::Inherent => {
unreachable!("fn should allow in-band lifetimes")
}
FnDeclKind::Pointer => ImplTraitPosition::PointerReturn,
FnDeclKind::Trait => ImplTraitPosition::TraitReturn,
FnDeclKind::Impl => ImplTraitPosition::ImplReturn,
- })
+ };
+ match kind {
+ FnDeclKind::Trait | FnDeclKind::Impl => ImplTraitContext::FeatureGated(
+ position,
+ sym::return_position_impl_trait_in_trait,
+ ),
+ _ => ImplTraitContext::Disallowed(position),
+ }
};
- hir::FnRetTy::Return(self.lower_ty(ty, &mut context))
+ hir::FnRetTy::Return(self.lower_ty(ty, &context))
}
FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)),
}
let fn_def_id = self.local_def_id(fn_node_id);
let opaque_ty_def_id =
- self.create_def(fn_def_id, opaque_ty_node_id, DefPathData::ImplTrait);
+ self.create_def(fn_def_id, opaque_ty_node_id, DefPathData::ImplTrait, opaque_ty_span);
// When we create the opaque type for this async fn, it is going to have
// to capture all the lifetimes involved in the signature (including in the
opaque_ty_def_id,
inner_node_id,
DefPathData::LifetimeNs(ident.name),
+ ident.span,
);
new_remapping.insert(outer_def_id, inner_def_id);
output,
span,
if in_trait && !this.tcx.features().return_position_impl_trait_in_trait {
- ImplTraitContext::Disallowed(ImplTraitPosition::TraitReturn)
+ ImplTraitContext::FeatureGated(
+ ImplTraitPosition::TraitReturn,
+ sym::return_position_impl_trait_in_trait,
+ )
} else {
ImplTraitContext::ReturnPositionOpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
/// has no attributes and is not targeted by a `break`.
fn lower_block_expr(&mut self, b: &Block) -> hir::Expr<'hir> {
let block = self.lower_block(b, false);
- self.expr_block(block, AttrVec::new())
+ self.expr_block(block)
}
fn lower_array_length(&mut self, c: &AnonConst) -> hir::ArrayLen {
use rustc_hir as hir;
use rustc_hir::def::{DefKind, PartialRes, Res};
use rustc_hir::GenericArg;
-use rustc_span::symbol::{kw, Ident};
+use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, Span, DUMMY_SP};
-use smallvec::smallvec;
+use smallvec::{smallvec, SmallVec};
impl<'a, 'hir> LoweringContext<'a, 'hir> {
#[instrument(level = "trace", skip(self))]
);
}
- pub(crate) fn lower_path_extra(
+ pub(crate) fn lower_use_path(
&mut self,
- res: Res,
+ res: SmallVec<[Res; 3]>,
p: &Path,
param_mode: ParamMode,
- ) -> &'hir hir::Path<'hir> {
- self.arena.alloc(hir::Path {
+ ) -> &'hir hir::UsePath<'hir> {
+ self.arena.alloc(hir::UsePath {
res,
segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {
self.lower_path_segment(
})
}
- pub(crate) fn lower_path(
- &mut self,
- id: NodeId,
- p: &Path,
- param_mode: ParamMode,
- ) -> &'hir hir::Path<'hir> {
- let res = self.expect_full_res(id);
- let res = self.lower_res(res);
- self.lower_path_extra(res, p, param_mode)
- }
-
pub(crate) fn lower_path_segment(
&mut self,
path_span: Span,
// fn f(_: impl Fn() -> impl Debug) -> impl Fn() -> impl Debug
// // disallowed --^^^^^^^^^^ allowed --^^^^^^^^^^
// ```
- FnRetTy::Ty(ty)
- if matches!(itctx, ImplTraitContext::ReturnPositionOpaqueTy { .. })
- && self.tcx.features().impl_trait_in_fn_trait_return =>
- {
- self.lower_ty(&ty, itctx)
+ FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::ReturnPositionOpaqueTy { .. }) => {
+ if self.tcx.features().impl_trait_in_fn_trait_return {
+ self.lower_ty(&ty, itctx)
+ } else {
+ self.lower_ty(
+ &ty,
+ &ImplTraitContext::FeatureGated(
+ ImplTraitPosition::FnTraitReturn,
+ sym::impl_trait_in_fn_trait_return,
+ ),
+ )
+ }
}
FnRetTy::Ty(ty) => {
self.lower_ty(&ty, &ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn))
ast::MetaItemKind::List(items) => {
self.print_path(&item.path, false, 0);
self.popen();
- self.commasep(Consistent, &items, |s, i| s.print_meta_list_item(i));
+ self.commasep(Consistent, items, |s, i| s.print_meta_list_item(i));
self.pclose();
}
}
fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) {
match tt {
TokenTree::Token(token, _) => {
- let token_str = self.token_to_string_ext(&token, convert_dollar_crate);
+ let token_str = self.token_to_string_ext(token, convert_dollar_crate);
self.word(token_str);
if let token::DocComment(..) = token.kind {
self.hardbreak()
ast::AssocConstraintKind::Bound { bounds } => {
if !bounds.is_empty() {
self.word_nbsp(":");
- self.print_type_bounds(&bounds);
+ self.print_type_bounds(bounds);
}
}
}
}
ast::TyKind::Tup(elts) => {
self.popen();
- self.commasep(Inconsistent, &elts, |s, ty| s.print_type(ty));
+ self.commasep(Inconsistent, elts, |s, ty| s.print_type(ty));
if elts.len() == 1 {
self.word(",");
}
self.popen();
self.commasep(Consistent, &args, |s, arg| match arg {
- AsmArg::Template(template) => s.print_string(&template, ast::StrStyle::Cooked),
+ AsmArg::Template(template) => s.print_string(template, ast::StrStyle::Cooked),
AsmArg::Operand(op) => {
let print_reg_or_class = |s: &mut Self, r: &InlineAsmRegOrRegClass| match r {
InlineAsmRegOrRegClass::Reg(r) => s.print_symbol(*r, ast::StrStyle::Cooked),
self.print_path(path, true, 0);
}
self.popen();
- self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p));
+ self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
self.pclose();
}
PatKind::Or(pats) => {
- self.strsep("|", true, Inconsistent, &pats, |s, p| s.print_pat(p));
+ self.strsep("|", true, Inconsistent, pats, |s, p| s.print_pat(p));
}
PatKind::Path(None, path) => {
self.print_path(path, true, 0);
}
self.commasep_cmnt(
Consistent,
- &fields,
+ fields,
|s, f| {
s.cbox(INDENT_UNIT);
if !f.is_shorthand {
}
PatKind::Tuple(elts) => {
self.popen();
- self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p));
+ self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
if elts.len() == 1 {
self.word(",");
}
self.print_pat(inner);
}
}
- PatKind::Lit(e) => self.print_expr(&**e),
+ PatKind::Lit(e) => self.print_expr(e),
PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => {
if let Some(e) = begin {
self.print_expr(e);
}
PatKind::Slice(elts) => {
self.word("[");
- self.commasep(Inconsistent, &elts, |s, p| s.print_pat(p));
+ self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
self.word("]");
}
PatKind::Rest => self.word(".."),
self.word("<");
- self.commasep(Inconsistent, &generic_params, |s, param| {
+ self.commasep(Inconsistent, generic_params, |s, param| {
s.print_outer_attributes_inline(¶m.attrs);
match ¶m.kind {
self.print_expr_tup(exprs);
}
ast::ExprKind::Call(func, args) => {
- self.print_expr_call(func, &args);
+ self.print_expr_call(func, args);
}
ast::ExprKind::MethodCall(box ast::MethodCall { seg, receiver, args, .. }) => {
- self.print_expr_method_call(seg, &receiver, &args);
+ self.print_expr_method_call(seg, receiver, args);
}
ast::ExprKind::Binary(op, lhs, rhs) => {
self.print_expr_binary(*op, lhs, rhs);
fn_decl,
body,
fn_decl_span: _,
+ fn_arg_span: _,
}) => {
self.print_closure_binder(binder);
self.print_movability(*movability);
match binder {
ast::ClosureBinder::NotPresent => {}
ast::ClosureBinder::For { generic_params, .. } => {
- self.print_formal_generic_params(&generic_params)
+ self.print_formal_generic_params(generic_params)
}
}
}
self.head(visibility_qualified(&item.vis, "trait"));
self.print_ident(item.ident);
self.print_generic_params(&generics.params);
- let mut real_bounds = Vec::with_capacity(bounds.len());
- // FIXME(durka) this seems to be some quite outdated syntax
- for b in bounds.iter() {
- if let GenericBound::Trait(ptr, ast::TraitBoundModifier::Maybe) = b {
- self.space();
- self.word_space("for ?");
- self.print_trait_ref(&ptr.trait_ref);
- } else {
- real_bounds.push(b.clone());
- }
- }
self.nbsp();
- if !real_bounds.is_empty() {
+ if !bounds.is_empty() {
self.word_nbsp("=");
- self.print_type_bounds(&real_bounds);
+ self.print_type_bounds(&bounds);
}
self.print_where_clause(&generics.where_clause);
self.word(";");
fn print_use_tree(&mut self, tree: &ast::UseTree) {
match &tree.kind {
- ast::UseTreeKind::Simple(rename, ..) => {
+ ast::UseTreeKind::Simple(rename) => {
self.print_path(&tree.prefix, false, 0);
if let &Some(rename) = rename {
self.nbsp();
allowed_through_unstable_modules = true;
}
// attributes with data
- else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta {
- let meta = meta.as_ref().unwrap();
+ else if let Some(meta @ MetaItem { kind: MetaItemKind::List(metas), .. }) = &meta {
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
handle_errors(
// Merge the const-unstable info into the stability info
if promotable {
- if let Some((ref mut stab, _)) = const_stab {
- stab.promotable = promotable;
- } else {
- sess.emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp });
+ match &mut const_stab {
+ Some((stab, _)) => stab.promotable = promotable,
+ _ => _ = sess.emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp }),
}
}
if allowed_through_unstable_modules {
- if let Some((
- Stability {
- level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. },
- ..
- },
- _,
- )) = stab
- {
- *allowed_through_unstable_modules = true;
- } else {
- sess.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
+ match &mut stab {
+ Some((
+ Stability {
+ level: StabilityLevel::Stable { allowed_through_unstable_modules, .. },
+ ..
+ },
+ _,
+ )) => *allowed_through_unstable_modules = true,
+ _ => {
+ sess.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
+ }
}
}
features: Option<&Features>,
eval: &mut impl FnMut(Condition) -> bool,
) -> bool {
- match cfg.kind {
- ast::MetaItemKind::List(ref mis) if cfg.name_or_empty() == sym::version => {
+ match &cfg.kind {
+ ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
try_gate_cfg(sym::version, cfg.span, sess, features);
let (min_version, span) = match &mis[..] {
[NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
rustc_version >= min_version
}
}
- ast::MetaItemKind::List(ref mis) => {
+ ast::MetaItemKind::List(mis) => {
for mi in mis.iter() {
if !mi.is_meta_item() {
handle_errors(
sess.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
true
}
- MetaItemKind::NameValue(ref lit) if !lit.kind.is_str() => {
+ MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
handle_errors(
sess,
lit.span,
});
}
} else if let Some(meta_item) = item.meta_item() {
- if let MetaItemKind::NameValue(ref value) = meta_item.kind {
- if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
- let name = meta_item.name_or_empty().to_ident_string();
- recognised = true;
- sess.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
- span: item.span(),
- repr_arg: &name,
- cause: IncorrectReprFormatGenericCause::from_lit_kind(
- item.span(),
- &value.kind,
- &name,
- ),
- });
- } else if matches!(
- meta_item.name_or_empty(),
- sym::C | sym::simd | sym::transparent
- ) || int_type_of_word(meta_item.name_or_empty()).is_some()
- {
- recognised = true;
- sess.emit_err(session_diagnostics::InvalidReprHintNoValue {
- span: meta_item.span,
- name: meta_item.name_or_empty().to_ident_string(),
- });
+ match &meta_item.kind {
+ MetaItemKind::NameValue(value) => {
+ if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
+ let name = meta_item.name_or_empty().to_ident_string();
+ recognised = true;
+ sess.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
+ span: item.span(),
+ repr_arg: &name,
+ cause: IncorrectReprFormatGenericCause::from_lit_kind(
+ item.span(),
+ &value.kind,
+ &name,
+ ),
+ });
+ } else if matches!(
+ meta_item.name_or_empty(),
+ sym::C | sym::simd | sym::transparent
+ ) || int_type_of_word(meta_item.name_or_empty()).is_some()
+ {
+ recognised = true;
+ sess.emit_err(session_diagnostics::InvalidReprHintNoValue {
+ span: meta_item.span,
+ name: meta_item.name_or_empty().to_ident_string(),
+ });
+ }
}
- } else if let MetaItemKind::List(_) = meta_item.kind {
- if meta_item.has_name(sym::align) {
- recognised = true;
- sess.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
- span: meta_item.span,
- });
- } else if meta_item.has_name(sym::packed) {
- recognised = true;
- sess.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
- span: meta_item.span,
- });
- } else if matches!(
- meta_item.name_or_empty(),
- sym::C | sym::simd | sym::transparent
- ) || int_type_of_word(meta_item.name_or_empty()).is_some()
- {
- recognised = true;
- sess.emit_err(session_diagnostics::InvalidReprHintNoParen {
- span: meta_item.span,
- name: meta_item.name_or_empty().to_ident_string(),
- });
+ MetaItemKind::List(_) => {
+ if meta_item.has_name(sym::align) {
+ recognised = true;
+ sess.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
+ span: meta_item.span,
+ });
+ } else if meta_item.has_name(sym::packed) {
+ recognised = true;
+ sess.emit_err(
+ session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
+ span: meta_item.span,
+ },
+ );
+ } else if matches!(
+ meta_item.name_or_empty(),
+ sym::C | sym::simd | sym::transparent
+ ) || int_type_of_word(meta_item.name_or_empty()).is_some()
+ {
+ recognised = true;
+ sess.emit_err(session_diagnostics::InvalidReprHintNoParen {
+ span: meta_item.span,
+ name: meta_item.name_or_empty().to_ident_string(),
+ });
+ }
}
+ _ => (),
}
}
if !recognised {
rvalue: &mir::Rvalue<'tcx>,
location: mir::Location,
) {
- if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue {
+ if let &mir::Rvalue::Ref(region, kind, borrowed_place) = rvalue {
if borrowed_place.ignore_borrow(self.tcx, self.body, &self.locals_state_at_exit) {
debug!("ignoring_borrow of {:?}", borrowed_place);
return;
region,
reserve_location: location,
activation_location: TwoPhaseActivation::NotTwoPhase,
- borrowed_place: *borrowed_place,
+ borrowed_place,
assigned_place: *assigned_place,
};
let (idx, _) = self.location_map.insert_full(location, borrow);
}
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) {
- if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue {
+ if let &mir::Rvalue::Ref(region, kind, place) = rvalue {
// double-check that we already registered a BorrowData for this
let borrow_data = &self.location_map[&location];
assert_eq!(borrow_data.reserve_location, location);
assert_eq!(borrow_data.kind, kind);
assert_eq!(borrow_data.region, region.to_region_vid());
- assert_eq!(borrow_data.borrowed_place, *place);
+ assert_eq!(borrow_data.borrowed_place, place);
}
self.super_rvalue(rvalue, location)
stmt: &mir::Statement<'tcx>,
location: Location,
) {
- match stmt.kind {
- mir::StatementKind::Assign(box (lhs, ref rhs)) => {
- if let mir::Rvalue::Ref(_, _, place) = *rhs {
+ match &stmt.kind {
+ mir::StatementKind::Assign(box (lhs, rhs)) => {
+ if let mir::Rvalue::Ref(_, _, place) = rhs {
if place.ignore_borrow(
self.tcx,
self.body,
// Make sure there are no remaining borrows for variables
// that are assigned over.
- self.kill_borrows_on_place(trans, lhs);
+ self.kill_borrows_on_place(trans, *lhs);
}
mir::StatementKind::StorageDead(local) => {
// Make sure there are no remaining borrows for locals that
// are gone out of scope.
- self.kill_borrows_on_place(trans, Place::from(local));
+ self.kill_borrows_on_place(trans, Place::from(*local));
}
mir::StatementKind::FakeRead(..)
placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
- let (ref infcx, key, _) =
+ let (infcx, key, _) =
mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
- let ocx = ObligationCtxt::new(infcx);
+ let ocx = ObligationCtxt::new(&infcx);
type_op_prove_predicate_with_cause(&ocx, key, cause);
try_extract_error_from_fulfill_cx(&ocx, placeholder_region, error_region)
}
placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
- let (ref infcx, key, _) =
+ let (infcx, key, _) =
mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
- let ocx = ObligationCtxt::new(infcx);
+ let ocx = ObligationCtxt::new(&infcx);
// FIXME(lqd): Unify and de-duplicate the following with the actual
// `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the
placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>,
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
- let (ref infcx, key, _) =
+ let (infcx, key, _) =
mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
- let ocx = ObligationCtxt::new(infcx);
+ let ocx = ObligationCtxt::new(&infcx);
type_op_ascribe_user_type_with_span(&ocx, key, Some(cause.span)).ok()?;
try_extract_error_from_fulfill_cx(&ocx, placeholder_region, error_region)
}
DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
);
let note_msg = match opt_name {
- Some(ref name) => format!("`{}`", name),
+ Some(name) => format!("`{}`", name),
None => "value".to_owned(),
};
if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) {
// then just use the normal error. The closure isn't escaping
// and `move` will not help here.
(
- Some(ref name),
+ Some(name),
BorrowExplanation::MustBeValidFor {
category:
category @ (ConstraintCategory::Return(_)
&format!("`{}`", name),
),
(
- ref name,
+ name,
BorrowExplanation::MustBeValidFor {
category: ConstraintCategory::Assignment,
from_closure: false,
span,
..
},
- ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
+ ) => self.report_escaping_data(borrow_span, &name, upvar_span, upvar_name, span),
(Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
location,
&name,
// and it'll make sense.
let location = borrow.reserve_location;
debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
- if let Some(&Statement { kind: StatementKind::Assign(box (ref reservation, _)), .. }) =
+ if let Some(Statement { kind: StatementKind::Assign(box (reservation, _)), .. }) =
&self.body[location.block].statements.get(location.statement_index)
{
debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
// Check if our `target` was captured by a closure.
if let Rvalue::Aggregate(
box AggregateKind::Closure(def_id, substs),
- ref operands,
- ) = *rvalue
+ operands,
+ ) = rvalue
{
for operand in operands {
let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
// into a place then we should annotate the closure in
// case it ends up being assigned into the return place.
annotated_closure =
- self.annotate_fn_sig(def_id, substs.as_closure().sig());
+ self.annotate_fn_sig(*def_id, substs.as_closure().sig());
debug!(
"annotate_argument_and_return_for_borrow: \
annotated_closure={:?} assigned_from_local={:?} \
} else if self.was_captured_by_trait_object(borrow) {
LaterUseKind::TraitCapture
} else if location.statement_index == block.statements.len() {
- if let TerminatorKind::Call { ref func, from_hir_call: true, .. } =
- block.terminator().kind
+ if let TerminatorKind::Call { func, from_hir_call: true, .. } =
+ &block.terminator().kind
{
// Just point to the function, to reduce the chance of overlapping spans.
let function_span = match func {
// will only ever have one item at any given time, but by using a vector, we can pop from
// it which simplifies the termination logic.
let mut queue = vec![location];
- let mut target = if let Some(&Statement {
- kind: StatementKind::Assign(box (ref place, _)),
- ..
- }) = stmt
- {
- if let Some(local) = place.as_local() {
- local
+ let mut target =
+ if let Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) = stmt {
+ if let Some(local) = place.as_local() {
+ local
+ } else {
+ return false;
+ }
} else {
return false;
- }
- } else {
- return false;
- };
+ };
debug!("was_captured_by_trait: target={:?} queue={:?}", target, queue);
while let Some(current_location) = queue.pop() {
if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind {
debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
match from {
- Operand::Copy(ref place) | Operand::Move(ref place)
+ Operand::Copy(place) | Operand::Move(place)
if target == place.local_or_deref_local() =>
{
target = into.local_or_deref_local()
debug!("add_moved_or_invoked_closure_note: id={:?}", id);
if Some(self.infcx.tcx.parent(id)) == self.infcx.tcx.lang_items().fn_once_trait() {
let closure = match args.first() {
- Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place))
+ Some(Operand::Copy(place) | Operand::Move(place))
if target == place.local_or_deref_local() =>
{
place.local_or_deref_local().unwrap()
if !is_terminator {
continue;
} else if let Some(Terminator {
- kind: TerminatorKind::Call { ref func, from_hir_call: false, .. },
+ kind: TerminatorKind::Call { func, from_hir_call: false, .. },
..
- }) = bbd.terminator
+ }) = &bbd.terminator
{
if let Some(source) =
BorrowedContentSource::from_call(func.ty(self.body, tcx), tcx)
};
debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
- if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) = stmt.kind {
- match **kind {
- AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) => {
- debug!("move_spans: def_id={:?} places={:?}", def_id, places);
- if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
- self.closure_span(def_id, moved_place, places)
- {
- return ClosureUse {
- generator_kind,
- args_span,
- capture_kind_span,
- path_span,
- };
- }
- }
- _ => {}
+ if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind
+ && let AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) = **kind
+ {
+ debug!("move_spans: def_id={:?} places={:?}", def_id, places);
+ if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
+ self.closure_span(def_id, moved_place, places)
+ {
+ return ClosureUse {
+ generator_kind,
+ args_span,
+ capture_kind_span,
+ path_span,
+ };
}
}
// StatementKind::FakeRead only contains a def_id if they are introduced as a result
// of pattern matching within a closure.
- if let StatementKind::FakeRead(box (cause, ref place)) = stmt.kind {
+ if let StatementKind::FakeRead(box (cause, place)) = stmt.kind {
match cause {
FakeReadCause::ForMatchedPlace(Some(closure_def_id))
| FakeReadCause::ForLet(Some(closure_def_id)) => {
debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
- let places = &[Operand::Move(*place)];
+ let places = &[Operand::Move(place)];
if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
self.closure_span(closure_def_id, moved_place, places)
{
debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
let target = match self.body[location.block].statements.get(location.statement_index) {
- Some(&Statement { kind: StatementKind::Assign(box (ref place, _)), .. }) => {
+ Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) => {
if let Some(local) = place.as_local() {
local
} else {
}
for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
- if let StatementKind::Assign(box (_, Rvalue::Aggregate(ref kind, ref places))) =
- stmt.kind
- {
+ if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind {
let (&def_id, is_generator) = match kind {
box AggregateKind::Closure(def_id, _) => (def_id, false),
box AggregateKind::Generator(def_id, _, _) => (def_id, true),
PlaceRef {
local,
projection:
- &[
- ref proj_base @ ..,
+ [
+ proj_base @ ..,
ProjectionElem::Deref,
ProjectionElem::Field(field, _),
ProjectionElem::Deref,
if let Some(span) = get_mut_span_in_struct_field(
self.infcx.tcx,
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty,
- field,
+ *field,
) {
err.span_suggestion_verbose(
span,
}
}
hir::ExprKind::Block(blk, _) => {
- if let Some(ref expr) = blk.expr {
+ if let Some(expr) = blk.expr {
// only when the block is a closure
if let hir::ExprKind::Closure(hir::Closure {
capture_clause: hir::CaptureBy::Ref,
.or_else(|| self.give_name_if_anonymous_region_appears_in_impl_signature(fr))
.or_else(|| self.give_name_if_anonymous_region_appears_in_arg_position_impl_trait(fr));
- if let Some(ref value) = value {
+ if let Some(value) = &value {
self.region_names.try_borrow_mut().unwrap().insert(fr, value.clone());
}
self.consume_operand(location, op);
}
StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping {
- ref src,
- ref dst,
- ref count,
+ src,
+ dst,
+ count,
})) => {
self.consume_operand(location, src);
self.consume_operand(location, dst);
self.check_activations(location);
match &terminator.kind {
- TerminatorKind::SwitchInt { ref discr, switch_ty: _, targets: _ } => {
+ TerminatorKind::SwitchInt { discr, targets: _ } => {
self.consume_operand(location, discr);
}
TerminatorKind::Drop { place: drop_place, target: _, unwind: _ } => {
}
TerminatorKind::DropAndReplace {
place: drop_place,
- value: ref new_value,
+ value: new_value,
target: _,
unwind: _,
} => {
self.consume_operand(location, new_value);
}
TerminatorKind::Call {
- ref func,
- ref args,
+ func,
+ args,
destination,
target: _,
cleanup: _,
}
self.mutate_place(location, *destination, Deep);
}
- TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => {
+ TerminatorKind::Assert { cond, expected: _, msg, target: _, cleanup: _ } => {
self.consume_operand(location, cond);
use rustc_middle::mir::AssertKind;
- if let AssertKind::BoundsCheck { ref len, ref index } = *msg {
+ if let AssertKind::BoundsCheck { len, index } = msg {
self.consume_operand(location, len);
self.consume_operand(location, index);
}
}
- TerminatorKind::Yield { ref value, resume, resume_arg, drop: _ } => {
+ TerminatorKind::Yield { value, resume, resume_arg, drop: _ } => {
self.consume_operand(location, value);
// Invalidate all borrows of local places
}
TerminatorKind::InlineAsm {
template: _,
- ref operands,
+ operands,
options: _,
line_spans: _,
destination: _,
cleanup: _,
} => {
for op in operands {
- match *op {
- InlineAsmOperand::In { reg: _, ref value } => {
+ match op {
+ InlineAsmOperand::In { reg: _, value } => {
self.consume_operand(location, value);
}
InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
- if let Some(place) = place {
+ if let &Some(place) = place {
self.mutate_place(location, place, Shallow(None));
}
}
- InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
+ InlineAsmOperand::InOut { reg: _, late: _, in_value, out_place } => {
self.consume_operand(location, in_value);
- if let Some(out_place) = out_place {
+ if let &Some(out_place) = out_place {
self.mutate_place(location, out_place, Shallow(None));
}
}
// Simulates consumption of an rvalue
fn consume_rvalue(&mut self, location: Location, rvalue: &Rvalue<'tcx>) {
- match *rvalue {
- Rvalue::Ref(_ /*rgn*/, bk, place) => {
+ match rvalue {
+ &Rvalue::Ref(_ /*rgn*/, bk, place) => {
let access_kind = match bk {
BorrowKind::Shallow => {
(Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
}
- Rvalue::AddressOf(mutability, place) => {
+ &Rvalue::AddressOf(mutability, place) => {
let access_kind = match mutability {
Mutability::Mut => (
Deep,
Rvalue::ThreadLocalRef(_) => {}
- Rvalue::Use(ref operand)
- | Rvalue::Repeat(ref operand, _)
- | Rvalue::UnaryOp(_ /*un_op*/, ref operand)
- | Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/)
- | Rvalue::ShallowInitBox(ref operand, _ /*ty*/) => {
- self.consume_operand(location, operand)
- }
- Rvalue::CopyForDeref(ref place) => {
- let op = &Operand::Copy(*place);
+ Rvalue::Use(operand)
+ | Rvalue::Repeat(operand, _)
+ | Rvalue::UnaryOp(_ /*un_op*/, operand)
+ | Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/)
+ | Rvalue::ShallowInitBox(operand, _ /*ty*/) => self.consume_operand(location, operand),
+
+ &Rvalue::CopyForDeref(place) => {
+ let op = &Operand::Copy(place);
self.consume_operand(location, op);
}
- Rvalue::Len(place) | Rvalue::Discriminant(place) => {
- let af = match *rvalue {
+ &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
+ let af = match rvalue {
Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
Rvalue::Discriminant(..) => None,
_ => unreachable!(),
);
}
- Rvalue::BinaryOp(_bin_op, box (ref operand1, ref operand2))
- | Rvalue::CheckedBinaryOp(_bin_op, box (ref operand1, ref operand2)) => {
+ Rvalue::BinaryOp(_bin_op, box (operand1, operand2))
+ | Rvalue::CheckedBinaryOp(_bin_op, box (operand1, operand2)) => {
self.consume_operand(location, operand1);
self.consume_operand(location, operand2);
}
Rvalue::NullaryOp(_op, _ty) => {}
- Rvalue::Aggregate(_, ref operands) => {
+ Rvalue::Aggregate(_, operands) => {
for operand in operands {
self.consume_operand(location, operand);
}
self.check_activations(location, span, flow_state);
match &stmt.kind {
- StatementKind::Assign(box (lhs, ref rhs)) => {
+ StatementKind::Assign(box (lhs, rhs)) => {
self.consume_rvalue(location, (rhs, span), flow_state);
self.mutate_place(location, (*lhs, span), Shallow(None), flow_state);
}
- StatementKind::FakeRead(box (_, ref place)) => {
+ StatementKind::FakeRead(box (_, place)) => {
// Read for match doesn't access any memory and is used to
// assert that a place is safe and live. So we don't have to
// do any checks here.
flow_state,
);
}
- StatementKind::Intrinsic(box ref kind) => match kind {
+ StatementKind::Intrinsic(box kind) => match kind {
NonDivergingIntrinsic::Assume(op) => self.consume_operand(location, (op, span), flow_state),
NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!(
span,
self.check_activations(loc, span, flow_state);
- match term.kind {
- TerminatorKind::SwitchInt { ref discr, switch_ty: _, targets: _ } => {
+ match &term.kind {
+ TerminatorKind::SwitchInt { discr, targets: _ } => {
self.consume_operand(loc, (discr, span), flow_state);
}
TerminatorKind::Drop { place, target: _, unwind: _ } => {
self.access_place(
loc,
- (place, span),
+ (*place, span),
(AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)),
LocalMutationIsAllowed::Yes,
flow_state,
}
TerminatorKind::DropAndReplace {
place: drop_place,
- value: ref new_value,
+ value: new_value,
target: _,
unwind: _,
} => {
- self.mutate_place(loc, (drop_place, span), Deep, flow_state);
+ self.mutate_place(loc, (*drop_place, span), Deep, flow_state);
self.consume_operand(loc, (new_value, span), flow_state);
}
TerminatorKind::Call {
- ref func,
- ref args,
+ func,
+ args,
destination,
target: _,
cleanup: _,
for arg in args {
self.consume_operand(loc, (arg, span), flow_state);
}
- self.mutate_place(loc, (destination, span), Deep, flow_state);
+ self.mutate_place(loc, (*destination, span), Deep, flow_state);
}
- TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => {
+ TerminatorKind::Assert { cond, expected: _, msg, target: _, cleanup: _ } => {
self.consume_operand(loc, (cond, span), flow_state);
use rustc_middle::mir::AssertKind;
- if let AssertKind::BoundsCheck { ref len, ref index } = *msg {
+ if let AssertKind::BoundsCheck { len, index } = msg {
self.consume_operand(loc, (len, span), flow_state);
self.consume_operand(loc, (index, span), flow_state);
}
}
- TerminatorKind::Yield { ref value, resume: _, resume_arg, drop: _ } => {
+ TerminatorKind::Yield { value, resume: _, resume_arg, drop: _ } => {
self.consume_operand(loc, (value, span), flow_state);
- self.mutate_place(loc, (resume_arg, span), Deep, flow_state);
+ self.mutate_place(loc, (*resume_arg, span), Deep, flow_state);
}
TerminatorKind::InlineAsm {
template: _,
- ref operands,
+ operands,
options: _,
line_spans: _,
destination: _,
cleanup: _,
} => {
for op in operands {
- match *op {
- InlineAsmOperand::In { reg: _, ref value } => {
+ match op {
+ InlineAsmOperand::In { reg: _, value } => {
self.consume_operand(loc, (value, span), flow_state);
}
InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
if let Some(place) = place {
- self.mutate_place(loc, (place, span), Shallow(None), flow_state);
+ self.mutate_place(loc, (*place, span), Shallow(None), flow_state);
}
}
- InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
+ InlineAsmOperand::InOut { reg: _, late: _, in_value, out_place } => {
self.consume_operand(loc, (in_value, span), flow_state);
- if let Some(out_place) = out_place {
+ if let &Some(out_place) = out_place {
self.mutate_place(
loc,
(out_place, span),
(rvalue, span): (&'cx Rvalue<'tcx>, Span),
flow_state: &Flows<'cx, 'tcx>,
) {
- match *rvalue {
- Rvalue::Ref(_ /*rgn*/, bk, place) => {
+ match rvalue {
+ &Rvalue::Ref(_ /*rgn*/, bk, place) => {
let access_kind = match bk {
BorrowKind::Shallow => {
(Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
);
}
- Rvalue::AddressOf(mutability, place) => {
+ &Rvalue::AddressOf(mutability, place) => {
let access_kind = match mutability {
Mutability::Mut => (
Deep,
Rvalue::ThreadLocalRef(_) => {}
- Rvalue::Use(ref operand)
- | Rvalue::Repeat(ref operand, _)
- | Rvalue::UnaryOp(_ /*un_op*/, ref operand)
- | Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/)
- | Rvalue::ShallowInitBox(ref operand, _ /*ty*/) => {
+ Rvalue::Use(operand)
+ | Rvalue::Repeat(operand, _)
+ | Rvalue::UnaryOp(_ /*un_op*/, operand)
+ | Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/)
+ | Rvalue::ShallowInitBox(operand, _ /*ty*/) => {
self.consume_operand(location, (operand, span), flow_state)
}
- Rvalue::CopyForDeref(place) => {
+
+ &Rvalue::CopyForDeref(place) => {
self.access_place(
location,
(place, span),
);
}
- Rvalue::Len(place) | Rvalue::Discriminant(place) => {
+ &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => {
let af = match *rvalue {
Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
Rvalue::Discriminant(..) => None,
);
}
- Rvalue::BinaryOp(_bin_op, box (ref operand1, ref operand2))
- | Rvalue::CheckedBinaryOp(_bin_op, box (ref operand1, ref operand2)) => {
+ Rvalue::BinaryOp(_bin_op, box (operand1, operand2))
+ | Rvalue::CheckedBinaryOp(_bin_op, box (operand1, operand2)) => {
self.consume_operand(location, (operand1, span), flow_state);
self.consume_operand(location, (operand2, span), flow_state);
}
// nullary ops take no dynamic input; no borrowck effect.
}
- Rvalue::Aggregate(ref aggregate_kind, ref operands) => {
+ Rvalue::Aggregate(aggregate_kind, operands) => {
// We need to report back the list of mutable upvars that were
// moved into the closure and subsequently used by the closure,
// in order to populate our used_mut set.
// Replace all remaining regions with fresh inference variables.
renumber::renumber_mir(infcx, body, promoted);
- dump_mir(infcx.tcx, None, "renumber", &0, body, |_, _| Ok(()));
+ dump_mir(infcx.tcx, false, "renumber", &0, body, |_, _| Ok(()));
universal_regions
}
return;
}
- dump_mir(infcx.tcx, None, "nll", &0, body, |pass_where, out| {
+ dump_mir(infcx.tcx, false, "nll", &0, body, |pass_where, out| {
match pass_where {
// Before the CFG, dump out the values for each region variable.
PassWhere::BeforeCFG => {
// Also dump the inference graph constraints as a graphviz file.
let _: io::Result<()> = try {
- let mut file =
- create_dump_file(infcx.tcx, "regioncx.all.dot", None, "nll", &0, body.source)?;
+ let mut file = create_dump_file(infcx.tcx, "regioncx.all.dot", false, "nll", &0, body)?;
regioncx.dump_graphviz_raw_constraints(&mut file)?;
};
// Also dump the inference graph constraints as a graphviz file.
let _: io::Result<()> = try {
- let mut file =
- create_dump_file(infcx.tcx, "regioncx.scc.dot", None, "nll", &0, body.source)?;
+ let mut file = create_dump_file(infcx.tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
regioncx.dump_graphviz_scc_constraints(&mut file)?;
};
}
pub(super) fn prove_predicates(
&mut self,
- predicates: impl IntoIterator<
- Item = impl ToPredicate<'tcx, ty::Predicate<'tcx>> + std::fmt::Debug,
- >,
+ predicates: impl IntoIterator<Item = impl ToPredicate<'tcx> + std::fmt::Debug>,
locations: Locations,
category: ConstraintCategory<'tcx>,
) {
#[instrument(skip(self), level = "debug")]
pub(super) fn prove_predicate(
&mut self,
- predicate: impl ToPredicate<'tcx, ty::Predicate<'tcx>> + std::fmt::Debug,
+ predicate: impl ToPredicate<'tcx> + std::fmt::Debug,
locations: Locations,
category: ConstraintCategory<'tcx>,
) {
fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) {
let tcx = self.tcx();
debug!("stmt kind: {:?}", stmt.kind);
- match stmt.kind {
- StatementKind::Assign(box (ref place, ref rv)) => {
+ match &stmt.kind {
+ StatementKind::Assign(box (place, rv)) => {
// Assignments to temporaries are not "interesting";
// they are not caused by the user, but rather artifacts
// of lowering. Assignments to other sorts of places *are* interesting
);
}
}
- StatementKind::AscribeUserType(box (ref place, ref projection), variance) => {
+ StatementKind::AscribeUserType(box (place, projection), variance) => {
let place_ty = place.ty(body, tcx).ty;
if let Err(terr) = self.relate_type_and_user_type(
place_ty,
- variance,
+ *variance,
projection,
Locations::All(stmt.source_info.span),
ConstraintCategory::TypeAnnotation,
);
}
}
- StatementKind::Intrinsic(box ref kind) => match kind {
+ StatementKind::Intrinsic(box kind) => match kind {
NonDivergingIntrinsic::Assume(op) => self.check_operand(op, location),
NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!(
stmt.source_info.span,
) {
let tcx = self.tcx();
debug!("terminator kind: {:?}", term.kind);
- match term.kind {
+ match &term.kind {
TerminatorKind::Goto { .. }
| TerminatorKind::Resume
| TerminatorKind::Abort
// no checks needed for these
}
- TerminatorKind::DropAndReplace { ref place, ref value, target: _, unwind: _ } => {
+ TerminatorKind::DropAndReplace { place, value, target: _, unwind: _ } => {
let place_ty = place.ty(body, tcx).ty;
let rv_ty = value.ty(body, tcx);
);
}
}
- TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => {
+ TerminatorKind::SwitchInt { discr, .. } => {
self.check_operand(discr, term_location);
- let discr_ty = discr.ty(body, tcx);
- if let Err(terr) = self.sub_types(
- discr_ty,
- switch_ty,
- term_location.to_locations(),
- ConstraintCategory::Assignment,
- ) {
- span_mirbug!(
- self,
- term,
- "bad SwitchInt ({:?} on {:?}): {:?}",
- switch_ty,
- discr_ty,
- terr
- );
- }
+ let switch_ty = discr.ty(body, tcx);
if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() {
span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty);
}
// FIXME: check the values
}
- TerminatorKind::Call {
- ref func,
- ref args,
- ref destination,
- from_hir_call,
- target,
- ..
- } => {
+ TerminatorKind::Call { func, args, destination, from_hir_call, target, .. } => {
self.check_operand(func, term_location);
for arg in args {
self.check_operand(arg, term_location);
ConstraintCategory::Boring,
);
let sig = self.normalize(sig, term_location);
- self.check_call_dest(body, term, &sig, *destination, target, term_location);
+ self.check_call_dest(body, term, &sig, *destination, *target, term_location);
// The ordinary liveness rules will ensure that all
// regions in the type of the callee are live here. We
.add_element(region_vid, term_location);
}
- self.check_call_inputs(body, term, &sig, args, term_location, from_hir_call);
+ self.check_call_inputs(body, term, &sig, args, term_location, *from_hir_call);
}
- TerminatorKind::Assert { ref cond, ref msg, .. } => {
+ TerminatorKind::Assert { cond, msg, .. } => {
self.check_operand(cond, term_location);
let cond_ty = cond.ty(body, tcx);
span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
}
- if let AssertKind::BoundsCheck { ref len, ref index } = *msg {
+ if let AssertKind::BoundsCheck { len, index } = msg {
if len.ty(body, tcx) != tcx.types.usize {
span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
}
}
}
}
- TerminatorKind::Yield { ref value, .. } => {
+ TerminatorKind::Yield { value, .. } => {
self.check_operand(value, term_location);
let value_ty = value.ty(body, tcx);
substs: SubstsRef<'tcx>,
location: Location,
) -> ty::InstantiatedPredicates<'tcx> {
- if let Some(ref closure_requirements) = tcx.mir_borrowck(def_id).closure_requirements {
+ if let Some(closure_requirements) = &tcx.mir_borrowck(def_id).closure_requirements {
constraint_conversion::ConstraintConversion::new(
self.infcx,
self.borrowck_context.universal_regions,
let typeck_root_def_id = tcx.typeck_root_def_id(self.mir_def.did.to_def_id());
let identity_substs = InternalSubsts::identity_for_item(tcx, typeck_root_def_id);
let fr_substs = match defining_ty {
- DefiningTy::Closure(_, ref substs)
- | DefiningTy::Generator(_, ref substs, _)
- | DefiningTy::InlineConst(_, ref substs) => {
+ DefiningTy::Closure(_, substs)
+ | DefiningTy::Generator(_, substs, _)
+ | DefiningTy::InlineConst(_, substs) => {
// In the case of closures, we rely on the fact that
// the first N elements in the ClosureSubsts are
// inherited from the `typeck_root_def_id`.
check_builtin_macro_attribute(ecx, meta_item, sym::alloc_error_handler);
let orig_item = item.clone();
- let not_function = || {
- ecx.sess
- .parse_sess
- .span_diagnostic
- .span_err(item.span(), "alloc_error_handler must be a function");
- vec![orig_item.clone()]
- };
// Allow using `#[alloc_error_handler]` on an item statement
// FIXME - if we get deref patterns, use them to reduce duplication here
- let (item, is_stmt, sig_span) = match &item {
- Annotatable::Item(item) => match item.kind {
- ItemKind::Fn(ref fn_kind) => (item, false, ecx.with_def_site_ctxt(fn_kind.sig.span)),
- _ => return not_function(),
- },
- Annotatable::Stmt(stmt) => match &stmt.kind {
- StmtKind::Item(item_) => match item_.kind {
- ItemKind::Fn(ref fn_kind) => {
- (item_, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
- }
- _ => return not_function(),
- },
- _ => return not_function(),
- },
- _ => return not_function(),
- };
+ let (item, is_stmt, sig_span) =
+ if let Annotatable::Item(item) = &item
+ && let ItemKind::Fn(fn_kind) = &item.kind
+ {
+ (item, false, ecx.with_def_site_ctxt(fn_kind.sig.span))
+ } else if let Annotatable::Stmt(stmt) = &item
+ && let StmtKind::Item(item) = &stmt.kind
+ && let ItemKind::Fn(fn_kind) = &item.kind
+ {
+ (item, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
+ } else {
+ ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "alloc_error_handler must be a function");
+ return vec![orig_item.clone()];
+ };
// Generate a bunch of new items using the AllocFnFactory
let span = ecx.with_def_site_ctxt(item.span);
(
UseTree {
prefix: this.cx.path(this.span, vec![Ident::with_dummy_span(sym)]),
- kind: UseTreeKind::Simple(None, DUMMY_NODE_ID, DUMMY_NODE_ID),
+ kind: UseTreeKind::Simple(None),
span: this.span,
},
DUMMY_NODE_ID,
///
/// See [Self::manage_initial_capture] and [Self::manage_try_capture]
fn manage_cond_expr(&mut self, expr: &mut P<Expr>) {
- match (*expr).kind {
- ExprKind::AddrOf(_, mutability, ref mut local_expr) => {
+ match &mut expr.kind {
+ ExprKind::AddrOf(_, mutability, local_expr) => {
self.with_is_consumed_management(
matches!(mutability, Mutability::Mut),
|this| this.manage_cond_expr(local_expr)
);
}
- ExprKind::Array(ref mut local_exprs) => {
+ ExprKind::Array(local_exprs) => {
for local_expr in local_exprs {
self.manage_cond_expr(local_expr);
}
}
- ExprKind::Binary(ref op, ref mut lhs, ref mut rhs) => {
+ ExprKind::Binary(op, lhs, rhs) => {
self.with_is_consumed_management(
matches!(
op.node,
}
);
}
- ExprKind::Call(_, ref mut local_exprs) => {
+ ExprKind::Call(_, local_exprs) => {
for local_expr in local_exprs {
self.manage_cond_expr(local_expr);
}
}
- ExprKind::Cast(ref mut local_expr, _) => {
+ ExprKind::Cast(local_expr, _) => {
self.manage_cond_expr(local_expr);
}
- ExprKind::Index(ref mut prefix, ref mut suffix) => {
+ ExprKind::Index(prefix, suffix) => {
self.manage_cond_expr(prefix);
self.manage_cond_expr(suffix);
}
- ExprKind::MethodCall(ref mut call) => {
- for arg in call.args.iter_mut() {
+ ExprKind::MethodCall(call) => {
+ for arg in &mut call.args {
self.manage_cond_expr(arg);
}
}
- ExprKind::Path(_, Path { ref segments, .. }) if let &[ref path_segment] = &segments[..] => {
+ ExprKind::Path(_, Path { segments, .. }) if let [path_segment] = &segments[..] => {
let path_ident = path_segment.ident;
self.manage_initial_capture(expr, path_ident);
}
- ExprKind::Paren(ref mut local_expr) => {
+ ExprKind::Paren(local_expr) => {
self.manage_cond_expr(local_expr);
}
- ExprKind::Range(ref mut prefix, ref mut suffix, _) => {
- if let Some(ref mut elem) = prefix {
+ ExprKind::Range(prefix, suffix, _) => {
+ if let Some(elem) = prefix {
self.manage_cond_expr(elem);
}
- if let Some(ref mut elem) = suffix {
+ if let Some(elem) = suffix {
self.manage_cond_expr(elem);
}
}
- ExprKind::Repeat(ref mut local_expr, ref mut elem) => {
+ ExprKind::Repeat(local_expr, elem) => {
self.manage_cond_expr(local_expr);
self.manage_cond_expr(&mut elem.value);
}
- ExprKind::Struct(ref mut elem) => {
+ ExprKind::Struct(elem) => {
for field in &mut elem.fields {
self.manage_cond_expr(&mut field.expr);
}
- if let StructRest::Base(ref mut local_expr) = elem.rest {
+ if let StructRest::Base(local_expr) = &mut elem.rest {
self.manage_cond_expr(local_expr);
}
}
- ExprKind::Tup(ref mut local_exprs) => {
+ ExprKind::Tup(local_exprs) => {
for local_expr in local_exprs {
self.manage_cond_expr(local_expr);
}
}
- ExprKind::Unary(un_op, ref mut local_expr) => {
+ ExprKind::Unary(un_op, local_expr) => {
self.with_is_consumed_management(
matches!(un_op, UnOp::Neg | UnOp::Not),
|this| this.manage_cond_expr(local_expr)
use rustc_session::errors::report_lit_error;
use rustc_span::symbol::Symbol;
-use std::string::String;
-
pub fn expand_concat(
cx: &mut base::ExtCtxt<'_>,
sp: rustc_span::Span,
tts: TokenStream,
) -> Box<dyn base::MacResult + 'static> {
- let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
+ let Some(es) = base::get_exprs_from_tts(cx, tts) else {
return DummyResult::any(sp);
};
let mut accumulator = String::new();
for e in es {
match e.kind {
ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
- Ok(ast::LitKind::Str(ref s, _) | ast::LitKind::Float(ref s, _)) => {
+ Ok(ast::LitKind::Str(s, _) | ast::LitKind::Float(s, _)) => {
accumulator.push_str(s.as_str());
}
Ok(ast::LitKind::Char(c)) => {
sp: rustc_span::Span,
tts: TokenStream,
) -> Box<dyn base::MacResult + 'static> {
- let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
+ let Some(es) = base::get_exprs_from_tts(cx, tts) else {
return DummyResult::any(sp);
};
let mut accumulator = Vec::new();
let mut missing_literals = vec![];
let mut has_errors = false;
for e in es {
- match e.kind {
- ast::ExprKind::Array(ref exprs) => {
+ match &e.kind {
+ ast::ExprKind::Array(exprs) => {
for expr in exprs {
if let Some(elem) =
handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
}
}
}
- ast::ExprKind::Repeat(ref expr, ref count) => {
+ ast::ExprKind::Repeat(expr, count) => {
if let ast::ExprKind::Lit(token_lit) = count.value.kind
&& let Ok(ast::LitKind::Int(count_val, _)) =
ast::LitKind::from_token_lit(token_lit)
cx.span_err(count.value.span, "repeat count is not a positive number");
}
}
- ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
+ &ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
Ok(ast::LitKind::Byte(val)) => {
accumulator.push(val);
}
has_errors = true;
}
},
- ast::ExprKind::IncludedBytes(ref bytes) => {
+ ast::ExprKind::IncludedBytes(bytes) => {
accumulator.extend_from_slice(bytes);
}
ast::ExprKind::Err => {
let bounds;
let substructure;
let is_simple;
- match *item {
- Annotatable::Item(ref annitem) => match annitem.kind {
- ItemKind::Struct(_, Generics { ref params, .. })
- | ItemKind::Enum(_, Generics { ref params, .. }) => {
+ match item {
+ Annotatable::Item(annitem) => match &annitem.kind {
+ ItemKind::Struct(_, Generics { params, .. })
+ | ItemKind::Enum(_, Generics { params, .. }) => {
let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
let has_derive_copy = cx.resolver.has_derive_copy(container_id);
if has_derive_copy
};
let vdata;
- match *substr.fields {
- Struct(vdata_, ref af) => {
+ match substr.fields {
+ Struct(vdata_, af) => {
ctor_path = cx.path(trait_span, vec![substr.type_ident]);
all_fields = af;
- vdata = vdata_;
+ vdata = *vdata_;
}
- EnumMatching(.., variant, ref af) => {
+ EnumMatching(.., variant, af) => {
ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]);
all_fields = af;
vdata = &variant.data;
let blkarg = Ident::new(sym::_d, trait_span);
let blkdecoder = cx.expr_ident(trait_span, blkarg);
- let expr = match *substr.fields {
- StaticStruct(_, ref summary) => {
- let nfields = match *summary {
- Unnamed(ref fields, _) => fields.len(),
- Named(ref fields) => fields.len(),
+ let expr = match substr.fields {
+ StaticStruct(_, summary) => {
+ let nfields = match summary {
+ Unnamed(fields, _) => fields.len(),
+ Named(fields) => fields.len(),
};
let fn_read_struct_field_path: Vec<_> =
cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_struct_field]);
],
)
}
- StaticEnum(_, ref fields) => {
+ StaticEnum(_, fields) => {
let variant = Ident::new(sym::i, trait_span);
let mut arms = Vec::with_capacity(fields.len() + 1);
where
F: FnMut(&mut ExtCtxt<'_>, Span, Symbol, usize) -> P<Expr>,
{
- match *fields {
- Unnamed(ref fields, is_tuple) => {
+ match fields {
+ Unnamed(fields, is_tuple) => {
let path_expr = cx.expr_path(outer_pat_path);
- if !is_tuple {
+ if !*is_tuple {
path_expr
} else {
let fields = fields
cx.expr_call(trait_span, path_expr, fields)
}
}
- Named(ref fields) => {
+ Named(fields) => {
// use the field's span to get nicer error messages.
let fields = fields
.iter()
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new());
let expr = match summary {
- Unnamed(ref fields, is_tuple) => {
- if !is_tuple {
- cx.expr_ident(trait_span, substr.type_ident)
- } else {
- let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
- cx.expr_call_ident(trait_span, substr.type_ident, exprs)
- }
+ Unnamed(_, false) => cx.expr_ident(trait_span, substr.type_ident),
+ Unnamed(fields, true) => {
+ let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
+ cx.expr_call_ident(trait_span, substr.type_ident, exprs)
}
- Named(ref fields) => {
+ Named(fields) => {
let default_fields = fields
.iter()
.map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
],
));
- match *substr.fields {
- Struct(_, ref fields) => {
+ match substr.fields {
+ Struct(_, fields) => {
let fn_emit_struct_field_path =
cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct_field]);
let mut stmts = Vec::new();
BlockOrExpr::new_expr(expr)
}
- EnumMatching(idx, _, variant, ref fields) => {
+ EnumMatching(idx, _, variant, fields) => {
// We're not generating an AST that the borrow checker is expecting,
// so we need to generate a unique local variable to take the
// mutable loan out on, otherwise we get conflicts which don't
vec![
blkencoder,
name,
- cx.expr_usize(trait_span, idx),
+ cx.expr_usize(trait_span, *idx),
cx.expr_usize(trait_span, fields.len()),
blk,
],
impl<'a, 'b> visit::Visitor<'a> for Visitor<'a, 'b> {
fn visit_ty(&mut self, ty: &'a ast::Ty) {
- if let ast::TyKind::Path(_, ref path) = ty.kind {
- if let Some(segment) = path.segments.first() {
- if self.ty_param_names.contains(&segment.ident.name) {
- self.type_params.push(TypeParameter {
- bound_generic_params: self.bound_generic_params_stack.clone(),
- ty: P(ty.clone()),
- });
- }
- }
+ if let ast::TyKind::Path(_, path) = &ty.kind
+ && let Some(segment) = path.segments.first()
+ && self.ty_param_names.contains(&segment.ident.name)
+ {
+ self.type_params.push(TypeParameter {
+ bound_generic_params: self.bound_generic_params_stack.clone(),
+ ty: P(ty.clone()),
+ });
}
visit::walk_ty(self, ty)
push: &mut dyn FnMut(Annotatable),
from_scratch: bool,
) {
- match *item {
- Annotatable::Item(ref item) => {
+ match item {
+ Annotatable::Item(item) => {
let is_packed = item.attrs.iter().any(|attr| {
for r in attr::find_repr_attrs(&cx.sess, attr) {
if let attr::ReprPacked(_) = r {
}
false
});
- let has_no_type_params = match item.kind {
- ast::ItemKind::Struct(_, ref generics)
- | ast::ItemKind::Enum(_, ref generics)
- | ast::ItemKind::Union(_, ref generics) => !generics
+ let has_no_type_params = match &item.kind {
+ ast::ItemKind::Struct(_, generics)
+ | ast::ItemKind::Enum(_, generics)
+ | ast::ItemKind::Union(_, generics) => !generics
.params
.iter()
.any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })),
let copy_fields =
is_packed && has_no_type_params && cx.resolver.has_derive_copy(container_id);
- let newitem = match item.kind {
- ast::ItemKind::Struct(ref struct_def, ref generics) => self.expand_struct_def(
+ let newitem = match &item.kind {
+ ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def(
cx,
&struct_def,
item.ident,
from_scratch,
copy_fields,
),
- ast::ItemKind::Enum(ref enum_def, ref generics) => {
+ ast::ItemKind::Enum(enum_def, generics) => {
// We ignore `is_packed` here, because `repr(packed)`
// enums cause an error later on.
//
// downstream in blatantly illegal code, so it is fine.
self.expand_enum_def(cx, enum_def, item.ident, generics, from_scratch)
}
- ast::ItemKind::Union(ref struct_def, ref generics) => {
+ ast::ItemKind::Union(struct_def, generics) => {
if self.supports_unions {
self.expand_struct_def(
cx,
for field_ty_param in field_ty_params {
// if we have already handled this type, skip it
- if let ast::TyKind::Path(_, ref p) = field_ty_param.ty.kind {
- if p.segments.len() == 1
- && ty_param_names.contains(&p.segments[0].ident.name)
- {
- continue;
- };
+ if let ast::TyKind::Path(_, p) = &field_ty_param.ty.kind
+ && let [sole_segment] = &*p.segments
+ && ty_param_names.contains(&sole_segment.ident.name)
+ {
+ continue;
}
let mut bounds: Vec<_> = self
.additional_bounds
self_ty: Ident,
generics: &Generics,
) -> ast::Path {
- match *self {
+ match self {
Self_ => {
let params: Vec<_> = generics
.params
cx.path_all(span, false, vec![self_ty], params)
}
- Path(ref p) => p.to_path(cx, span, self_ty, generics),
+ Path(p) => p.to_path(cx, span, self_ty, generics),
Ref(..) => cx.span_bug(span, "ref in a path in generic `derive`"),
Unit => cx.span_bug(span, "unit in a path in generic `derive`"),
}
let params = self
.bounds
.iter()
- .map(|t| {
- let (name, ref bounds) = *t;
- mk_ty_param(cx, span, name, &bounds, self_ty, self_generics)
- })
+ .map(|&(name, ref bounds)| mk_ty_param(cx, span, name, &bounds, self_ty, self_generics))
.collect();
Generics {
structural_path: generic::ty::Path,
push: &mut dyn FnMut(Annotatable),
) {
- let Annotatable::Item(ref item) = *item else {
+ let Annotatable::Item(item) = item else {
unreachable!();
};
- let generics = match item.kind {
- ItemKind::Struct(_, ref generics) | ItemKind::Enum(_, ref generics) => generics,
+ let generics = match &item.kind {
+ ItemKind::Struct(_, generics) | ItemKind::Enum(_, generics) => generics,
// Do not inject `impl Structural for Union`. (`PartialEq` does not
// support unions, so we will see error downstream.)
ItemKind::Union(..) => return,
sp: Span,
tts: TokenStream,
) -> Box<dyn base::MacResult + 'cx> {
- let mut exprs = match get_exprs_from_tts(cx, sp, tts) {
- Some(ref exprs) if exprs.is_empty() => {
+ let mut exprs = match get_exprs_from_tts(cx, tts) {
+ Some(exprs) if exprs.is_empty() => {
cx.span_err(sp, "env! takes 1 or 2 arguments");
return DummyResult::any(sp);
}
parse::Piece::String(s) => {
unfinished_literal.push_str(s);
}
- parse::Piece::NextArgument(parse::Argument { position, position_span, format }) => {
+ parse::Piece::NextArgument(box parse::Argument { position, position_span, format }) => {
if !unfinished_literal.is_empty() {
template.push(FormatArgsPiece::Literal(Symbol::intern(&unfinished_literal)));
unfinished_literal.clear();
impl<'a> Substitution<'a> {
pub fn as_str(&self) -> &str {
- match *self {
- Substitution::Format(ref fmt) => fmt.span,
+ match self {
+ Substitution::Format(fmt) => fmt.span,
Substitution::Escape(_) => "%%",
}
}
pub fn position(&self) -> Option<InnerSpan> {
- match *self {
- Substitution::Format(ref fmt) => Some(fmt.position),
- Substitution::Escape((start, end)) => Some(InnerSpan::new(start, end)),
+ match self {
+ Substitution::Format(fmt) => Some(fmt.position),
+ &Substitution::Escape((start, end)) => Some(InnerSpan::new(start, end)),
}
}
pub fn set_position(&mut self, start: usize, end: usize) {
match self {
- Substitution::Format(ref mut fmt) => fmt.position = InnerSpan::new(start, end),
- Substitution::Escape(ref mut pos) => *pos = (start, end),
+ Substitution::Format(fmt) => fmt.position = InnerSpan::new(start, end),
+ Substitution::Escape(pos) => *pos = (start, end),
}
}
/// This ignores cases where the substitution does not have an exact equivalent, or where
/// the substitution would be unnecessary.
pub fn translate(&self) -> Result<String, Option<String>> {
- match *self {
- Substitution::Format(ref fmt) => fmt.translate(),
+ match self {
+ Substitution::Format(fmt) => fmt.translate(),
Substitution::Escape(_) => Err(None),
}
}
}
pub fn position(&self) -> Option<InnerSpan> {
- match self {
- Substitution::Ordinal(_, pos)
- | Substitution::Name(_, pos)
- | Substitution::Escape(pos) => Some(InnerSpan::new(pos.0, pos.1)),
- }
+ let (Self::Ordinal(_, pos) | Self::Name(_, pos) | Self::Escape(pos)) = self;
+ Some(InnerSpan::new(pos.0, pos.1))
}
pub fn set_position(&mut self, start: usize, end: usize) {
- match self {
- Substitution::Ordinal(_, ref mut pos)
- | Substitution::Name(_, ref mut pos)
- | Substitution::Escape(ref mut pos) => *pos = (start, end),
- }
+ let (Self::Ordinal(_, pos) | Self::Name(_, pos) | Self::Escape(pos)) = self;
+ *pos = (start, end);
}
pub fn translate(&self) -> Result<String, Option<String>> {
- match *self {
+ match self {
Substitution::Ordinal(n, _) => Ok(format!("{{{}}}", n)),
Substitution::Name(n, _) => Ok(format!("{{{}}}", n)),
Substitution::Escape(_) => Err(None),
check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator);
let orig_item = item.clone();
- let not_static = || {
- ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
- vec![orig_item.clone()]
- };
// Allow using `#[global_allocator]` on an item statement
// FIXME - if we get deref patterns, use them to reduce duplication here
- let (item, is_stmt, ty_span) = match &item {
- Annotatable::Item(item) => match item.kind {
- ItemKind::Static(ref ty, ..) => (item, false, ecx.with_def_site_ctxt(ty.span)),
- _ => return not_static(),
- },
- Annotatable::Stmt(stmt) => match &stmt.kind {
- StmtKind::Item(item_) => match item_.kind {
- ItemKind::Static(ref ty, ..) => (item_, true, ecx.with_def_site_ctxt(ty.span)),
- _ => return not_static(),
- },
- _ => return not_static(),
- },
- _ => return not_static(),
- };
+ let (item, is_stmt, ty_span) =
+ if let Annotatable::Item(item) = &item
+ && let ItemKind::Static(ty, ..) = &item.kind
+ {
+ (item, false, ecx.with_def_site_ctxt(ty.span))
+ } else if let Annotatable::Stmt(stmt) = &item
+ && let StmtKind::Item(item) = &stmt.kind
+ && let ItemKind::Static(ty, ..) = &item.kind
+ {
+ (item, true, ecx.with_def_site_ctxt(ty.span))
+ } else {
+ ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
+ return vec![orig_item.clone()]
+ };
// Generate a bunch of new items using the AllocFnFactory
let span = ecx.with_def_site_ctxt(item.span);
mod source_util;
mod test;
mod trace_macros;
+mod type_ascribe;
mod util;
pub mod asm;
unreachable: edition_panic::expand_unreachable,
stringify: source_util::expand_stringify,
trace_macros: trace_macros::expand_trace_macros,
+ type_ascribe: type_ascribe::expand_type_ascribe,
}
register_attr! {
fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
let has_should_panic_attr = cx.sess.contains_name(&i.attrs, sym::should_panic);
let sd = &cx.sess.parse_sess.span_diagnostic;
- if let ast::ItemKind::Fn(box ast::Fn { ref sig, ref generics, .. }) = i.kind {
- if let ast::Unsafe::Yes(span) = sig.header.unsafety {
- sd.struct_span_err(i.span, "unsafe functions cannot be used for tests")
- .span_label(span, "`unsafe` because of this")
- .emit();
- return false;
- }
- if let ast::Async::Yes { span, .. } = sig.header.asyncness {
- sd.struct_span_err(i.span, "async functions cannot be used for tests")
- .span_label(span, "`async` because of this")
- .emit();
- return false;
- }
-
- // If the termination trait is active, the compiler will check that the output
- // type implements the `Termination` trait as `libtest` enforces that.
- let has_output = match sig.decl.output {
- ast::FnRetTy::Default(..) => false,
- ast::FnRetTy::Ty(ref t) if t.kind.is_unit() => false,
- _ => true,
- };
-
- if !sig.decl.inputs.is_empty() {
- sd.span_err(i.span, "functions used as tests can not have any arguments");
- return false;
- }
+ match &i.kind {
+ ast::ItemKind::Fn(box ast::Fn { sig, generics, .. }) => {
+ if let ast::Unsafe::Yes(span) = sig.header.unsafety {
+ sd.struct_span_err(i.span, "unsafe functions cannot be used for tests")
+ .span_label(span, "`unsafe` because of this")
+ .emit();
+ return false;
+ }
+ if let ast::Async::Yes { span, .. } = sig.header.asyncness {
+ sd.struct_span_err(i.span, "async functions cannot be used for tests")
+ .span_label(span, "`async` because of this")
+ .emit();
+ return false;
+ }
- match (has_output, has_should_panic_attr) {
- (true, true) => {
- sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
- false
+ // If the termination trait is active, the compiler will check that the output
+ // type implements the `Termination` trait as `libtest` enforces that.
+ let has_output = match &sig.decl.output {
+ ast::FnRetTy::Default(..) => false,
+ ast::FnRetTy::Ty(t) if t.kind.is_unit() => false,
+ _ => true,
+ };
+
+ if !sig.decl.inputs.is_empty() {
+ sd.span_err(i.span, "functions used as tests can not have any arguments");
+ return false;
}
- (true, false) => {
- if !generics.params.is_empty() {
- sd.span_err(i.span, "functions used as tests must have signature fn() -> ()");
+
+ match (has_output, has_should_panic_attr) {
+ (true, true) => {
+ sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
false
- } else {
- true
}
+ (true, false) => {
+ if !generics.params.is_empty() {
+ sd.span_err(
+ i.span,
+ "functions used as tests must have signature fn() -> ()",
+ );
+ false
+ } else {
+ true
+ }
+ }
+ (false, _) => true,
}
- (false, _) => true,
}
- } else {
- // should be unreachable because `is_test_fn_item` should catch all non-fn items
- false
+ _ => {
+ // should be unreachable because `is_test_fn_item` should catch all non-fn items
+ debug_assert!(false);
+ false
+ }
}
}
fn has_bench_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
- let has_sig = if let ast::ItemKind::Fn(box ast::Fn { ref sig, .. }) = i.kind {
+ let has_sig = match &i.kind {
// N.B., inadequate check, but we're running
// well before resolve, can't get too deep.
- sig.decl.inputs.len() == 1
- } else {
- false
+ ast::ItemKind::Fn(box ast::Fn { sig, .. }) => sig.decl.inputs.len() == 1,
+ _ => false,
};
if !has_sig {
// We don't want to recurse into anything other than mods, since
// mods or tests inside of functions will break things
- if let ast::ItemKind::Mod(_, ModKind::Loaded(.., ref spans)) = item.kind {
- let ast::ModSpans { inner_span: span, inject_use_span: _ } = *spans;
+ if let ast::ItemKind::Mod(_, ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. })) =
+ item.kind
+ {
let prev_tests = mem::take(&mut self.tests);
noop_visit_item_kind(&mut item.kind, self);
self.add_test_cases(item.id, span, prev_tests);
--- /dev/null
+use rustc_ast::ptr::P;
+use rustc_ast::tokenstream::TokenStream;
+use rustc_ast::{token, Expr, ExprKind, Ty};
+use rustc_errors::PResult;
+use rustc_expand::base::{self, DummyResult, ExtCtxt, MacEager};
+use rustc_span::Span;
+
+pub fn expand_type_ascribe(
+ cx: &mut ExtCtxt<'_>,
+ span: Span,
+ tts: TokenStream,
+) -> Box<dyn base::MacResult + 'static> {
+ let (expr, ty) = match parse_ascribe(cx, tts) {
+ Ok(parsed) => parsed,
+ Err(mut err) => {
+ err.emit();
+ return DummyResult::any(span);
+ }
+ };
+
+ let asc_expr = cx.expr(span, ExprKind::Type(expr, ty));
+
+ return MacEager::expr(asc_expr);
+}
+
+fn parse_ascribe<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P<Expr>, P<Ty>)> {
+ let mut parser = cx.new_parser_from_tts(stream);
+
+ let expr = parser.parse_expr()?;
+ parser.expect(&token::Comma)?;
+
+ let ty = parser.parse_ty()?;
+
+ Ok((expr, ty))
+}
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c794e162a5eff65c72ef524dfe393eb923c354e350bb78b9c7383df13f3bc142"
-[[package]]
-name = "ar"
-version = "0.8.0"
-source = "git+https://github.com/bjorn3/rust-ar.git?branch=do_not_remove_cg_clif_ranlib#de9ab0e56bf3a208381d342aa5b60f9ff2891648"
-
[[package]]
name = "arrayvec"
version = "0.7.2"
name = "rustc_codegen_cranelift"
version = "0.1.0"
dependencies = [
- "ar",
"cranelift-codegen",
"cranelift-frontend",
"cranelift-jit",
gimli = { version = "0.26.0", default-features = false, features = ["write"]}
object = { version = "0.29.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
-ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" }
indexmap = "1.9.1"
libloading = { version = "0.7.3", optional = true }
once_cell = "1.10.0"
-//! Creation of ar archives like for the lib and staticlib crate type
-
-use std::collections::BTreeMap;
-use std::fs::File;
-use std::io::{self, Read, Seek};
use std::path::{Path, PathBuf};
-use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
+use rustc_codegen_ssa::back::archive::{
+ get_native_object_symbols, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder,
+};
use rustc_session::Session;
-use object::read::archive::ArchiveFile;
-use object::{Object, ObjectSymbol, ReadCache};
-
-#[derive(Debug)]
-enum ArchiveEntry {
- FromArchive { archive_index: usize, file_range: (u64, u64) },
- File(PathBuf),
-}
-
pub(crate) struct ArArchiveBuilderBuilder;
impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a> {
- Box::new(ArArchiveBuilder {
- sess,
- use_gnu_style_archive: sess.target.archive_format == "gnu",
- // FIXME fix builtin ranlib on macOS
- no_builtin_ranlib: sess.target.is_like_osx,
-
- src_archives: vec![],
- entries: vec![],
- })
+ Box::new(ArArchiveBuilder::new(sess, get_native_object_symbols))
}
fn create_dll_import_lib(
_tmpdir: &Path,
_is_direct_dependency: bool,
) -> PathBuf {
- bug!("creating dll imports is not supported");
- }
-}
-
-pub(crate) struct ArArchiveBuilder<'a> {
- sess: &'a Session,
- use_gnu_style_archive: bool,
- no_builtin_ranlib: bool,
-
- src_archives: Vec<File>,
- // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
- // the end of an archive for linkers to not get confused.
- entries: Vec<(Vec<u8>, ArchiveEntry)>,
-}
-
-impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
- fn add_file(&mut self, file: &Path) {
- self.entries.push((
- file.file_name().unwrap().to_str().unwrap().to_string().into_bytes(),
- ArchiveEntry::File(file.to_owned()),
- ));
- }
-
- fn add_archive(
- &mut self,
- archive_path: &Path,
- mut skip: Box<dyn FnMut(&str) -> bool + 'static>,
- ) -> std::io::Result<()> {
- let read_cache = ReadCache::new(std::fs::File::open(&archive_path)?);
- let archive = ArchiveFile::parse(&read_cache).unwrap();
- let archive_index = self.src_archives.len();
-
- for entry in archive.members() {
- let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
- let file_name = String::from_utf8(entry.name().to_vec())
- .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
- if !skip(&file_name) {
- self.entries.push((
- file_name.into_bytes(),
- ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() },
- ));
- }
- }
-
- self.src_archives.push(read_cache.into_inner());
- Ok(())
- }
-
- fn build(mut self: Box<Self>, output: &Path) -> bool {
- enum BuilderKind {
- Bsd(ar::Builder<File>),
- Gnu(ar::GnuBuilder<File>),
- }
-
- let sess = self.sess;
-
- let mut symbol_table = BTreeMap::new();
-
- let mut entries = Vec::new();
-
- for (mut entry_name, entry) in self.entries {
- // FIXME only read the symbol table of the object files to avoid having to keep all
- // object files in memory at once, or read them twice.
- let data = match entry {
- ArchiveEntry::FromArchive { archive_index, file_range } => {
- // FIXME read symbols from symtab
- let src_read_cache = &mut self.src_archives[archive_index];
-
- src_read_cache.seek(io::SeekFrom::Start(file_range.0)).unwrap();
- let mut data = std::vec::from_elem(0, usize::try_from(file_range.1).unwrap());
- src_read_cache.read_exact(&mut data).unwrap();
-
- data
- }
- ArchiveEntry::File(file) => std::fs::read(file).unwrap_or_else(|err| {
- sess.fatal(&format!(
- "error while reading object file during archive building: {}",
- err
- ));
- }),
- };
-
- if !self.no_builtin_ranlib {
- if symbol_table.contains_key(&entry_name) {
- // The ar crate can't handle creating a symbol table in case of multiple archive
- // members with the same name. Work around this by prepending a number until we
- // get a unique name.
- for i in 1.. {
- let new_name = format!("{}_", i)
- .into_bytes()
- .into_iter()
- .chain(entry_name.iter().copied())
- .collect::<Vec<_>>();
- if !symbol_table.contains_key(&new_name) {
- entry_name = new_name;
- break;
- }
- }
- }
-
- match object::File::parse(&*data) {
- Ok(object) => {
- symbol_table.insert(
- entry_name.to_vec(),
- object
- .symbols()
- .filter_map(|symbol| {
- if symbol.is_undefined() || symbol.is_local() {
- None
- } else {
- symbol.name().map(|name| name.as_bytes().to_vec()).ok()
- }
- })
- .collect::<Vec<_>>(),
- );
- }
- Err(err) => {
- let err = err.to_string();
- if err == "Unknown file magic" {
- // Not an object file; skip it.
- } else if object::read::archive::ArchiveFile::parse(&*data).is_ok() {
- // Nested archive file; skip it.
- } else {
- sess.fatal(&format!(
- "error parsing `{}` during archive creation: {}",
- String::from_utf8_lossy(&entry_name),
- err
- ));
- }
- }
- }
- }
-
- entries.push((entry_name, data));
- }
-
- let mut builder = if self.use_gnu_style_archive {
- BuilderKind::Gnu(
- ar::GnuBuilder::new(
- File::create(output).unwrap_or_else(|err| {
- sess.fatal(&format!(
- "error opening destination during archive building: {}",
- err
- ));
- }),
- entries.iter().map(|(name, _)| name.clone()).collect(),
- ar::GnuSymbolTableFormat::Size32,
- symbol_table,
- )
- .unwrap(),
- )
- } else {
- BuilderKind::Bsd(
- ar::Builder::new(
- File::create(output).unwrap_or_else(|err| {
- sess.fatal(&format!(
- "error opening destination during archive building: {}",
- err
- ));
- }),
- symbol_table,
- )
- .unwrap(),
- )
- };
-
- let any_members = !entries.is_empty();
-
- // Add all files
- for (entry_name, data) in entries.into_iter() {
- let header = ar::Header::new(entry_name, data.len() as u64);
- match builder {
- BuilderKind::Bsd(ref mut builder) => builder.append(&header, &mut &*data).unwrap(),
- BuilderKind::Gnu(ref mut builder) => builder.append(&header, &mut &*data).unwrap(),
- }
- }
-
- // Finalize archive
- std::mem::drop(builder);
-
- if self.no_builtin_ranlib {
- let ranlib = crate::toolchain::get_toolchain_binary(self.sess, "ranlib");
-
- // Run ranlib to be able to link the archive
- let status = std::process::Command::new(ranlib)
- .arg(output)
- .status()
- .expect("Couldn't run ranlib");
-
- if !status.success() {
- self.sess.fatal(&format!("Ranlib exited with code {:?}", status.code()));
- }
- }
-
- any_members
+ unimplemented!("creating dll imports is not yet supported");
}
}
}
}
- TerminatorKind::SwitchInt { discr, switch_ty, targets } => {
- let discr = codegen_operand(fx, discr).load_scalar(fx);
+ TerminatorKind::SwitchInt { discr, targets } => {
+ let discr = codegen_operand(fx, discr);
+ let switch_ty = discr.layout().ty;
+ let discr = discr.load_scalar(fx);
let use_bool_opt = switch_ty.kind() == fx.tcx.types.bool.kind()
|| (targets.iter().count() == 1 && targets.iter().next().unwrap().0 == 0);
"memchr",
]
-[[package]]
-name = "ar"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "450575f58f7bee32816abbff470cbc47797397c2a81e0eaced4b98436daf52e1"
-
[[package]]
name = "bitflags"
version = "1.3.2"
name = "rustc_codegen_gcc"
version = "0.1.0"
dependencies = [
- "ar",
"gccjit",
"lang_tester",
- "target-lexicon",
"tempfile",
]
"winapi-util",
]
-[[package]]
-name = "target-lexicon"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d"
-
[[package]]
name = "tempfile"
version = "3.2.0"
# Local copy.
#gccjit = { path = "../gccjit.rs" }
-target-lexicon = "0.10.0"
-
-ar = "0.8.0"
-
[dev-dependencies]
lang_tester = "0.3.9"
tempfile = "3.1.0"
-use std::fs::File;
use std::path::{Path, PathBuf};
-use crate::errors::RanlibFailure;
-
-use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
+use rustc_codegen_ssa::back::archive::{
+ get_native_object_symbols, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder,
+};
use rustc_session::Session;
use rustc_session::cstore::DllImport;
-struct ArchiveConfig<'a> {
- sess: &'a Session,
- use_native_ar: bool,
- use_gnu_style_archive: bool,
-}
-
-#[derive(Debug)]
-enum ArchiveEntry {
- FromArchive {
- archive_index: usize,
- entry_index: usize,
- },
- File(PathBuf),
-}
-
-pub struct ArArchiveBuilderBuilder;
+pub(crate) struct ArArchiveBuilderBuilder;
impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a> {
- let config = ArchiveConfig {
- sess,
- use_native_ar: false,
- // FIXME test for linux and System V derivatives instead
- use_gnu_style_archive: sess.target.options.archive_format == "gnu",
- };
-
- Box::new(ArArchiveBuilder {
- config,
- src_archives: vec![],
- entries: vec![],
- })
+ Box::new(ArArchiveBuilder::new(sess, get_native_object_symbols))
}
fn create_dll_import_lib(
_tmpdir: &Path,
_is_direct_dependency: bool,
) -> PathBuf {
- unimplemented!();
- }
-}
-
-pub struct ArArchiveBuilder<'a> {
- config: ArchiveConfig<'a>,
- src_archives: Vec<(PathBuf, ar::Archive<File>)>,
- // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
- // the end of an archive for linkers to not get confused.
- entries: Vec<(String, ArchiveEntry)>,
-}
-
-impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
- fn add_file(&mut self, file: &Path) {
- self.entries.push((
- file.file_name().unwrap().to_str().unwrap().to_string(),
- ArchiveEntry::File(file.to_owned()),
- ));
- }
-
- fn add_archive(
- &mut self,
- archive_path: &Path,
- mut skip: Box<dyn FnMut(&str) -> bool + 'static>,
- ) -> std::io::Result<()> {
- let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
- let archive_index = self.src_archives.len();
-
- let mut i = 0;
- while let Some(entry) = archive.next_entry() {
- let entry = entry?;
- let file_name = String::from_utf8(entry.header().identifier().to_vec())
- .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
- if !skip(&file_name) {
- self.entries
- .push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i }));
- }
- i += 1;
- }
-
- self.src_archives.push((archive_path.to_owned(), archive));
- Ok(())
- }
-
- fn build(mut self: Box<Self>, output: &Path) -> bool {
- use std::process::Command;
-
- fn add_file_using_ar(archive: &Path, file: &Path) {
- Command::new("ar")
- .arg("r") // add or replace file
- .arg("-c") // silence created file message
- .arg(archive)
- .arg(&file)
- .status()
- .unwrap();
- }
-
- enum BuilderKind<'a> {
- Bsd(ar::Builder<File>),
- Gnu(ar::GnuBuilder<File>),
- NativeAr(&'a Path),
- }
-
- let mut builder = if self.config.use_native_ar {
- BuilderKind::NativeAr(output)
- } else if self.config.use_gnu_style_archive {
- BuilderKind::Gnu(ar::GnuBuilder::new(
- File::create(output).unwrap(),
- self.entries
- .iter()
- .map(|(name, _)| name.as_bytes().to_vec())
- .collect(),
- ))
- } else {
- BuilderKind::Bsd(ar::Builder::new(File::create(output).unwrap()))
- };
-
- let any_members = !self.entries.is_empty();
-
- // Add all files
- for (entry_name, entry) in self.entries.into_iter() {
- match entry {
- ArchiveEntry::FromArchive {
- archive_index,
- entry_index,
- } => {
- let (ref src_archive_path, ref mut src_archive) =
- self.src_archives[archive_index];
- let entry = src_archive.jump_to_entry(entry_index).unwrap();
- let header = entry.header().clone();
-
- match builder {
- BuilderKind::Bsd(ref mut builder) => {
- builder.append(&header, entry).unwrap()
- }
- BuilderKind::Gnu(ref mut builder) => {
- builder.append(&header, entry).unwrap()
- }
- BuilderKind::NativeAr(archive_file) => {
- Command::new("ar")
- .arg("x")
- .arg(src_archive_path)
- .arg(&entry_name)
- .status()
- .unwrap();
- add_file_using_ar(archive_file, Path::new(&entry_name));
- std::fs::remove_file(entry_name).unwrap();
- }
- }
- }
- ArchiveEntry::File(file) =>
- match builder {
- BuilderKind::Bsd(ref mut builder) => {
- builder
- .append_file(entry_name.as_bytes(), &mut File::open(file).expect("file for bsd builder"))
- .unwrap()
- },
- BuilderKind::Gnu(ref mut builder) => {
- builder
- .append_file(entry_name.as_bytes(), &mut File::open(&file).expect(&format!("file {:?} for gnu builder", file)))
- .unwrap()
- },
- BuilderKind::NativeAr(archive_file) => add_file_using_ar(archive_file, &file),
- },
- }
- }
-
- // Finalize archive
- std::mem::drop(builder);
-
- // Run ranlib to be able to link the archive
- let status =
- std::process::Command::new("ranlib").arg(output).status().expect("Couldn't run ranlib");
-
- if !status.success() {
- self.config.sess.emit_fatal(RanlibFailure::new(status.code()));
- }
-
- any_members
+ unimplemented!("creating dll imports is not yet supported");
}
}
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::mir::interpret::{self, ConstAllocation, ErrorHandled, Scalar as InterpScalar, read_target_uint};
-use rustc_span::Span;
use rustc_span::def_id::DefId;
use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange};
use crate::base;
use crate::context::CodegenCx;
-use crate::errors::LinkageConstOrMutType;
use crate::type_of::LayoutGccExt;
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
}
Node::ForeignItem(&hir::ForeignItem {
- span,
+ span: _,
kind: hir::ForeignItemKind::Static(..),
..
}) => {
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
- check_and_apply_linkage(&self, &fn_attrs, ty, sym, span)
+ check_and_apply_linkage(&self, &fn_attrs, ty, sym)
}
item => bug!("get_static: expected static, found {:?}", item),
//debug!("get_static: sym={} item_attr={:?}", sym, self.tcx.item_attrs(def_id));
let attrs = self.tcx.codegen_fn_attrs(def_id);
- let span = self.tcx.def_span(def_id);
- let global = check_and_apply_linkage(&self, &attrs, ty, sym, span);
+ let global = check_and_apply_linkage(&self, &attrs, ty, sym);
let needs_dll_storage_attr = false; // TODO(antoyo)
Ok((const_alloc_to_gcc(cx, alloc), alloc))
}
-fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &CodegenFnAttrs, ty: Ty<'tcx>, sym: &str, span: Span) -> LValue<'gcc> {
+fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &CodegenFnAttrs, ty: Ty<'tcx>, sym: &str) -> LValue<'gcc> {
let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
let llty = cx.layout_of(ty).gcc_type(cx, true);
- if let Some(linkage) = attrs.linkage {
- // If this is a static with a linkage specified, then we need to handle
- // it a little specially. The typesystem prevents things like &T and
- // extern "C" fn() from being non-null, so we can't just declare a
- // static and call it a day. Some linkages (like weak) will make it such
- // that the static actually has a null value.
- let llty2 =
- if let ty::RawPtr(ref mt) = ty.kind() {
- cx.layout_of(mt.ty).gcc_type(cx, true)
- }
- else {
- cx.sess().emit_fatal(LinkageConstOrMutType { span: span })
- };
+ if let Some(linkage) = attrs.import_linkage {
// Declare a symbol `foo` with the desired linkage.
- let global1 = cx.declare_global_with_linkage(&sym, llty2, base::global_linkage_to_gcc(linkage));
+ let global1 = cx.declare_global_with_linkage(&sym, cx.type_i8(), base::global_linkage_to_gcc(linkage));
// Declare an internal global `extern_with_linkage_foo` which
// is initialized with the address of `foo`. If `foo` is
}
}
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_ranlib_failure)]
-pub(crate) struct RanlibFailure {
- exit_code: ExitCode,
-}
-
-impl RanlibFailure {
- pub fn new(exit_code: Option<i32>) -> Self {
- RanlibFailure { exit_code: ExitCode(exit_code) }
- }
-}
-
#[derive(Diagnostic)]
#[diag(codegen_gcc_invalid_monomorphization_basic_integer, code = "E0511")]
pub(crate) struct InvalidMonomorphizationBasicInteger<'a> {
pub in_elem: Ty<'a>,
}
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_linkage_const_or_mut_type)]
-pub(crate) struct LinkageConstOrMutType {
- #[primary_span]
- pub span: Span
-}
-
#[derive(Diagnostic)]
#[diag(codegen_gcc_lto_not_supported)]
pub(crate) struct LTONotSupported;
#[diag(codegen_gcc_unwinding_inline_asm)]
pub(crate) struct UnwindingInlineAsm {
#[primary_span]
- pub span: Span
+ pub span: Span,
}
// Unsupported.
self.context.new_rvalue_from_int(self.int_type, 0)
}
+
+ fn set_kcfi_type_metadata(&self, _function: RValue<'gcc>, _kcfi_typeid: u32) {
+ // Unsupported.
+ }
}
cstr = "0.2"
libc = "0.2"
measureme = "10.0.0"
-object = { version = "0.29.0", default-features = false, features = ["std", "read_core", "archive", "coff", "elf", "macho", "pe"] }
+object = { version = "0.29.0", default-features = false, features = ["std", "read"] }
tracing = "0.1"
rustc_middle = { path = "../rustc_middle" }
rustc-demangle = "0.1.21"
callee,
args.as_ptr(),
args.len() as c_uint,
- None,
+ [].as_ptr(),
+ 0 as c_uint,
);
llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
.enumerate()
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
.collect::<Vec<_>>();
- let ret =
- llvm::LLVMRustBuildCall(llbuilder, ty, callee, args.as_ptr(), args.len() as c_uint, None);
+ let ret = llvm::LLVMRustBuildCall(
+ llbuilder,
+ ty,
+ callee,
+ args.as_ptr(),
+ args.len() as c_uint,
+ [].as_ptr(),
+ 0 as c_uint,
+ );
llvm::LLVMSetTailCall(ret, True);
llvm::LLVMBuildRetVoid(llbuilder);
llvm::LLVMDisposeBuilder(llbuilder);
OptimizeAttr::Speed => {}
}
- let inline = if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
- InlineAttr::Never
- } else if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) {
- InlineAttr::Hint
- } else {
- codegen_fn_attrs.inline
- };
+ let inline =
+ if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) {
+ InlineAttr::Hint
+ } else {
+ codegen_fn_attrs.inline
+ };
to_add.extend(inline_attr(cx, inline));
// The `uwtable` attribute according to LLVM is:
//! A helper class for dealing with static archives
use std::env;
-use std::ffi::{CStr, CString, OsString};
-use std::fs;
-use std::io::{self, Write};
+use std::ffi::{c_char, c_void, CStr, CString, OsString};
+use std::io;
use std::mem;
use std::path::{Path, PathBuf};
use std::ptr;
use std::str;
-use object::read::macho::FatArch;
-
use crate::common;
use crate::errors::{
- ArchiveBuildFailure, DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary,
- ErrorWritingDEFFile, UnknownArchiveKind,
+ DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, ErrorWritingDEFFile,
};
use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
-use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
-use rustc_data_structures::memmap::Mmap;
+use rustc_codegen_ssa::back::archive::{
+ get_native_object_symbols, try_extract_macho_fat_archive, ArArchiveBuilder,
+ ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, UnknownArchiveKind,
+};
+
use rustc_session::cstore::DllImport;
use rustc_session::Session;
/// Helper for adding many files to an archive.
#[must_use = "must call build() to finish building the archive"]
-pub struct LlvmArchiveBuilder<'a> {
+pub(crate) struct LlvmArchiveBuilder<'a> {
sess: &'a Session,
additions: Vec<Addition>,
}
}
}
-fn try_filter_fat_archs(
- archs: object::read::Result<&[impl FatArch]>,
- target_arch: object::Architecture,
- archive_path: &Path,
- archive_map_data: &[u8],
-) -> io::Result<Option<PathBuf>> {
- let archs = archs.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
-
- let desired = match archs.iter().filter(|a| a.architecture() == target_arch).next() {
- Some(a) => a,
- None => return Ok(None),
- };
-
- let (mut new_f, extracted_path) = tempfile::Builder::new()
- .suffix(archive_path.file_name().unwrap())
- .tempfile()?
- .keep()
- .unwrap();
-
- new_f.write_all(
- desired.data(archive_map_data).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?,
- )?;
-
- Ok(Some(extracted_path))
-}
-
-fn try_extract_macho_fat_archive(
- sess: &Session,
- archive_path: &Path,
-) -> io::Result<Option<PathBuf>> {
- let archive_map = unsafe { Mmap::map(fs::File::open(&archive_path)?)? };
- let target_arch = match sess.target.arch.as_ref() {
- "aarch64" => object::Architecture::Aarch64,
- "x86_64" => object::Architecture::X86_64,
- _ => return Ok(None),
- };
-
- match object::macho::FatHeader::parse(&*archive_map) {
- Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC => {
- let archs = object::macho::FatHeader::parse_arch32(&*archive_map);
- try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
- }
- Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC_64 => {
- let archs = object::macho::FatHeader::parse_arch64(&*archive_map);
- try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
- }
- // Not a FatHeader at all, just return None.
- _ => Ok(None),
- }
-}
-
impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
fn add_archive(
&mut self,
impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a> {
- Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() })
+ if sess.target.arch == "wasm32" || sess.target.arch == "wasm64" {
+ Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() })
+ } else {
+ Box::new(ArArchiveBuilder::new(sess, get_llvm_object_symbols))
+ }
}
fn create_dll_import_lib(
}
}
+// The object crate doesn't know how to get symbols for LLVM bitcode and COFF bigobj files.
+// As such we need to use LLVM for them.
+#[deny(unsafe_op_in_unsafe_fn)]
+fn get_llvm_object_symbols(
+ buf: &[u8],
+ f: &mut dyn FnMut(&[u8]) -> io::Result<()>,
+) -> io::Result<bool> {
+ let is_bitcode = unsafe { llvm::LLVMRustIsBitcode(buf.as_ptr(), buf.len()) };
+
+ // COFF bigobj file, msvc LTO file or import library. See
+ // https://github.com/llvm/llvm-project/blob/453f27bc9/llvm/lib/BinaryFormat/Magic.cpp#L38-L51
+ let is_unsupported_windows_obj_file = buf.get(0..4) == Some(b"\0\0\xFF\xFF");
+
+ if is_bitcode || is_unsupported_windows_obj_file {
+ let mut state = Box::new(f);
+
+ let err = unsafe {
+ llvm::LLVMRustGetSymbols(
+ buf.as_ptr(),
+ buf.len(),
+ &mut *state as *mut &mut _ as *mut c_void,
+ callback,
+ error_callback,
+ )
+ };
+
+ if err.is_null() {
+ return Ok(true);
+ } else {
+ return Err(unsafe { *Box::from_raw(err as *mut io::Error) });
+ }
+
+ unsafe extern "C" fn callback(
+ state: *mut c_void,
+ symbol_name: *const c_char,
+ ) -> *mut c_void {
+ let f = unsafe { &mut *(state as *mut &mut dyn FnMut(&[u8]) -> io::Result<()>) };
+ match f(unsafe { CStr::from_ptr(symbol_name) }.to_bytes()) {
+ Ok(()) => std::ptr::null_mut(),
+ Err(err) => Box::into_raw(Box::new(err)) as *mut c_void,
+ }
+ }
+
+ unsafe extern "C" fn error_callback(error: *const c_char) -> *mut c_void {
+ let error = unsafe { CStr::from_ptr(error) };
+ Box::into_raw(Box::new(io::Error::new(
+ io::ErrorKind::Other,
+ format!("LLVM error: {}", error.to_string_lossy()),
+ ))) as *mut c_void
+ }
+ } else {
+ get_native_object_symbols(buf, f)
+ }
+}
+
impl<'a> LlvmArchiveBuilder<'a> {
fn build_with_llvm(&mut self, output: &Path) -> io::Result<bool> {
let kind = &*self.sess.target.archive_format;
}
}
+ // __llvm_profile_counter_bias is pulled in at link time by an undefined reference to
+ // __llvm_profile_runtime, therefore we won't know until link time if this symbol
+ // should have default visibility.
+ symbols_below_threshold.push(CString::new("__llvm_profile_counter_bias").unwrap());
Ok((symbols_below_threshold, upstream_modules))
}
};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span;
+use rustc_symbol_mangling::typeid::kcfi_typeid_for_fnabi;
use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
use rustc_target::spec::{HasTargetSpec, Target};
use std::borrow::Cow;
debug!("invoke {:?} with args ({:?})", llfn, args);
let args = self.check_call("invoke", llty, llfn, args);
- let bundle = funclet.map(|funclet| funclet.bundle());
- let bundle = bundle.as_ref().map(|b| &*b.raw);
+ let funclet_bundle = funclet.map(|funclet| funclet.bundle());
+ let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
+ let mut bundles = vec![funclet_bundle];
+
+ // Set KCFI operand bundle
+ let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
+ let kcfi_bundle =
+ if self.tcx.sess.is_sanitizer_kcfi_enabled() && fn_abi.is_some() && is_indirect_call {
+ let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap());
+ Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
+ } else {
+ None
+ };
+ if kcfi_bundle.is_some() {
+ let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
+ bundles.push(kcfi_bundle);
+ }
+ bundles.retain(|bundle| bundle.is_some());
let invoke = unsafe {
llvm::LLVMRustBuildInvoke(
self.llbuilder,
args.len() as c_uint,
then,
catch,
- bundle,
+ bundles.as_ptr(),
+ bundles.len() as c_uint,
UNNAMED,
)
};
llfn,
args.as_ptr() as *const &llvm::Value,
args.len() as c_uint,
- None,
+ [].as_ptr(),
+ 0 as c_uint,
);
}
}
debug!("call {:?} with args ({:?})", llfn, args);
let args = self.check_call("call", llty, llfn, args);
- let bundle = funclet.map(|funclet| funclet.bundle());
- let bundle = bundle.as_ref().map(|b| &*b.raw);
+ let funclet_bundle = funclet.map(|funclet| funclet.bundle());
+ let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
+ let mut bundles = vec![funclet_bundle];
+
+ // Set KCFI operand bundle
+ let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
+ let kcfi_bundle =
+ if self.tcx.sess.is_sanitizer_kcfi_enabled() && fn_abi.is_some() && is_indirect_call {
+ let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap());
+ Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
+ } else {
+ None
+ };
+ if kcfi_bundle.is_some() {
+ let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
+ bundles.push(kcfi_bundle);
+ }
+ bundles.retain(|bundle| bundle.is_some());
let call = unsafe {
llvm::LLVMRustBuildCall(
self.llbuilder,
llfn,
args.as_ptr() as *const &llvm::Value,
args.len() as c_uint,
- bundle,
+ bundles.as_ptr(),
+ bundles.len() as c_uint,
)
};
if let Some(fn_abi) = fn_abi {
use crate::base;
use crate::common::{self, CodegenCx};
use crate::debuginfo;
-use crate::errors::{InvalidMinimumAlignment, LinkageConstOrMutType, SymbolAlreadyDefined};
+use crate::errors::{InvalidMinimumAlignment, SymbolAlreadyDefined};
use crate::llvm::{self, True};
use crate::llvm_util;
use crate::type_::Type;
def_id: DefId,
) -> &'ll Value {
let llty = cx.layout_of(ty).llvm_type(cx);
- if let Some(linkage) = attrs.linkage {
+ if let Some(linkage) = attrs.import_linkage {
debug!("get_static: sym={} linkage={:?}", sym, linkage);
- // If this is a static with a linkage specified, then we need to handle
- // it a little specially. The typesystem prevents things like &T and
- // extern "C" fn() from being non-null, so we can't just declare a
- // static and call it a day. Some linkages (like weak) will make it such
- // that the static actually has a null value.
- let llty2 = if let ty::RawPtr(ref mt) = ty.kind() {
- cx.layout_of(mt.ty).llvm_type(cx)
- } else {
- cx.sess().emit_fatal(LinkageConstOrMutType { span: cx.tcx.def_span(def_id) })
- };
unsafe {
// Declare a symbol `foo` with the desired linkage.
- let g1 = cx.declare_global(sym, llty2);
+ let g1 = cx.declare_global(sym, cx.type_i8());
llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage));
// Declare an internal global `extern_with_linkage_foo` which
})
});
llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage);
- llvm::LLVMSetInitializer(g2, g1);
+ llvm::LLVMSetInitializer(g2, cx.const_ptrcast(g1, llty));
g2
}
} else if cx.tcx.sess.target.arch == "x86" &&
);
}
+ if sess.is_sanitizer_kcfi_enabled() {
+ let kcfi = "kcfi\0".as_ptr().cast();
+ llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1);
+ }
+
// Control Flow Guard is currently only supported by the MSVC linker on Windows.
if sess.target.is_like_msvc {
match sess.opts.cg.control_flow_guard {
use rustc_fs_util::path_to_c_string;
use rustc_hir::def::CtorKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::bug;
-use rustc_middle::mir::{self, GeneratorLayout};
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{
// Tuples
//=-----------------------------------------------------------------------------
-/// Returns names of captured upvars for closures and generators.
-///
-/// Here are some examples:
-/// - `name__field1__field2` when the upvar is captured by value.
-/// - `_ref__name__field` when the upvar is captured by reference.
-///
-/// For generators this only contains upvars that are shared by all states.
-fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> SmallVec<String> {
- let body = tcx.optimized_mir(def_id);
-
- body.var_debug_info
- .iter()
- .filter_map(|var| {
- let is_ref = match var.value {
- mir::VarDebugInfoContents::Place(place) if place.local == mir::Local::new(1) => {
- // The projection is either `[.., Field, Deref]` or `[.., Field]`. It
- // implies whether the variable is captured by value or by reference.
- matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref)
- }
- _ => return None,
- };
- let prefix = if is_ref { "_ref__" } else { "" };
- Some(prefix.to_owned() + var.name.as_str())
- })
- .collect()
-}
-
/// Builds the DW_TAG_member debuginfo nodes for the upvars of a closure or generator.
/// For a generator, this will handle upvars shared by all states.
fn build_upvar_field_di_nodes<'ll, 'tcx>(
.all(|&t| t == cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t))
);
- let capture_names = closure_saved_names_of_captured_variables(cx.tcx, def_id);
+ let capture_names = cx.tcx.closure_saved_names_of_captured_variables(def_id);
let layout = cx.layout_of(closure_or_generator_ty);
up_var_tys
)
}
-// FIXME(eddyb) maybe precompute this? Right now it's computed once
-// per generator monomorphization, but it doesn't depend on substs.
-fn generator_layout_and_saved_local_names<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: DefId,
-) -> (&'tcx GeneratorLayout<'tcx>, IndexVec<mir::GeneratorSavedLocal, Option<Symbol>>) {
- let body = tcx.optimized_mir(def_id);
- let generator_layout = body.generator_layout().unwrap();
- let mut generator_saved_local_names = IndexVec::from_elem(None, &generator_layout.field_tys);
-
- let state_arg = mir::Local::new(1);
- for var in &body.var_debug_info {
- let mir::VarDebugInfoContents::Place(place) = &var.value else { continue };
- if place.local != state_arg {
- continue;
- }
- match place.projection[..] {
- [
- // Deref of the `Pin<&mut Self>` state argument.
- mir::ProjectionElem::Field(..),
- mir::ProjectionElem::Deref,
- // Field of a variant of the state.
- mir::ProjectionElem::Downcast(_, variant),
- mir::ProjectionElem::Field(field, _),
- ] => {
- let name = &mut generator_saved_local_names
- [generator_layout.variant_fields[variant][field]];
- if name.is_none() {
- name.replace(var.name);
- }
- }
- _ => {}
- }
- }
- (generator_layout, generator_saved_local_names)
-}
-
/// Computes the type parameters for a type, if any, for the given metadata.
fn build_generic_type_param_di_nodes<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
common::CodegenCx,
debuginfo::{
metadata::{
- build_field_di_node, closure_saved_names_of_captured_variables,
+ build_field_di_node,
enums::{tag_base_type, DiscrResult},
- file_metadata, generator_layout_and_saved_local_names, size_and_align_of, type_di_node,
+ file_metadata, size_and_align_of, type_di_node,
type_map::{self, Stub, UniqueTypeId},
unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, NO_SCOPE_METADATA,
UNKNOWN_LINE_NUMBER,
};
let (generator_layout, state_specific_upvar_names) =
- generator_layout_and_saved_local_names(cx.tcx, generator_def_id);
+ cx.tcx.generator_layout_and_saved_local_names(generator_def_id);
- let common_upvar_names = closure_saved_names_of_captured_variables(cx.tcx, generator_def_id);
+ let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(generator_def_id);
let variant_range = generator_substs.variant_range(generator_def_id, cx.tcx);
let variant_count = (variant_range.start.as_u32()..variant_range.end.as_u32()).len();
common::CodegenCx,
debuginfo::{
metadata::{
- closure_saved_names_of_captured_variables,
enums::tag_base_type,
- file_metadata, generator_layout_and_saved_local_names, size_and_align_of, type_di_node,
+ file_metadata, size_and_align_of, type_di_node,
type_map::{self, Stub, StubInfo, UniqueTypeId},
unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS,
UNKNOWN_LINE_NUMBER,
),
|cx, generator_type_di_node| {
let (generator_layout, state_specific_upvar_names) =
- generator_layout_and_saved_local_names(cx.tcx, generator_def_id);
+ cx.tcx.generator_layout_and_saved_local_names(generator_def_id);
let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = generator_type_and_layout.variants else {
bug!(
};
let common_upvar_names =
- closure_saved_names_of_captured_variables(cx.tcx, generator_def_id);
+ cx.tcx.closure_saved_names_of_captured_variables(generator_def_id);
// Build variant struct types
let variant_struct_type_di_nodes: SmallVec<_> = variants
use crate::value::Value;
use rustc_codegen_ssa::traits::TypeMembershipMethods;
use rustc_middle::ty::Ty;
-use rustc_symbol_mangling::typeid::typeid_for_fnabi;
+use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi};
use smallvec::SmallVec;
/// Declare a function.
self.set_type_metadata(llfn, typeid);
}
+ if self.tcx.sess.is_sanitizer_kcfi_enabled() {
+ let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
+ self.set_kcfi_type_metadata(llfn, kcfi_typeid);
+ }
+
llfn
}
pub err: String,
}
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_linkage_const_or_mut_type)]
-pub(crate) struct LinkageConstOrMutType {
- #[primary_span]
- pub span: Span,
-}
-
#[derive(Diagnostic)]
#[diag(codegen_llvm_sanitizer_memtag_requires_mte)]
pub(crate) struct SanitizerMemtagRequiresMte;
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_archive_build_failure)]
-pub(crate) struct ArchiveBuildFailure {
- pub error: std::io::Error,
-}
-
#[derive(Diagnostic)]
#[diag(codegen_llvm_error_writing_def_file)]
pub(crate) struct ErrorWritingDEFFile {
pub stderr: Cow<'a, str>,
}
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_unknown_archive_kind)]
-pub(crate) struct UnknownArchiveKind<'a> {
- pub kind: &'a str,
-}
-
#[derive(Diagnostic)]
#[diag(codegen_llvm_dynamic_linking_with_lto)]
#[note]
MD_type = 19,
MD_vcall_visibility = 28,
MD_noundef = 29,
+ MD_kcfi_type = 36,
}
/// LLVMRustAsmDialect
unsafe extern "C" fn(*mut c_void, *const c_char, *const c_char);
pub type SelfProfileAfterPassCallback = unsafe extern "C" fn(*mut c_void);
+pub type GetSymbolsCallback = unsafe extern "C" fn(*mut c_void, *const c_char) -> *mut c_void;
+pub type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> *mut c_void;
+
extern "C" {
pub fn LLVMRustInstallFatalErrorHandler();
pub fn LLVMRustDisableSystemDialogsOnCrash();
pub fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
pub fn LLVMRustGlobalAddMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata);
pub fn LLVMValueAsMetadata(Node: &Value) -> &Metadata;
+ pub fn LLVMIsAFunction(Val: &Value) -> Option<&Value>;
// Operations on constants of any type
pub fn LLVMConstNull(Ty: &Type) -> &Value;
NumArgs: c_uint,
Then: &'a BasicBlock,
Catch: &'a BasicBlock,
- Bundle: Option<&OperandBundleDef<'a>>,
+ OpBundles: *const Option<&OperandBundleDef<'a>>,
+ NumOpBundles: c_uint,
Name: *const c_char,
) -> &'a Value;
pub fn LLVMBuildLandingPad<'a>(
Fn: &'a Value,
Args: *const &'a Value,
NumArgs: c_uint,
- Bundle: Option<&OperandBundleDef<'a>>,
+ OpBundles: *const Option<&OperandBundleDef<'a>>,
+ NumOpBundles: c_uint,
) -> &'a Value;
pub fn LLVMRustBuildMemCpy<'a>(
B: &Builder<'a>,
pub fn LLVMRustGetMangledName(V: &Value, out: &RustString);
pub fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32;
+
+ pub fn LLVMRustIsBitcode(ptr: *const u8, len: usize) -> bool;
+
+ pub fn LLVMRustGetSymbols(
+ buf_ptr: *const u8,
+ buf_len: usize,
+ state: *mut c_void,
+ callback: GetSymbolsCallback,
+ error_callback: GetSymbolsErrorCallback,
+ ) -> *mut c_void;
}
.flatten();
features.extend(feats);
+ // FIXME: Move v8a to target definition list when earliest supported LLVM is 14.
+ if get_version() >= (14, 0, 0) && sess.target.arch == "aarch64" {
+ features.push("+v8a".into());
+ }
+
if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
sess.emit_err(TargetFeatureDisableOrEnable {
features: f,
)
}
}
+
+ fn set_kcfi_type_metadata(&self, function: &'ll Value, kcfi_typeid: u32) {
+ let kcfi_type_metadata = self.const_u32(kcfi_typeid);
+ unsafe {
+ llvm::LLVMGlobalSetMetadata(
+ function,
+ llvm::MD_kcfi_type as c_uint,
+ llvm::LLVMMDNodeInContext2(
+ self.llcx,
+ &llvm::LLVMValueAsMetadata(kcfi_type_metadata),
+ 1,
+ ),
+ )
+ }
+ }
}
test = false
[dependencies]
+ar_archive_writer = "0.1.1"
bitflags = "1.2.1"
cc = "1.0.69"
itertools = "0.10.1"
use super::metadata::search_for_section;
+pub use ar_archive_writer::get_native_object_symbols;
+use ar_archive_writer::{write_archive_to_stream, ArchiveKind, NewArchiveMember};
use object::read::archive::ArchiveFile;
+use object::read::macho::FatArch;
+use tempfile::Builder as TempFileBuilder;
use std::error::Error;
use std::fs::File;
-use std::io;
+use std::io::{self, Write};
use std::path::{Path, PathBuf};
-use crate::errors::ExtractBundledLibsError;
+// Re-exporting for rustc_codegen_llvm::back::archive
+pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind};
pub trait ArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a>;
fn build(self: Box<Self>, output: &Path) -> bool;
}
+
+#[must_use = "must call build() to finish building the archive"]
+pub struct ArArchiveBuilder<'a> {
+ sess: &'a Session,
+ get_object_symbols:
+ fn(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> io::Result<()>) -> io::Result<bool>,
+
+ src_archives: Vec<(PathBuf, Mmap)>,
+ // Don't use an `HashMap` here, as the order is important. `lib.rmeta` needs
+ // to be at the end of an archive in some cases for linkers to not get confused.
+ entries: Vec<(Vec<u8>, ArchiveEntry)>,
+}
+
+#[derive(Debug)]
+enum ArchiveEntry {
+ FromArchive { archive_index: usize, file_range: (u64, u64) },
+ File(PathBuf),
+}
+
+impl<'a> ArArchiveBuilder<'a> {
+ pub fn new(
+ sess: &'a Session,
+ get_object_symbols: fn(
+ buf: &[u8],
+ f: &mut dyn FnMut(&[u8]) -> io::Result<()>,
+ ) -> io::Result<bool>,
+ ) -> ArArchiveBuilder<'a> {
+ ArArchiveBuilder { sess, get_object_symbols, src_archives: vec![], entries: vec![] }
+ }
+}
+
+fn try_filter_fat_archs(
+ archs: object::read::Result<&[impl FatArch]>,
+ target_arch: object::Architecture,
+ archive_path: &Path,
+ archive_map_data: &[u8],
+) -> io::Result<Option<PathBuf>> {
+ let archs = archs.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
+
+ let desired = match archs.iter().filter(|a| a.architecture() == target_arch).next() {
+ Some(a) => a,
+ None => return Ok(None),
+ };
+
+ let (mut new_f, extracted_path) = tempfile::Builder::new()
+ .suffix(archive_path.file_name().unwrap())
+ .tempfile()?
+ .keep()
+ .unwrap();
+
+ new_f.write_all(
+ desired.data(archive_map_data).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?,
+ )?;
+
+ Ok(Some(extracted_path))
+}
+
+pub fn try_extract_macho_fat_archive(
+ sess: &Session,
+ archive_path: &Path,
+) -> io::Result<Option<PathBuf>> {
+ let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
+ let target_arch = match sess.target.arch.as_ref() {
+ "aarch64" => object::Architecture::Aarch64,
+ "x86_64" => object::Architecture::X86_64,
+ _ => return Ok(None),
+ };
+
+ match object::macho::FatHeader::parse(&*archive_map) {
+ Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC => {
+ let archs = object::macho::FatHeader::parse_arch32(&*archive_map);
+ try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
+ }
+ Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC_64 => {
+ let archs = object::macho::FatHeader::parse_arch64(&*archive_map);
+ try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
+ }
+ // Not a FatHeader at all, just return None.
+ _ => Ok(None),
+ }
+}
+
+impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
+ fn add_archive(
+ &mut self,
+ archive_path: &Path,
+ mut skip: Box<dyn FnMut(&str) -> bool + 'static>,
+ ) -> io::Result<()> {
+ let mut archive_path = archive_path.to_path_buf();
+ if self.sess.target.llvm_target.contains("-apple-macosx") {
+ if let Some(new_archive_path) =
+ try_extract_macho_fat_archive(&self.sess, &archive_path)?
+ {
+ archive_path = new_archive_path
+ }
+ }
+
+ if self.src_archives.iter().any(|archive| archive.0 == archive_path) {
+ return Ok(());
+ }
+
+ let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
+ let archive = ArchiveFile::parse(&*archive_map)
+ .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
+ let archive_index = self.src_archives.len();
+
+ for entry in archive.members() {
+ let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
+ let file_name = String::from_utf8(entry.name().to_vec())
+ .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
+ if !skip(&file_name) {
+ self.entries.push((
+ file_name.into_bytes(),
+ ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() },
+ ));
+ }
+ }
+
+ self.src_archives.push((archive_path.to_owned(), archive_map));
+ Ok(())
+ }
+
+ /// Adds an arbitrary file to this archive
+ fn add_file(&mut self, file: &Path) {
+ self.entries.push((
+ file.file_name().unwrap().to_str().unwrap().to_string().into_bytes(),
+ ArchiveEntry::File(file.to_owned()),
+ ));
+ }
+
+ /// Combine the provided files, rlibs, and native libraries into a single
+ /// `Archive`.
+ fn build(self: Box<Self>, output: &Path) -> bool {
+ let sess = self.sess;
+ match self.build_inner(output) {
+ Ok(any_members) => any_members,
+ Err(e) => sess.emit_fatal(ArchiveBuildFailure { error: e }),
+ }
+ }
+}
+
+impl<'a> ArArchiveBuilder<'a> {
+ fn build_inner(self, output: &Path) -> io::Result<bool> {
+ let archive_kind = match &*self.sess.target.archive_format {
+ "gnu" => ArchiveKind::Gnu,
+ "bsd" => ArchiveKind::Bsd,
+ "darwin" => ArchiveKind::Darwin,
+ "coff" => ArchiveKind::Coff,
+ kind => {
+ self.sess.emit_fatal(UnknownArchiveKind { kind });
+ }
+ };
+
+ let mut entries = Vec::new();
+
+ for (entry_name, entry) in self.entries {
+ let data =
+ match entry {
+ ArchiveEntry::FromArchive { archive_index, file_range } => {
+ let src_archive = &self.src_archives[archive_index];
+
+ let data = &src_archive.1
+ [file_range.0 as usize..file_range.0 as usize + file_range.1 as usize];
+
+ Box::new(data) as Box<dyn AsRef<[u8]>>
+ }
+ ArchiveEntry::File(file) => unsafe {
+ Box::new(
+ Mmap::map(File::open(file).map_err(|err| {
+ io_error_context("failed to open object file", err)
+ })?)
+ .map_err(|err| io_error_context("failed to map object file", err))?,
+ ) as Box<dyn AsRef<[u8]>>
+ },
+ };
+
+ entries.push(NewArchiveMember {
+ buf: data,
+ get_symbols: self.get_object_symbols,
+ member_name: String::from_utf8(entry_name).unwrap(),
+ mtime: 0,
+ uid: 0,
+ gid: 0,
+ perms: 0o644,
+ })
+ }
+
+ // Write to a temporary file first before atomically renaming to the final name.
+ // This prevents programs (including rustc) from attempting to read a partial archive.
+ // It also enables writing an archive with the same filename as a dependency on Windows as
+ // required by a test.
+ let mut archive_tmpfile = TempFileBuilder::new()
+ .suffix(".temp-archive")
+ .tempfile_in(output.parent().unwrap_or_else(|| Path::new("")))
+ .map_err(|err| io_error_context("couldn't create a temp file", err))?;
+
+ write_archive_to_stream(
+ archive_tmpfile.as_file_mut(),
+ &entries,
+ true,
+ archive_kind,
+ true,
+ false,
+ )?;
+
+ let any_entries = !entries.is_empty();
+ drop(entries);
+ // Drop src_archives to unmap all input archives, which is necessary if we want to write the
+ // output archive to the same location as an input archive on Windows.
+ drop(self.src_archives);
+
+ archive_tmpfile
+ .persist(output)
+ .map_err(|err| io_error_context("failed to rename archive file", err.error))?;
+
+ Ok(any_entries)
+ }
+}
+
+fn io_error_context(context: &str, err: io::Error) -> io::Error {
+ io::Error::new(io::ErrorKind::Other, format!("{context}: {err}"))
+}
sess,
crate_type,
outputs,
- codegen_results.crate_info.local_crate_name.as_str(),
+ codegen_results.crate_info.local_crate_name,
);
match crate_type {
CrateType::Rlib => {
&search_paths.get_or_init(|| archive_search_paths(sess)),
);
} else {
- // HACK/FIXME: Fixup a circular dependency between libgcc and libc
- // with glibc. This logic should be moved to the libc crate.
- if cnum != LOCAL_CRATE
- && sess.target.os == "linux"
- && sess.target.env == "gnu"
- && name == "c"
- {
- cmd.link_staticlib("gcc", false);
- }
cmd.link_staticlib(name, verbatim)
}
}
use rustc_target::abi::{Align, Size, VariantIdx};
use std::collections::BTreeSet;
-use std::convert::TryFrom;
use std::time::{Duration, Instant};
use itertools::Itertools;
#[derive(Diagnostic)]
#[diag(codegen_ssa_unsupported_link_self_contained)]
pub struct UnsupportedLinkSelfContained;
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_archive_build_failure)]
+// Public for rustc_codegen_llvm::back::archive
+pub struct ArchiveBuildFailure {
+ pub error: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unknown_archive_kind)]
+// Public for rustc_codegen_llvm::back::archive
+pub struct UnknownArchiveKind<'a> {
+ pub kind: &'a str,
+}
helper: TerminatorCodegenHelper<'tcx>,
bx: &mut Bx,
discr: &mir::Operand<'tcx>,
- switch_ty: Ty<'tcx>,
targets: &SwitchTargets,
) {
let discr = self.codegen_operand(bx, &discr);
- // `switch_ty` is redundant, sanity-check that.
- assert_eq!(discr.layout.ty, switch_ty);
+ let switch_ty = discr.layout.ty;
let mut target_iter = targets.iter();
if target_iter.len() == 1 {
// If there are two targets (one conditional, one fallback), emit `br` instead of
helper.funclet_br(self, bx, target, mergeable_succ())
}
- mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref targets } => {
- self.codegen_switchint_terminator(helper, bx, discr, switch_ty, targets);
+ mir::TerminatorKind::SwitchInt { ref discr, ref targets } => {
+ self.codegen_switchint_terminator(helper, bx, discr, targets);
MergingSucc::False
}
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir;
use rustc_middle::ty;
+use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_session::config::DebugInfo;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{BytePos, Span};
-use rustc_target::abi::Abi;
-use rustc_target::abi::Size;
+use rustc_target::abi::{Abi, Size, VariantIdx};
use super::operand::{OperandRef, OperandValue};
use super::place::PlaceRef;
}
}
+trait DebugInfoOffsetLocation<'tcx, Bx> {
+ fn deref(&self, bx: &mut Bx) -> Self;
+ fn layout(&self) -> TyAndLayout<'tcx>;
+ fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self;
+ fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self;
+}
+
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
+ for PlaceRef<'tcx, Bx::Value>
+{
+ fn deref(&self, bx: &mut Bx) -> Self {
+ bx.load_operand(*self).deref(bx.cx())
+ }
+
+ fn layout(&self) -> TyAndLayout<'tcx> {
+ self.layout
+ }
+
+ fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self {
+ PlaceRef::project_field(*self, bx, field.index())
+ }
+
+ fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
+ self.project_downcast(bx, variant)
+ }
+}
+
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
+ for TyAndLayout<'tcx>
+{
+ fn deref(&self, bx: &mut Bx) -> Self {
+ bx.cx().layout_of(
+ self.ty.builtin_deref(true).unwrap_or_else(|| bug!("cannot deref `{}`", self.ty)).ty,
+ )
+ }
+
+ fn layout(&self) -> TyAndLayout<'tcx> {
+ *self
+ }
+
+ fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self {
+ self.field(bx.cx(), field.index())
+ }
+
+ fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
+ self.for_variant(bx.cx(), variant)
+ }
+}
+
+struct DebugInfoOffset<T> {
+ /// Offset from the `base` used to calculate the debuginfo offset.
+ direct_offset: Size,
+ /// Each offset in this vector indicates one level of indirection from the base or previous
+ /// indirect offset plus a dereference.
+ indirect_offsets: Vec<Size>,
+ /// The final location debuginfo should point to.
+ result: T,
+}
+
+fn calculate_debuginfo_offset<
+ 'a,
+ 'tcx,
+ Bx: BuilderMethods<'a, 'tcx>,
+ L: DebugInfoOffsetLocation<'tcx, Bx>,
+>(
+ bx: &mut Bx,
+ local: mir::Local,
+ var: &PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
+ base: L,
+) -> DebugInfoOffset<L> {
+ let mut direct_offset = Size::ZERO;
+ // FIXME(eddyb) use smallvec here.
+ let mut indirect_offsets = vec![];
+ let mut place = base;
+
+ for elem in &var.projection[..] {
+ match *elem {
+ mir::ProjectionElem::Deref => {
+ indirect_offsets.push(Size::ZERO);
+ place = place.deref(bx);
+ }
+ mir::ProjectionElem::Field(field, _) => {
+ let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
+ *offset += place.layout().fields.offset(field.index());
+ place = place.project_field(bx, field);
+ }
+ mir::ProjectionElem::Downcast(_, variant) => {
+ place = place.downcast(bx, variant);
+ }
+ _ => span_bug!(
+ var.source_info.span,
+ "unsupported var debuginfo place `{:?}`",
+ mir::Place { local, projection: var.projection },
+ ),
+ }
+ }
+
+ DebugInfoOffset { direct_offset, indirect_offsets, result: place }
+}
+
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
bx.set_span(source_info.span);
let Some(dbg_var) = var.dbg_var else { continue };
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
- let mut direct_offset = Size::ZERO;
- // FIXME(eddyb) use smallvec here.
- let mut indirect_offsets = vec![];
- let mut place = base;
-
- for elem in &var.projection[..] {
- match *elem {
- mir::ProjectionElem::Deref => {
- indirect_offsets.push(Size::ZERO);
- place = bx.load_operand(place).deref(bx.cx());
- }
- mir::ProjectionElem::Field(field, _) => {
- let i = field.index();
- let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
- *offset += place.layout.fields.offset(i);
- place = place.project_field(bx, i);
- }
- mir::ProjectionElem::Downcast(_, variant) => {
- place = place.project_downcast(bx, variant);
- }
- _ => span_bug!(
- var.source_info.span,
- "unsupported var debuginfo place `{:?}`",
- mir::Place { local, projection: var.projection },
- ),
- }
- }
+ let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
+ calculate_debuginfo_offset(bx, local, &var, base.layout);
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
|| !matches!(&indirect_offsets[..], [Size::ZERO] | []));
if should_create_individual_allocas {
+ let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
+ calculate_debuginfo_offset(bx, local, &var, base);
+
// Create a variable which will be a pointer to the actual value
let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut {
mutbl: mir::Mutability::Mut,
// In the algorithm above, we can change
// cast(relative_tag) + niche_variants.start()
// into
- // cast(tag) + (niche_variants.start() - niche_start)
+ // cast(tag + (niche_variants.start() - niche_start))
// if either the casted type is no larger than the original
// type, or if the niche values are contiguous (in either the
// signed or unsigned sense).
- let can_incr_after_cast = cast_smaller || niches_ule || niches_sle;
+ let can_incr = cast_smaller || niches_ule || niches_sle;
let data_for_boundary_niche = || -> Option<(IntPredicate, u128)> {
- if !can_incr_after_cast {
+ if !can_incr {
None
} else if niche_start == low_unsigned {
Some((IntPredicate::IntULE, niche_end))
// The algorithm is now this:
// is_niche = tag <= niche_end
// discr = if is_niche {
- // cast(tag) + (niche_variants.start() - niche_start)
+ // cast(tag + (niche_variants.start() - niche_start))
// } else {
// untagged_variant
// }
// (the first line may instead be tag >= niche_start,
// and may be a signed or unsigned comparison)
+ // The arithmetic must be done before the cast, so we can
+ // have the correct wrapping behavior. See issue #104519 for
+ // the consequences of getting this wrong.
let is_niche =
bx.icmp(predicate, tag, bx.cx().const_uint_big(tag_llty, constant));
+ let delta = (niche_variants.start().as_u32() as u128).wrapping_sub(niche_start);
+ let incr_tag = if delta == 0 {
+ tag
+ } else {
+ bx.add(tag, bx.cx().const_uint_big(tag_llty, delta))
+ };
+
let cast_tag = if cast_smaller {
- bx.intcast(tag, cast_to, false)
+ bx.intcast(incr_tag, cast_to, false)
} else if niches_ule {
- bx.zext(tag, cast_to)
+ bx.zext(incr_tag, cast_to)
} else {
- bx.sext(tag, cast_to)
+ bx.sext(incr_tag, cast_to)
};
- let delta = (niche_variants.start().as_u32() as u128).wrapping_sub(niche_start);
- (is_niche, cast_tag, delta)
+ (is_niche, cast_tag, 0)
} else {
// The special cases don't apply, so we'll have to go with
// the general algorithm.
pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> {
fn set_type_metadata(&self, function: Self::Function, typeid: String);
fn typeid_metadata(&self, typeid: String) -> Self::Value;
+ fn set_kcfi_type_metadata(&self, function: Self::Function, typeid: u32);
}
pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {
// Helper closure to print duplicated lines.
let mut flush_last_line = |last_frame, times| {
if let Some((line, span)) = last_frame {
- err.span_label(span, &line);
+ err.span_note(span, &line);
// Don't print [... additional calls ...] if the number of lines is small
if times < 3 {
for _ in 0..times {
- err.span_label(span, &line);
+ err.span_note(span, &line);
}
} else {
- err.span_label(
+ err.span_note(
span,
format!("[... {} additional calls {} ...]", times, &line),
);
use std::borrow::Cow;
-use std::convert::TryInto;
use either::{Left, Right};
let align = ImmTy::from_uint(target_align, args[1].layout).into();
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
- // We replace the entire entire function call with a "tail call".
+ // We replace the entire function call with a "tail call".
// Note that this happens before the frame of the original function
// is pushed on the stack.
self.eval_fn_call(
use std::assert_matches::assert_matches;
-use std::convert::TryFrom;
use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::{Float, FloatConvert};
};
use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_session::Limit;
-use rustc_span::{Pos, Span};
+use rustc_span::Span;
use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout};
use super::{
if tcx.def_key(self.instance.def_id()).disambiguated_data.data
== DefPathData::ClosureExpr
{
- write!(f, "inside closure")?;
+ write!(f, "inside closure")
} else {
// Note: this triggers a `good_path_bug` state, which means that if we ever get here
// we must emit a diagnostic. We should never display a `FrameInfo` unless we
// actually want to emit a warning or error to the user.
- write!(f, "inside `{}`", self.instance)?;
+ write!(f, "inside `{}`", self.instance)
}
- if !self.span.is_dummy() {
- let sm = tcx.sess.source_map();
- let lo = sm.lookup_char_pos(self.span.lo());
- write!(
- f,
- " at {}:{}:{}",
- sm.filename_for_diagnostics(&lo.file.name),
- lo.line,
- lo.col.to_usize() + 1
- )?;
- }
- Ok(())
})
}
}
return_to_block: StackPopCleanup,
) -> InterpResult<'tcx> {
trace!("body: {:#?}", body);
+ // Clobber previous return place contents, nobody is supposed to be able to see them any more
+ // This also checks dereferenceable, but not align. We rely on all constructed places being
+ // sufficiently aligned (in particular we rely on `deref_operand` checking alignment).
+ self.write_uninit(return_place)?;
// first push a stack frame so we have access to the local substs
let pre_frame = Frame {
body,
//! looking at their MIR. Intrinsics/functions supported here are shared by CTFE
//! and miri.
-use std::convert::TryFrom;
-
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
self,
-use std::convert::TryFrom;
-
use rustc_ast::Mutability;
use rustc_hir::lang_items::LangItem;
use rustc_middle::mir::TerminatorKind;
Ok(())
}
- /// Executes a retagging operation.
+ /// Executes a retagging operation for a single pointer.
+ /// Returns the possibly adjusted pointer.
#[inline]
- fn retag(
+ fn retag_ptr_value(
+ _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ _kind: mir::RetagKind,
+ val: &ImmTy<'tcx, Self::Provenance>,
+ ) -> InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> {
+ Ok(val.clone())
+ }
+
+ /// Executes a retagging operation on a compound value.
+ /// Replaces all pointers stored in the given place.
+ #[inline]
+ fn retag_place_contents(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_kind: mir::RetagKind,
_place: &PlaceTy<'tcx, Self::Provenance>,
-use std::convert::TryFrom;
-
use rustc_apfloat::Float;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::ty::layout::LayoutOf;
-use super::{InterpCx, Machine};
+use super::{ImmTy, InterpCx, Machine};
/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
/// same type as the result.
// Stacked Borrows.
Retag(kind, place) => {
let dest = self.eval_place(**place)?;
- M::retag(self, *kind, &dest)?;
+ M::retag_place_contents(self, *kind, &dest)?;
}
Intrinsic(box ref intrinsic) => self.emulate_nondiverging_intrinsic(intrinsic)?,
self.write_scalar(Scalar::from_machine_usize(len, self), &dest)?;
}
- AddressOf(_, place) | Ref(_, _, place) => {
+ Ref(_, borrow_kind, place) => {
let src = self.eval_place(place)?;
let place = self.force_allocation(&src)?;
- self.write_immediate(place.to_ref(self), &dest)?;
+ let val = ImmTy::from_immediate(place.to_ref(self), dest.layout);
+ // A fresh reference was created, make sure it gets retagged.
+ let val = M::retag_ptr_value(
+ self,
+ if borrow_kind.allows_two_phase_borrow() {
+ mir::RetagKind::TwoPhase
+ } else {
+ mir::RetagKind::Default
+ },
+ &val,
+ )?;
+ self.write_immediate(*val, &dest)?;
+ }
+
+ AddressOf(_, place) => {
+ // Figure out whether this is an addr_of of an already raw place.
+ let place_base_raw = if place.has_deref() {
+ let ty = self.frame().body.local_decls[place.local].ty;
+ ty.is_unsafe_ptr()
+ } else {
+ // Not a deref, and thus not raw.
+ false
+ };
+
+ let src = self.eval_place(place)?;
+ let place = self.force_allocation(&src)?;
+ let mut val = ImmTy::from_immediate(place.to_ref(self), dest.layout);
+ if !place_base_raw {
+ // If this was not already raw, it needs retagging.
+ val = M::retag_ptr_value(self, mir::RetagKind::Raw, &val)?;
+ }
+ self.write_immediate(*val, &dest)?;
}
NullaryOp(null_op, ty) => {
Goto { target } => self.go_to_block(target),
- SwitchInt { ref discr, ref targets, switch_ty } => {
+ SwitchInt { ref discr, ref targets } => {
let discr = self.read_immediate(&self.eval_operand(discr, None)?)?;
trace!("SwitchInt({:?})", *discr);
- assert_eq!(discr.layout.ty, switch_ty);
// Branch to the `otherwise` case by default, if no match is found.
let mut target_block = targets.otherwise();
use rustc_middle::mir::interpret::InterpResult;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
-use std::convert::TryInto;
use std::ops::ControlFlow;
/// Checks whether a type contains generic parameters which require substitution.
//! That's useful because it means other passes (e.g. promotion) can rely on `const`s
//! to be const-safe.
-use std::convert::TryFrom;
use std::fmt::{Display, Write};
use std::num::NonZeroUsize;
match elem {
ProjectionElem::Deref => {
let mut promotable = false;
+ // When a static is used by-value, that gets desugared to `*STATIC_ADDR`,
+ // and we need to be able to promote this. So check if this deref matches
+ // that specific pattern.
+
// We need to make sure this is a `Deref` of a local with no further projections.
// Discussion can be found at
// https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
if let Some(local) = place_base.as_local() {
- // This is a special treatment for cases like *&STATIC where STATIC is a
- // global static variable.
- // This pattern is generated only when global static variables are directly
- // accessed and is qualified for promotion safely.
if let TempState::Defined { location, .. } = self.temps[local] {
let def_stmt = self.body[location.block]
.statements
use rustc_data_structures::fx::FxHashSet;
use rustc_index::bit_set::BitSet;
+use rustc_infer::traits::Reveal;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
use rustc_middle::mir::visit::{PlaceContext, Visitor};
return;
}
let def_id = body.source.def_id();
- let param_env = tcx.param_env(def_id);
let mir_phase = self.mir_phase;
+ let param_env = match mir_phase.reveal() {
+ Reveal::UserFacing => tcx.param_env(def_id),
+ Reveal::All => tcx.param_env_reveal_all_normalized(def_id),
+ };
let always_live_locals = always_storage_live_locals(body);
let storage_liveness = MaybeStorageLive::new(always_live_locals)
TerminatorKind::Goto { target } => {
self.check_edge(location, *target, EdgeKind::Normal);
}
- TerminatorKind::SwitchInt { targets, switch_ty, discr } => {
- let ty = discr.ty(&self.body.local_decls, self.tcx);
- if ty != *switch_ty {
- self.fail(
- location,
- format!(
- "encountered `SwitchInt` terminator with type mismatch: {:?} != {:?}",
- ty, switch_ty,
- ),
- );
- }
+ TerminatorKind::SwitchInt { targets, discr } => {
+ let switch_ty = discr.ty(&self.body.local_decls, self.tcx);
let target_width = self.tcx.sess.target.pointer_width;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_target::abi::VariantIdx;
-use std::convert::TryFrom;
use std::iter::TrustedLen;
/// Expand `lhs = Rvalue::Aggregate(kind, operands)` into assignments to the fields.
use crate::stable_hasher;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
-use std::convert::TryInto;
use std::hash::{Hash, Hasher};
#[cfg(test)]
}
}
-impl_stable_hash_via_hash!(Fingerprint);
+impl_stable_traits_for_trivial_type!(Fingerprint);
impl<E: Encoder> Encodable<E> for Fingerprint {
#[inline]
use crate::graph::vec_graph::VecGraph;
use crate::graph::{DirectedGraph, GraphSuccessors, WithNumEdges, WithNumNodes, WithSuccessors};
use rustc_index::vec::{Idx, IndexVec};
-use std::cmp::Ord;
use std::ops::Range;
#[cfg(test)]
-use std::cmp::Ord;
-
use crate::graph::{DirectedGraph, GraphSuccessors, WithNumEdges, WithNumNodes, WithSuccessors};
use rustc_index::vec::{Idx, IndexVec};
use std::ops::Deref;
use std::ptr;
-use crate::fingerprint::Fingerprint;
-
mod private {
#[derive(Clone, Copy, Debug)]
pub struct PrivateZst;
if ptr::eq(self.0, other.0) {
Some(Ordering::Equal)
} else {
- let res = self.0.partial_cmp(&other.0);
+ let res = self.0.partial_cmp(other.0);
debug_assert_ne!(res, Some(Ordering::Equal));
res
}
if ptr::eq(self.0, other.0) {
Ordering::Equal
} else {
- let res = self.0.cmp(&other.0);
+ let res = self.0.cmp(other.0);
debug_assert_ne!(res, Ordering::Equal);
res
}
}
}
-/// A helper type that you can wrap round your own type in order to automatically
-/// cache the stable hash on creation and not recompute it whenever the stable hash
-/// of the type is computed.
-/// This is only done in incremental mode. You can also opt out of caching by using
-/// StableHash::ZERO for the hash, in which case the hash gets computed each time.
-/// This is useful if you have values that you intern but never (can?) use for stable
-/// hashing.
-#[derive(Copy, Clone)]
-pub struct WithStableHash<T> {
- pub internee: T,
- pub stable_hash: Fingerprint,
-}
-
-impl<T: PartialEq> PartialEq for WithStableHash<T> {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.internee.eq(&other.internee)
- }
-}
-
-impl<T: Eq> Eq for WithStableHash<T> {}
-
-impl<T: Ord> PartialOrd for WithStableHash<T> {
- fn partial_cmp(&self, other: &WithStableHash<T>) -> Option<Ordering> {
- Some(self.internee.cmp(&other.internee))
- }
-}
-
-impl<T: Ord> Ord for WithStableHash<T> {
- fn cmp(&self, other: &WithStableHash<T>) -> Ordering {
- self.internee.cmp(&other.internee)
- }
-}
-
-impl<T> Deref for WithStableHash<T> {
- type Target = T;
-
- #[inline]
- fn deref(&self) -> &T {
- &self.internee
- }
-}
-
-impl<T: Hash> Hash for WithStableHash<T> {
- #[inline]
- fn hash<H: Hasher>(&self, s: &mut H) {
- if self.stable_hash != Fingerprint::ZERO {
- self.stable_hash.hash(s)
- } else {
- self.internee.hash(s)
- }
- }
-}
-
-impl<T: HashStable<CTX>, CTX> HashStable<CTX> for WithStableHash<T> {
- fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
- if self.stable_hash == Fingerprint::ZERO || cfg!(debug_assertions) {
- // No cached hash available. This can only mean that incremental is disabled.
- // We don't cache stable hashes in non-incremental mode, because they are used
- // so rarely that the performance actually suffers.
-
- // We need to build the hash as if we cached it and then hash that hash, as
- // otherwise the hashes will differ between cached and non-cached mode.
- let stable_hash: Fingerprint = {
- let mut hasher = StableHasher::new();
- self.internee.hash_stable(hcx, &mut hasher);
- hasher.finish()
- };
- if cfg!(debug_assertions) && self.stable_hash != Fingerprint::ZERO {
- assert_eq!(
- stable_hash, self.stable_hash,
- "cached stable hash does not match freshly computed stable hash"
- );
- }
- stable_hash.hash_stable(hcx, hasher);
- } else {
- self.stable_hash.hash_stable(hcx, hasher);
- }
- }
-}
-
#[cfg(test)]
mod tests;
#[inline]
fn deref(&self) -> &[u8] {
+ &self.0
+ }
+}
+
+impl AsRef<[u8]> for Mmap {
+ fn as_ref(&self) -> &[u8] {
&*self.0
}
}
#[inline]
fn deref(&self) -> &[u8] {
- &*self.0
+ &self.0
}
}
impl DerefMut for MmapMut {
#[inline]
fn deref_mut(&mut self) -> &mut [u8] {
- &mut *self.0
+ &mut self.0
}
}
/////////////////////////////////////////////////////////////////////////////
use std::borrow::Borrow;
-use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
-use std::convert::From;
+use std::cmp::Ordering;
use std::fmt::{self, Debug};
use std::hash::{Hash, Hasher};
-use std::marker::{Send, Sync};
impl<O, T: ?Sized> Deref for OwningRef<O, T> {
type Target = T;
impl<O, T: ?Sized> AsRef<T> for OwningRef<O, T> {
fn as_ref(&self) -> &T {
- &*self
+ self
}
}
impl<O, T: ?Sized> AsRef<T> for OwningRefMut<O, T> {
fn as_ref(&self) -> &T {
- &*self
+ self
}
}
impl<O, T: ?Sized> AsMut<T> for OwningRefMut<O, T> {
fn as_mut(&mut self) -> &mut T {
- &mut *self
+ self
}
}
impl<O, T: ?Sized> Borrow<T> for OwningRef<O, T> {
fn borrow(&self) -> &T {
- &*self
+ self
}
}
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
- (&*self as &T).eq(&*other as &T)
+ self.deref().eq(other.deref())
}
}
T: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- (&*self as &T).partial_cmp(&*other as &T)
+ self.deref().partial_cmp(other.deref())
}
}
T: Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
- (&*self as &T).cmp(&*other as &T)
+ self.deref().cmp(other.deref())
}
}
T: Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
- (&*self as &T).hash(state);
+ self.deref().hash(state);
}
}
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
- (&*self as &T).eq(&*other as &T)
+ self.deref().eq(other.deref())
}
}
T: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- (&*self as &T).partial_cmp(&*other as &T)
+ self.deref().partial_cmp(other.deref())
}
}
T: Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
- (&*self as &T).cmp(&*other as &T)
+ self.deref().cmp(other.deref())
}
}
T: Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
- (&*self as &T).hash(state);
+ self.deref().hash(state);
}
}
// std types integration and convenience type defs
/////////////////////////////////////////////////////////////////////////////
-use std::boxed::Box;
use std::cell::{Ref, RefCell, RefMut};
use std::rc::Rc;
use std::sync::Arc;
mod owning_ref {
use super::super::OwningRef;
use super::super::{BoxRef, Erased, ErasedBoxRef, RcRef};
- use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
+ use std::cmp::Ordering;
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
mod owning_ref_mut {
use super::super::BoxRef;
use super::super::{BoxRefMut, Erased, ErasedBoxRefMut, OwningRefMut};
- use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
+ use std::cmp::Ordering;
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::borrow::Borrow;
use std::collections::hash_map::Entry;
-use std::convert::Into;
use std::error::Error;
use std::fs;
use std::path::Path;
F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a>,
{
let profiler = profiler_ref.profiler.as_ref().unwrap();
- f(&**profiler)
+ f(profiler)
}
if self.event_filter_mask.contains(event_filter) {
pub fn with_profiler(&self, f: impl FnOnce(&SelfProfiler)) {
if let Some(profiler) = &self.profiler {
- f(&profiler)
+ f(profiler)
}
}
if let Some((start_time, start_rss, ref message)) = self.start_and_message {
let end_rss = get_resident_set_size();
let dur = start_time.elapsed();
- print_time_passes_entry(&message, dur, start_rss, end_rss);
+ print_time_passes_entry(message, dur, start_rss, end_rss);
}
}
}
-use crate::stable_hasher::{HashStable, StableHasher};
+use crate::stable_hasher::{HashStable, StableHasher, StableOrd};
use std::borrow::Borrow;
use std::cmp::Ordering;
-use std::iter::FromIterator;
use std::mem;
use std::ops::{Bound, Index, IndexMut, RangeBounds};
}
}
-impl<K: HashStable<CTX>, V: HashStable<CTX>, CTX> HashStable<CTX> for SortedMap<K, V> {
+impl<K: HashStable<CTX> + StableOrd, V: HashStable<CTX>, CTX> HashStable<CTX> for SortedMap<K, V> {
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
self.data.hash_stable(ctx, hasher);
//! A variant of `SortedMap` that preserves insertion order.
use std::hash::{Hash, Hasher};
-use std::iter::FromIterator;
use crate::stable_hasher::{HashStable, StableHasher};
use rustc_index::vec::{Idx, IndexVec};
self.items.hash(hasher)
}
}
+
impl<I: Idx, K, V, C> HashStable<C> for SortedIndexMultiMap<I, K, V>
where
K: HashStable<C>,
V: HashStable<C>,
{
fn hash_stable(&self, ctx: &mut C, hasher: &mut StableHasher) {
- self.items.hash_stable(ctx, hasher)
+ let SortedIndexMultiMap {
+ items,
+ // We can ignore this field because it is not observable from the outside.
+ idx_sorted_by_item_key: _,
+ } = self;
+
+ items.hash_stable(ctx, hasher)
}
}
use std::fmt;
-use std::iter::ExactSizeIterator;
use std::iter::FusedIterator;
-use std::iter::Iterator;
/// Iterator which may contain instance of
/// one of two specific implementations.
use arrayvec::ArrayVec;
use std::fmt;
use std::hash::Hash;
-use std::iter::FromIterator;
use std::ops::Index;
// For pointer-sized arguments arrays
use std::fmt;
use std::hash::Hash;
-use std::iter::FromIterator;
use super::map::SsoHashMap;
fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType;
}
-/// Implement HashStable by just calling `Hash::hash()`.
+/// Trait for marking a type as having a sort order that is
+/// stable across compilation session boundaries. More formally:
+///
+/// ```txt
+/// Ord::cmp(a1, b1) == Ord:cmp(a2, b2)
+/// where a2 = decode(encode(a1, context1), context2)
+/// b2 = decode(encode(b1, context1), context2)
+/// ```
+///
+/// i.e. the result of `Ord::cmp` is not influenced by encoding
+/// the values in one session and then decoding them in another
+/// session.
+///
+/// This is trivially true for types where encoding and decoding
+/// don't change the bytes of the values that are used during
+/// comparison and comparison only depends on these bytes (as
+/// opposed to some non-local state). Examples are u32, String,
+/// Path, etc.
+///
+/// But it is not true for:
+/// - `*const T` and `*mut T` because the values of these pointers
+/// will change between sessions.
+/// - `DefIndex`, `CrateNum`, `LocalDefId`, because their concrete
+/// values depend on state that might be different between
+/// compilation sessions.
+pub unsafe trait StableOrd: Ord {}
+
+/// Implement HashStable by just calling `Hash::hash()`. Also implement `StableOrd` for the type since
+/// that has the same requirements.
///
/// **WARNING** This is only valid for types that *really* don't need any context for fingerprinting.
/// But it is easy to misuse this macro (see [#96013](https://github.com/rust-lang/rust/issues/96013)
/// here in this module.
///
/// Use `#[derive(HashStable_Generic)]` instead.
-macro_rules! impl_stable_hash_via_hash {
+macro_rules! impl_stable_traits_for_trivial_type {
($t:ty) => {
impl<CTX> $crate::stable_hasher::HashStable<CTX> for $t {
#[inline]
::std::hash::Hash::hash(self, hasher);
}
}
+
+ unsafe impl $crate::stable_hasher::StableOrd for $t {}
};
}
-impl_stable_hash_via_hash!(i8);
-impl_stable_hash_via_hash!(i16);
-impl_stable_hash_via_hash!(i32);
-impl_stable_hash_via_hash!(i64);
-impl_stable_hash_via_hash!(isize);
+impl_stable_traits_for_trivial_type!(i8);
+impl_stable_traits_for_trivial_type!(i16);
+impl_stable_traits_for_trivial_type!(i32);
+impl_stable_traits_for_trivial_type!(i64);
+impl_stable_traits_for_trivial_type!(isize);
-impl_stable_hash_via_hash!(u8);
-impl_stable_hash_via_hash!(u16);
-impl_stable_hash_via_hash!(u32);
-impl_stable_hash_via_hash!(u64);
-impl_stable_hash_via_hash!(usize);
+impl_stable_traits_for_trivial_type!(u8);
+impl_stable_traits_for_trivial_type!(u16);
+impl_stable_traits_for_trivial_type!(u32);
+impl_stable_traits_for_trivial_type!(u64);
+impl_stable_traits_for_trivial_type!(usize);
-impl_stable_hash_via_hash!(u128);
-impl_stable_hash_via_hash!(i128);
+impl_stable_traits_for_trivial_type!(u128);
+impl_stable_traits_for_trivial_type!(i128);
-impl_stable_hash_via_hash!(char);
-impl_stable_hash_via_hash!(());
+impl_stable_traits_for_trivial_type!(char);
+impl_stable_traits_for_trivial_type!(());
impl<CTX> HashStable<CTX> for ! {
fn hash_stable(&self, _ctx: &mut CTX, _hasher: &mut StableHasher) {
impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Vec<T> {
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
- (&self[..]).hash_stable(ctx, hasher);
+ self[..].hash_stable(ctx, hasher);
}
}
}
}
-impl<A, CTX> HashStable<CTX> for SmallVec<[A; 1]>
+impl<A, const N: usize, CTX> HashStable<CTX> for SmallVec<[A; N]>
where
A: HashStable<CTX>,
{
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
- (&self[..]).hash_stable(ctx, hasher);
+ self[..].hash_stable(ctx, hasher);
}
}
impl<CTX> HashStable<CTX> for String {
#[inline]
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
- (&self[..]).hash_stable(hcx, hasher);
+ self[..].hash_stable(hcx, hasher);
}
}
+// Safety: String comparison only depends on their contents and the
+// contents are not changed by (de-)serialization.
+unsafe impl StableOrd for String {}
+
impl<HCX> ToStableHashKey<HCX> for String {
type KeyType = String;
#[inline]
}
}
+// Safety: sort order of bools is not changed by (de-)serialization.
+unsafe impl StableOrd for bool {}
+
impl<T, CTX> HashStable<CTX> for Option<T>
where
T: HashStable<CTX>,
}
}
+// Safety: the Option wrapper does not add instability to comparison.
+unsafe impl<T: StableOrd> StableOrd for Option<T> {}
+
impl<T1, T2, CTX> HashStable<CTX> for Result<T1, T2>
where
T1: HashStable<CTX>,
}
}
-impl_stable_hash_via_hash!(::std::path::Path);
-impl_stable_hash_via_hash!(::std::path::PathBuf);
+impl_stable_traits_for_trivial_type!(::std::path::Path);
+impl_stable_traits_for_trivial_type!(::std::path::PathBuf);
impl<K, V, R, HCX> HashStable<HCX> for ::std::collections::HashMap<K, V, R>
where
impl<K, V, HCX> HashStable<HCX> for ::std::collections::BTreeMap<K, V>
where
- K: ToStableHashKey<HCX>,
+ K: HashStable<HCX> + StableOrd,
V: HashStable<HCX>,
{
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
- stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, (key, value)| {
- let key = key.to_stable_hash_key(hcx);
- key.hash_stable(hcx, hasher);
- value.hash_stable(hcx, hasher);
- });
+ self.len().hash_stable(hcx, hasher);
+ for entry in self.iter() {
+ entry.hash_stable(hcx, hasher);
+ }
}
}
impl<K, HCX> HashStable<HCX> for ::std::collections::BTreeSet<K>
where
- K: ToStableHashKey<HCX>,
+ K: HashStable<HCX> + StableOrd,
{
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
- stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, key| {
- let key = key.to_stable_hash_key(hcx);
- key.hash_stable(hcx, hasher);
- });
+ self.len().hash_stable(hcx, hasher);
+ for entry in self.iter() {
+ entry.hash_stable(hcx, hasher);
+ }
}
}
}
}
- pub use std::iter::Iterator as ParallelIterator;
+ pub use Iterator as ParallelIterator;
pub fn par_iter<T: IntoIterator>(t: T) -> T::IntoIter {
t.into_iter()
#[inline(always)]
fn deref(&self) -> &T {
- &*self.0
+ &self.0
}
}
fn drop(&mut self) {
// No need to drop the tag, as it's Copy
unsafe {
- std::mem::drop(P::from_usize(self.raw.pointer_raw()));
+ drop(P::from_usize(self.raw.pointer_raw()));
}
}
}
use std::borrow::Borrow;
use std::fmt::Debug;
-use std::iter::FromIterator;
use std::slice::Iter;
use std::vec::IntoIter;
use rustc_errors::registry::{InvalidErrorCode, Registry};
use rustc_errors::{ErrorGuaranteed, PResult};
use rustc_feature::find_gated_cfg;
+use rustc_hir::def_id::LOCAL_CRATE;
use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
use rustc_interface::{interface, Queries};
use rustc_lint::LintStore;
use std::borrow::Cow;
use std::cmp::max;
-use std::default::Default;
use std::env;
use std::ffi::OsString;
use std::fs;
interface::run_compiler(config, |compiler| {
let sopts = &compiler.session().opts;
if sopts.describe_lints {
- let mut lint_store = rustc_lint::new_lint_store(
- sopts.unstable_opts.no_interleave_lints,
- compiler.session().enable_internal_lints(),
- );
+ let mut lint_store =
+ rustc_lint::new_lint_store(compiler.session().enable_internal_lints());
let registered_lints =
if let Some(register_lints) = compiler.register_lints() {
register_lints(compiler.session(), &mut lint_store);
queries.global_ctxt()?.peek_mut().enter(|tcx| {
let result = tcx.analysis(());
if sess.opts.unstable_opts.save_analysis {
- let crate_name = queries.crate_name()?.peek().clone();
+ let crate_name = tcx.crate_name(LOCAL_CRATE);
sess.time("save_analysis", || {
save::process_crate(
tcx,
- &crate_name,
+ crate_name,
compiler.input(),
None,
- DumpHandler::new(compiler.output_dir().as_deref(), &crate_name),
+ DumpHandler::new(compiler.output_dir().as_deref(), crate_name),
)
});
}
let crate_types = collect_crate_types(sess, attrs);
for &style in &crate_types {
let fname =
- rustc_session::output::filename_for_input(sess, style, &id, &t_outputs);
+ rustc_session::output::filename_for_input(sess, style, id, &t_outputs);
println!("{}", fname.file_name().unwrap().to_string_lossy());
}
}
E0787: include_str!("./error_codes/E0787.md"),
E0788: include_str!("./error_codes/E0788.md"),
E0790: include_str!("./error_codes/E0790.md"),
+E0791: include_str!("./error_codes/E0791.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
```
use std::cell::Cell;
-use std::marker::Sync;
struct NotThreadSafe<T> {
value: Cell<T>,
--- /dev/null
+Static variables with the `#[linkage]` attribute within external blocks
+must have one of the following types, which are equivalent to a nullable
+pointer in C:
+
+* `*mut T` or `*const T`, where `T` may be any type.
+
+* An enumerator type with no `#[repr]` attribute and with two variants, where
+ one of the variants has no fields, and the other has a single field of one of
+ the following non-nullable types:
+ * Reference type
+ * Function pointer type
+
+ The variants can appear in either order.
+
+For example, the following declaration is invalid:
+
+```compile_fail,E0791
+#![feature(linkage)]
+
+extern "C" {
+ #[linkage = "extern_weak"]
+ static foo: i8;
+}
+```
+
+The following declarations are valid:
+
+```
+#![feature(linkage)]
+
+extern "C" {
+ #[linkage = "extern_weak"]
+ static foo: Option<unsafe extern "C" fn()>;
+
+ #[linkage = "extern_weak"]
+ static bar: Option<&'static i8>;
+
+ #[linkage = "extern_weak"]
+ static baz: *mut i8;
+}
+```
-codegen_gcc_ranlib_failure =
- Ranlib exited with code {$exit_code}
-
-codegen_gcc_linkage_const_or_mut_type =
- must have type `*const T` or `*mut T` due to `#[linkage]` attribute
-
codegen_gcc_unwinding_inline_asm =
GCC backend does not support unwinding from inline asm
codegen_llvm_invalid_minimum_alignment =
invalid minimum global alignment: {$err}
-codegen_llvm_linkage_const_or_mut_type =
- must have type `*const T` or `*mut T` due to `#[linkage]` attribute
-
codegen_llvm_sanitizer_memtag_requires_mte =
`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`
-codegen_llvm_archive_build_failure =
- failed to build archive: {$error}
-
codegen_llvm_error_writing_def_file =
Error writing .DEF file: {$error}
codegen_llvm_dlltool_fail_import_library =
Dlltool could not create import library: {$stdout}\n{$stderr}
-codegen_llvm_unknown_archive_kind =
- Don't know how to build archive of type: {$kind}
-
codegen_llvm_target_feature_disable_or_enable =
the target features {$features} must all be either enabled or disabled together
codegen_ssa_read_file = failed to read file: {message}
codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target
+
+codegen_ssa_archive_build_failure =
+ failed to build archive: {$error}
+
+codegen_ssa_unknown_archive_kind =
+ Don't know how to build archive of type: {$kind}
variable '{$ident}' is still repeating at this depth
expand_meta_var_dif_seq_matchers = {$msg}
+
+expand_macro_const_stability =
+ macros cannot have const stability attributes
+ .label = invalid const stability attribute
+ .label2 = const stability attribute affects this macro
+
+expand_macro_body_stability =
+ macros cannot have body stability attributes
+ .label = invalid body stability attribute
+ .label2 = body stability attribute affects this macro
+
+expand_resolve_relative_path =
+ cannot resolve relative path in non-file source `{$path}`
+
+expand_attr_no_arguments =
+ attribute must have either one or two arguments
+
+expand_not_a_meta_item =
+ not a meta item
+
+expand_only_one_word =
+ must only be one word
+
+expand_cannot_be_name_of_macro =
+ `{$trait_ident}` cannot be a name of {$macro_type} macro
+
+expand_arg_not_attributes =
+ second argument must be `attributes`
+
+expand_attributes_wrong_form =
+ attribute must be of form: `attributes(foo, bar)`
+
+expand_attribute_meta_item =
+ attribute must be a meta item, not a literal
+
+expand_attribute_single_word =
+ attribute must only be a single word
+
+expand_helper_attribute_name_invalid =
+ `{$name}` cannot be a name of derive helper attribute
+
+expand_expected_comma_in_list =
+ expected token: `,`
+
+expand_only_one_argument =
+ {$name} takes 1 argument
+
+expand_takes_no_arguments =
+ {$name} takes no arguments
+
+expand_feature_included_in_edition =
+ the feature `{$feature}` is included in the Rust {$edition} edition
+
+expand_feature_removed =
+ feature has been removed
+ .label = feature has been removed
+ .reason = {$reason}
+
+expand_feature_not_allowed =
+ the feature `{$name}` is not in the list of allowed features
+
+expand_recursion_limit_reached =
+ recursion limit reached while expanding `{$descr}`
+ .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`)
+
+expand_malformed_feature_attribute =
+ malformed `feature` attribute input
+ .expected = expected just one word
+
+expand_remove_expr_not_supported =
+ removing an expression is not supported in this position
+
+expand_invalid_cfg_no_parens = `cfg` is not followed by parentheses
+expand_invalid_cfg_no_predicate = `cfg` predicate is not specified
+expand_invalid_cfg_multiple_predicates = multiple `cfg` predicates are specified
+expand_invalid_cfg_predicate_literal = `cfg` predicate key cannot be a literal
+expand_invalid_cfg_expected_syntax = expected syntax is
+
+expand_wrong_fragment_kind =
+ non-{$kind} macro in {$kind} position: {$name}
+
+expand_unsupported_key_value =
+ key-value macro attributes are not supported
+
+expand_incomplete_parse =
+ macro expansion ignores token `{$token}` and any following
+ .label = caused by the macro expansion here
+ .note = the usage of `{$macro_path}!` is likely invalid in {$kind_name} context
+ .suggestion_add_semi = you might be missing a semicolon here
+
+expand_remove_node_not_supported =
+ removing {$descr} is not supported in this position
+
+expand_module_circular =
+ circular modules: {$modules}
+
+expand_module_in_block =
+ cannot declare a non-inline module inside a block unless it has a path attribute
+ .note = maybe `use` the module `{$name}` instead of redeclaring it
+
+expand_module_file_not_found =
+ file not found for module `{$name}`
+ .help = to create the module `{$name}`, create file "{$default_path}" or "{$secondary_path}"
+
+expand_module_multiple_candidates =
+ file for module `{$name}` found at both "{$default_path}" and "{$secondary_path}"
+ .help = delete or rename one of them to remove the ambiguity
lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
.label = lifetimes do not match {$item_kind} in trait
.generics_label = lifetimes in impl do not match this {$item_kind} in trait
+ .where_label = this `where` clause might not match the one in the trait
+ .bounds_label = this bound might be missing in the impl
hir_analysis_drop_impl_on_wrong_item =
the `Drop` trait may only be implemented for local structs, enums, and unions
hir_analysis_self_in_impl_self =
`Self` is not valid in the self type of an impl block
.note = replace `Self` with a different type
+
+hir_analysis_linkage_type =
+ invalid type for variable with `#[linkage]` attribute
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
- &**self.fallback_bundle
+ &self.fallback_bundle
}
}
let fluent_args = to_fluent_args(diag.args());
let mut children = diag.children.clone();
- let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
+ let (mut primary_span, suggestions) = self.primary_span_formatted(diag, &fluent_args);
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
&mut primary_span,
&diag.code,
&primary_span,
&children,
- &suggestions,
+ suggestions,
);
}
let lint_index = expectation_id.get_lint_index();
expectation_id.set_lint_index(None);
let mut stable_id = unstable_to_stable
- .get(&expectation_id)
+ .get(expectation_id)
.expect("each unstable `LintExpectationId` must have a matching stable id")
.normalize();
i128,
u128,
std::io::Error,
- std::boxed::Box<dyn std::error::Error>,
+ Box<dyn std::error::Error>,
std::num::NonZeroU32,
hir::Target,
Edition,
}
}
+impl IntoDiagnosticArg for &ast::Path {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(self)))
+ }
+}
+
impl IntoDiagnosticArg for ast::token::Token {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(pprust::token_to_string(&self))
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::sync::Lrc;
-use rustc_error_messages::FluentArgs;
+use rustc_error_messages::{FluentArgs, SpanLabel};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use std::borrow::Cow;
use std::cmp::{max, min, Reverse};
if self
.source_map()
.map(|sm| is_case_difference(
- &**sm,
+ sm,
substitution,
sugg.substitutions[0].parts[0].span,
))
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
- &**self.fallback_bundle
+ &self.fallback_bundle
}
}
let fluent_args = to_fluent_args(diag.args());
let mut children = diag.children.clone();
- let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
+ let (mut primary_span, suggestions) = self.primary_span_formatted(diag, &fluent_args);
debug!("emit_diagnostic: suggestions={:?}", suggestions);
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
&diag.code,
&primary_span,
&children,
- &suggestions,
+ suggestions,
self.track_diagnostics.then_some(&diag.emitted_at),
);
}
draw_col_separator_no_space(buffer, line_offset, width_offset - 2);
}
+ #[instrument(level = "trace", skip(self), ret)]
fn render_source_line(
&self,
buffer: &mut StyledBuffer,
}
let source_string = match file.get_line(line.line_index - 1) {
- Some(s) => normalize_whitespace(&*s),
+ Some(s) => normalize_whitespace(&s),
None => return Vec::new(),
};
+ trace!(?source_string);
let line_offset = buffer.num_lines();
(pos + 2, annotation.start_col.saturating_sub(left))
};
if let Some(ref label) = annotation.label {
- buffer.puts(line_offset + pos, code_offset + col, &label, style);
+ buffer.puts(line_offset + pos, code_offset + col, label, style);
}
}
}
}
+ #[instrument(level = "trace", skip(self, args), ret)]
fn emit_message_default(
&mut self,
msp: &MultiSpan,
// only render error codes, not lint codes
if let Some(DiagnosticId::Error(ref code)) = *code {
buffer.append(0, "[", Style::Level(*level));
- buffer.append(0, &code, Style::Level(*level));
+ buffer.append(0, code, Style::Level(*level));
buffer.append(0, "]", Style::Level(*level));
label_width += 2 + code.len();
}
}
}
let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
+ trace!("{annotated_files:#?}");
// Make sure our primary file comes first
- let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) =
- (self.sm.as_ref(), msp.primary_span().as_ref())
- {
- if !primary_span.is_dummy() {
- (sm.lookup_char_pos(primary_span.lo()), sm)
- } else {
- emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
- return Ok(());
- }
- } else {
+ let primary_span = msp.primary_span().unwrap_or_default();
+ let (Some(sm), false) = (self.sm.as_ref(), primary_span.is_dummy()) else {
// If we don't have span information, emit and exit
- emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
- return Ok(());
+ return emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message);
};
+ let primary_lo = sm.lookup_char_pos(primary_span.lo());
if let Ok(pos) =
annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
{
for annotated_file in annotated_files {
// we can't annotate anything if the source is unavailable.
if !sm.ensure_source_file_source_present(annotated_file.file.clone()) {
+ if !self.short_message {
+ // We'll just print an unannotated message.
+ for (annotation_id, line) in annotated_file.lines.into_iter().enumerate() {
+ let mut annotations = line.annotations.clone();
+ annotations.sort_by_key(|a| Reverse(a.start_col));
+ let mut line_idx = buffer.num_lines();
+ buffer.append(
+ line_idx,
+ &format!(
+ "{}:{}:{}",
+ sm.filename_for_diagnostics(&annotated_file.file.name),
+ sm.doctest_offset_line(&annotated_file.file.name, line.line_index),
+ annotations[0].start_col + 1,
+ ),
+ Style::LineAndColumn,
+ );
+ if annotation_id == 0 {
+ buffer.prepend(line_idx, "--> ", Style::LineNumber);
+ for _ in 0..max_line_num_len {
+ buffer.prepend(line_idx, " ", Style::NoStyle);
+ }
+ line_idx += 1;
+ };
+ for (i, annotation) in annotations.into_iter().enumerate() {
+ if let Some(label) = &annotation.label {
+ let style = if annotation.is_primary {
+ Style::LabelPrimary
+ } else {
+ Style::LabelSecondary
+ };
+ if annotation_id == 0 {
+ buffer.prepend(line_idx, " |", Style::LineNumber);
+ for _ in 0..max_line_num_len {
+ buffer.prepend(line_idx, " ", Style::NoStyle);
+ }
+ line_idx += 1;
+ buffer.append(line_idx + i, " = note: ", style);
+ for _ in 0..max_line_num_len {
+ buffer.prepend(line_idx, " ", Style::NoStyle);
+ }
+ } else {
+ buffer.append(line_idx + i, ": ", style);
+ }
+ buffer.append(line_idx + i, label, style);
+ }
+ }
+ }
+ }
continue;
}
multilines.extend(&to_add);
}
}
+ trace!("buffer: {:#?}", buffer.render());
}
if let Some(tracked) = emitted_at {
};
// Render the replacements for each suggestion
- let suggestions = suggestion.splice_lines(&**sm);
+ let suggestions = suggestion.splice_lines(sm);
debug!("emit_suggestion_default: suggestions={:?}", suggestions);
if suggestions.is_empty() {
buffer.puts(
row_num - 1 + line - line_start,
max_line_num_len + 3,
- &normalize_whitespace(&*file_lines.file.get_line(line - 1).unwrap()),
+ &normalize_whitespace(&file_lines.file.get_line(line - 1).unwrap()),
Style::Removal,
);
}
buffer.putc(
row_num,
(padding as isize + p) as usize,
- if part.is_addition(&sm) { '+' } else { '~' },
+ if part.is_addition(sm) { '+' } else { '~' },
Style::Addition,
);
}
buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle);
} else if notice_capitalization {
let msg = "notice the capitalization difference";
- buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle);
+ buffer.puts(row_num, max_line_num_len + 3, msg, Style::NoStyle);
}
emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
Ok(())
}
+ #[instrument(level = "trace", skip(self, args, code, children, suggestions))]
fn emit_messages_default(
&mut self,
level: &Level,
for child in children {
let span = child.render_span.as_ref().unwrap_or(&child.span);
if let Err(err) = self.emit_message_default(
- &span,
+ span,
&child.message,
args,
&None,
*row_num - 1,
max_line_num_len + 3,
&normalize_whitespace(
- &*file_lines.file.get_line(file_lines.lines[line_pos].line_index).unwrap(),
+ &file_lines.file.get_line(file_lines.lines[line_pos].line_index).unwrap(),
),
Style::NoStyle,
);
let mut multiline_annotations = vec![];
if let Some(ref sm) = emitter.source_map() {
- for span_label in msp.span_labels() {
- let fixup_lo_hi = |span: Span| {
- let lo = sm.lookup_char_pos(span.lo());
- let mut hi = sm.lookup_char_pos(span.hi());
-
- // Watch out for "empty spans". If we get a span like 6..6, we
- // want to just display a `^` at 6, so convert that to
- // 6..7. This is degenerate input, but it's best to degrade
- // gracefully -- and the parser likes to supply a span like
- // that for EOF, in particular.
-
- if lo.col_display == hi.col_display && lo.line == hi.line {
- hi.col_display += 1;
- }
- (lo, hi)
+ for SpanLabel { span, is_primary, label } in msp.span_labels() {
+ // If we don't have a useful span, pick the primary span if that exists.
+ // Worst case we'll just print an error at the top of the main file.
+ let span = match (span.is_dummy(), msp.primary_span()) {
+ (_, None) | (false, _) => span,
+ (true, Some(span)) => span,
};
- if span_label.span.is_dummy() {
- if let Some(span) = msp.primary_span() {
- // if we don't know where to render the annotation, emit it as a note
- // on the primary span.
-
- let (lo, hi) = fixup_lo_hi(span);
-
- let ann = Annotation {
- start_col: lo.col_display,
- end_col: hi.col_display,
- is_primary: span_label.is_primary,
- label: span_label
- .label
- .as_ref()
- .map(|m| emitter.translate_message(m, args).to_string()),
- annotation_type: AnnotationType::Singleline,
- };
- add_annotation_to_file(&mut output, lo.file, lo.line, ann);
- }
- continue;
+ let lo = sm.lookup_char_pos(span.lo());
+ let mut hi = sm.lookup_char_pos(span.hi());
+
+ // Watch out for "empty spans". If we get a span like 6..6, we
+ // want to just display a `^` at 6, so convert that to
+ // 6..7. This is degenerate input, but it's best to degrade
+ // gracefully -- and the parser likes to supply a span like
+ // that for EOF, in particular.
+
+ if lo.col_display == hi.col_display && lo.line == hi.line {
+ hi.col_display += 1;
}
- let (lo, hi) = fixup_lo_hi(span_label.span);
+ let label = label.as_ref().map(|m| emitter.translate_message(m, args).to_string());
if lo.line != hi.line {
let ml = MultilineAnnotation {
line_end: hi.line,
start_col: lo.col_display,
end_col: hi.col_display,
- is_primary: span_label.is_primary,
- label: span_label
- .label
- .as_ref()
- .map(|m| emitter.translate_message(m, args).to_string()),
+ is_primary,
+ label,
overlaps_exactly: false,
};
multiline_annotations.push((lo.file, ml));
let ann = Annotation {
start_col: lo.col_display,
end_col: hi.col_display,
- is_primary: span_label.is_primary,
- label: span_label
- .label
- .as_ref()
- .map(|m| emitter.translate_message(m, args).to_string()),
+ is_primary,
+ label,
annotation_type: AnnotationType::Singleline,
};
add_annotation_to_file(&mut output, lo.file, lo.line, ann);
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
- &**self.fallback_bundle
+ &self.fallback_bundle
}
}
/// Maybe there was a typo where a comma was forgotten before
/// FRU syntax
MaybeFruTypo,
+ CallAssocMethod,
}
fn default_track_diagnostic(_: &Diagnostic) {}
diagnostic.children.drain_filter(already_emitted_sub).for_each(|_| {});
- self.emitter.emit_diagnostic(&diagnostic);
+ self.emitter.emit_diagnostic(diagnostic);
if diagnostic.is_error() {
self.deduplicated_err_count += 1;
} else if let Warning(_) = diagnostic.level {
trace!(?message, ?args);
let (identifier, attr) = match message {
DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
- return Cow::Borrowed(&msg);
+ return Cow::Borrowed(msg);
}
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
};
let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> {
- let message = bundle.get_message(&identifier)?;
+ let message = bundle.get_message(identifier)?;
let value = match attr {
Some(attr) => message.get_attribute(attr)?.value(),
None => message.value()?,
debug!(?message, ?value);
let mut errs = vec![];
- let translated = bundle.format_pattern(value, Some(&args), &mut errs);
+ let translated = bundle.format_pattern(value, Some(args), &mut errs);
debug!(?translated, ?errs);
Some((translated, errs))
};
+#![deny(rustc::untranslatable_diagnostic)]
+
+use crate::errors::{
+ ArgumentNotAttributes, AttrNoArguments, AttributeMetaItem, AttributeSingleWord,
+ AttributesWrongForm, CannotBeNameOfMacro, ExpectedCommaInList, HelperAttributeNameInvalid,
+ MacroBodyStability, MacroConstStability, NotAMetaItem, OnlyOneArgument, OnlyOneWord,
+ ResolveRelativePath, TakesNoArguments,
+};
use crate::expand::{self, AstFragment, Invocation};
use crate::module::DirOwnership;
use rustc_span::{BytePos, FileName, RealFileName, Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
-use std::default::Default;
use std::iter;
use std::path::PathBuf;
use std::rc::Rc;
.unwrap_or_else(|| (None, helper_attrs));
let (stability, const_stability, body_stability) = attr::find_stability(&sess, attrs, span);
if let Some((_, sp)) = const_stability {
- sess.parse_sess
- .span_diagnostic
- .struct_span_err(sp, "macros cannot have const stability attributes")
- .span_label(sp, "invalid const stability attribute")
- .span_label(
- sess.source_map().guess_head_span(span),
- "const stability attribute affects this macro",
- )
- .emit();
+ sess.emit_err(MacroConstStability {
+ span: sp,
+ head_span: sess.source_map().guess_head_span(span),
+ });
}
if let Some((_, sp)) = body_stability {
- sess.parse_sess
- .span_diagnostic
- .struct_span_err(sp, "macros cannot have body stability attributes")
- .span_label(sp, "invalid body stability attribute")
- .span_label(
- sess.source_map().guess_head_span(span),
- "body stability attribute affects this macro",
- )
- .emit();
+ sess.emit_err(MacroBodyStability {
+ span: sp,
+ head_span: sess.source_map().guess_head_span(span),
+ });
}
SyntaxExtension {
node_id: NodeId,
attrs: &[Attribute],
items: &[P<Item>],
- name: &str,
+ name: Symbol,
);
}
.expect("attempting to resolve a file path in an external file"),
FileName::DocTest(path, _) => path,
other => {
- return Err(parse_sess.span_diagnostic.struct_span_err(
+ return Err(ResolveRelativePath {
span,
- &format!(
- "cannot resolve relative path in non-file source `{}`",
- parse_sess.source_map().filename_for_diagnostics(&other)
- ),
- ));
+ path: parse_sess.source_map().filename_for_diagnostics(&other).to_string(),
+ }
+ .into_diagnostic(&parse_sess.span_diagnostic));
}
};
result.pop();
/// The returned bool indicates whether an applicable suggestion has already been
/// added to the diagnostic to avoid emitting multiple suggestions. `Err(None)`
/// indicates that an ast error was encountered.
+// FIXME(Nilstrieb) Make this function setup translatable
+#[allow(rustc::untranslatable_diagnostic)]
pub fn expr_to_spanned_string<'a>(
cx: &'a mut ExtCtxt<'_>,
expr: P<ast::Expr>,
/// compilation should call
/// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be
/// done as rarely as possible).
-pub fn check_zero_tts(cx: &ExtCtxt<'_>, sp: Span, tts: TokenStream, name: &str) {
+pub fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) {
if !tts.is_empty() {
- cx.span_err(sp, &format!("{} takes no arguments", name));
+ cx.emit_err(TakesNoArguments { span, name });
}
}
/// expect exactly one string literal, or emit an error and return `None`.
pub fn get_single_str_from_tts(
cx: &mut ExtCtxt<'_>,
- sp: Span,
+ span: Span,
tts: TokenStream,
name: &str,
) -> Option<Symbol> {
let mut p = cx.new_parser_from_tts(tts);
if p.token == token::Eof {
- cx.span_err(sp, &format!("{} takes 1 argument", name));
+ cx.emit_err(OnlyOneArgument { span, name });
return None;
}
let ret = parse_expr(&mut p)?;
let _ = p.eat(&token::Comma);
if p.token != token::Eof {
- cx.span_err(sp, &format!("{} takes 1 argument", name));
+ cx.emit_err(OnlyOneArgument { span, name });
}
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s)
}
/// Extracts comma-separated expressions from `tts`.
/// On error, emit it, and return `None`.
-pub fn get_exprs_from_tts(
- cx: &mut ExtCtxt<'_>,
- sp: Span,
- tts: TokenStream,
-) -> Option<Vec<P<ast::Expr>>> {
+pub fn get_exprs_from_tts(cx: &mut ExtCtxt<'_>, tts: TokenStream) -> Option<Vec<P<ast::Expr>>> {
let mut p = cx.new_parser_from_tts(tts);
let mut es = Vec::new();
while p.token != token::Eof {
continue;
}
if p.token != token::Eof {
- cx.span_err(sp, "expected token: `,`");
+ cx.emit_err(ExpectedCommaInList { span: p.token.span });
return None;
}
}
pub fn parse_macro_name_and_helper_attrs(
diag: &rustc_errors::Handler,
attr: &Attribute,
- descr: &str,
+ macro_type: &str,
) -> Option<(Symbol, Vec<Symbol>)> {
// Once we've located the `#[proc_macro_derive]` attribute, verify
// that it's of the form `#[proc_macro_derive(Foo)]` or
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
let list = attr.meta_item_list()?;
if list.len() != 1 && list.len() != 2 {
- diag.span_err(attr.span, "attribute must have either one or two arguments");
+ diag.emit_err(AttrNoArguments { span: attr.span });
return None;
}
let Some(trait_attr) = list[0].meta_item() else {
- diag.span_err(list[0].span(), "not a meta item");
+ diag.emit_err(NotAMetaItem {span: list[0].span()});
return None;
};
let trait_ident = match trait_attr.ident() {
Some(trait_ident) if trait_attr.is_word() => trait_ident,
_ => {
- diag.span_err(trait_attr.span, "must only be one word");
+ diag.emit_err(OnlyOneWord { span: trait_attr.span });
return None;
}
};
if !trait_ident.name.can_be_raw() {
- diag.span_err(
- trait_attr.span,
- &format!("`{}` cannot be a name of {} macro", trait_ident, descr),
- );
+ diag.emit_err(CannotBeNameOfMacro { span: trait_attr.span, trait_ident, macro_type });
}
let attributes_attr = list.get(1);
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
if !attr.has_name(sym::attributes) {
- diag.span_err(attr.span(), "second argument must be `attributes`");
+ diag.emit_err(ArgumentNotAttributes { span: attr.span() });
}
attr.meta_item_list()
.unwrap_or_else(|| {
- diag.span_err(attr.span(), "attribute must be of form: `attributes(foo, bar)`");
+ diag.emit_err(AttributesWrongForm { span: attr.span() });
&[]
})
.iter()
.filter_map(|attr| {
let Some(attr) = attr.meta_item() else {
- diag.span_err(attr.span(), "not a meta item");
+ diag.emit_err(AttributeMetaItem { span: attr.span() });
return None;
};
let ident = match attr.ident() {
Some(ident) if attr.is_word() => ident,
_ => {
- diag.span_err(attr.span, "must only be one word");
+ diag.emit_err(AttributeSingleWord { span: attr.span });
return None;
}
};
if !ident.name.can_be_raw() {
- diag.span_err(
- attr.span,
- &format!("`{}` cannot be a name of derive helper attribute", ident),
- );
+ diag.emit_err(HelperAttributeNameInvalid { span: attr.span, name: ident });
}
Some(ident.name)
fn_decl,
body,
fn_decl_span: span,
+ // FIXME(SarthakSingh31): This points to the start of the declaration block and
+ // not the span of the argument block.
+ fn_arg_span: span,
})),
)
}
//! Conditional compilation stripping.
+use crate::errors::{
+ FeatureIncludedInEdition, FeatureNotAllowed, FeatureRemoved, FeatureRemovedReason, InvalidCfg,
+ MalformedFeatureAttribute, MalformedFeatureAttributeHelp, RemoveExprNotSupported,
+};
use rustc_ast::ptr::P;
use rustc_ast::token::{Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree};
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::map_in_place::MapInPlace;
-use rustc_errors::{error_code, struct_span_err, Applicability, Handler};
use rustc_feature::{Feature, Features, State as FeatureState};
use rustc_feature::{
ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
pub lint_node_id: NodeId,
}
-fn get_features(
- sess: &Session,
- span_handler: &Handler,
- krate_attrs: &[ast::Attribute],
-) -> Features {
- fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
- let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed");
- err.span_label(span, "feature has been removed");
- if let Some(reason) = reason {
- err.note(reason);
- }
- err.emit();
+fn get_features(sess: &Session, krate_attrs: &[ast::Attribute]) -> Features {
+ fn feature_removed(sess: &Session, span: Span, reason: Option<&str>) {
+ sess.emit_err(FeatureRemoved {
+ span,
+ reason: reason.map(|reason| FeatureRemovedReason { reason }),
+ });
}
fn active_features_up_to(edition: Edition) -> impl Iterator<Item = &'static Feature> {
continue;
};
- let bad_input = |span| {
- struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input")
- };
-
for mi in list {
let name = match mi.ident() {
Some(ident) if mi.is_word() => ident.name,
Some(ident) => {
- bad_input(mi.span())
- .span_suggestion(
- mi.span(),
- "expected just one word",
- ident.name,
- Applicability::MaybeIncorrect,
- )
- .emit();
+ sess.emit_err(MalformedFeatureAttribute {
+ span: mi.span(),
+ help: MalformedFeatureAttributeHelp::Suggestion {
+ span: mi.span(),
+ suggestion: ident.name,
+ },
+ });
continue;
}
None => {
- bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit();
+ sess.emit_err(MalformedFeatureAttribute {
+ span: mi.span(),
+ help: MalformedFeatureAttributeHelp::Label { span: mi.span() },
+ });
continue;
}
};
- if let Some(edition) = edition_enabled_features.get(&name) {
- let msg =
- &format!("the feature `{}` is included in the Rust {} edition", name, edition);
- span_handler.struct_span_warn_with_code(mi.span(), msg, error_code!(E0705)).emit();
+ if let Some(&edition) = edition_enabled_features.get(&name) {
+ sess.emit_warning(FeatureIncludedInEdition {
+ span: mi.span(),
+ feature: name,
+ edition,
+ });
continue;
}
if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } =
state
{
- feature_removed(span_handler, mi.span(), *reason);
+ feature_removed(sess, mi.span(), *reason);
continue;
}
}
if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() {
if allowed.iter().all(|f| name.as_str() != f) {
- struct_span_err!(
- span_handler,
- mi.span(),
- E0725,
- "the feature `{}` is not in the list of allowed features",
- name
- )
- .emit();
+ sess.emit_err(FeatureNotAllowed { span: mi.span(), name });
continue;
}
}
}
Some(attrs) => {
krate.attrs = attrs;
- let features = get_features(sess, diag, &krate.attrs);
+ let features = get_features(sess, &krate.attrs);
if err_count == diag.err_count() {
// Avoid reconfiguring malformed `cfg_attr`s.
strip_unconfigured.features = Some(&features);
// N.B., this is intentionally not part of the visit_expr() function
// in order for filter_map_expr() to be able to avoid this check
if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(*a)) {
- let msg = "removing an expression is not supported in this position";
- self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg);
+ self.sess.emit_err(RemoveExprNotSupported { span: attr.span });
}
self.process_cfg_attrs(expr);
}
pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItem> {
- let error = |span, msg, suggestion: &str| {
- let mut err = sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
- if !suggestion.is_empty() {
- err.span_suggestion(
- span,
- "expected syntax is",
- suggestion,
- Applicability::HasPlaceholders,
- );
- }
- err.emit();
- None
- };
let span = meta_item.span;
match meta_item.meta_item_list() {
- None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
- Some([]) => error(span, "`cfg` predicate is not specified", ""),
- Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
+ None => {
+ sess.emit_err(InvalidCfg::NotFollowedByParens { span });
+ None
+ }
+ Some([]) => {
+ sess.emit_err(InvalidCfg::NoPredicate { span });
+ None
+ }
+ Some([_, .., l]) => {
+ sess.emit_err(InvalidCfg::MultiplePredicates { span: l.span() });
+ None
+ }
Some([single]) => match single.meta_item() {
Some(meta_item) => Some(meta_item),
- None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
+ None => {
+ sess.emit_err(InvalidCfg::PredicateLiteral { span: single.span() });
+ None
+ }
},
}
}
+use rustc_ast::ast;
use rustc_macros::Diagnostic;
-use rustc_span::symbol::MacroRulesNormalizedIdent;
-use rustc_span::Span;
+use rustc_session::Limit;
+use rustc_span::edition::Edition;
+use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent};
+use rustc_span::{Span, Symbol};
+use std::borrow::Cow;
#[derive(Diagnostic)]
#[diag(expand_expr_repeat_no_syntax_vars)]
pub span: Span,
pub msg: String,
}
+
+#[derive(Diagnostic)]
+#[diag(expand_resolve_relative_path)]
+pub(crate) struct ResolveRelativePath {
+ #[primary_span]
+ pub span: Span,
+ pub path: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_macro_const_stability)]
+pub(crate) struct MacroConstStability {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[label(label2)]
+ pub head_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_macro_body_stability)]
+pub(crate) struct MacroBodyStability {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[label(label2)]
+ pub head_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_attr_no_arguments)]
+pub(crate) struct AttrNoArguments {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_not_a_meta_item)]
+pub(crate) struct NotAMetaItem {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_only_one_word)]
+pub(crate) struct OnlyOneWord {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_cannot_be_name_of_macro)]
+pub(crate) struct CannotBeNameOfMacro<'a> {
+ #[primary_span]
+ pub span: Span,
+ pub trait_ident: Ident,
+ pub macro_type: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_arg_not_attributes)]
+pub(crate) struct ArgumentNotAttributes {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_attributes_wrong_form)]
+pub(crate) struct AttributesWrongForm {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_attribute_meta_item)]
+pub(crate) struct AttributeMetaItem {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_attribute_single_word)]
+pub(crate) struct AttributeSingleWord {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_helper_attribute_name_invalid)]
+pub(crate) struct HelperAttributeNameInvalid {
+ #[primary_span]
+ pub span: Span,
+ pub name: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_expected_comma_in_list)]
+pub(crate) struct ExpectedCommaInList {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_only_one_argument)]
+pub(crate) struct OnlyOneArgument<'a> {
+ #[primary_span]
+ pub span: Span,
+ pub name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_takes_no_arguments)]
+pub(crate) struct TakesNoArguments<'a> {
+ #[primary_span]
+ pub span: Span,
+ pub name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_feature_included_in_edition, code = "E0705")]
+pub(crate) struct FeatureIncludedInEdition {
+ #[primary_span]
+ pub span: Span,
+ pub feature: Symbol,
+ pub edition: Edition,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_feature_removed, code = "E0557")]
+pub(crate) struct FeatureRemoved<'a> {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[subdiagnostic]
+ pub reason: Option<FeatureRemovedReason<'a>>,
+}
+
+#[derive(Subdiagnostic)]
+#[note(reason)]
+pub(crate) struct FeatureRemovedReason<'a> {
+ pub reason: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_feature_not_allowed, code = "E0725")]
+pub(crate) struct FeatureNotAllowed {
+ #[primary_span]
+ pub span: Span,
+ pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_recursion_limit_reached)]
+#[help]
+pub(crate) struct RecursionLimitReached<'a> {
+ #[primary_span]
+ pub span: Span,
+ pub descr: String,
+ pub suggested_limit: Limit,
+ pub crate_name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_malformed_feature_attribute, code = "E0556")]
+pub(crate) struct MalformedFeatureAttribute {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub help: MalformedFeatureAttributeHelp,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum MalformedFeatureAttributeHelp {
+ #[label(expected)]
+ Label {
+ #[primary_span]
+ span: Span,
+ },
+ #[suggestion(expected, code = "{suggestion}", applicability = "maybe-incorrect")]
+ Suggestion {
+ #[primary_span]
+ span: Span,
+ suggestion: Symbol,
+ },
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_remove_expr_not_supported)]
+pub(crate) struct RemoveExprNotSupported {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+pub(crate) enum InvalidCfg {
+ #[diag(expand_invalid_cfg_no_parens)]
+ NotFollowedByParens {
+ #[primary_span]
+ #[suggestion(
+ expand_invalid_cfg_expected_syntax,
+ code = "cfg(/* predicate */)",
+ applicability = "has-placeholders"
+ )]
+ span: Span,
+ },
+ #[diag(expand_invalid_cfg_no_predicate)]
+ NoPredicate {
+ #[primary_span]
+ #[suggestion(
+ expand_invalid_cfg_expected_syntax,
+ code = "cfg(/* predicate */)",
+ applicability = "has-placeholders"
+ )]
+ span: Span,
+ },
+ #[diag(expand_invalid_cfg_multiple_predicates)]
+ MultiplePredicates {
+ #[primary_span]
+ span: Span,
+ },
+ #[diag(expand_invalid_cfg_predicate_literal)]
+ PredicateLiteral {
+ #[primary_span]
+ span: Span,
+ },
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_wrong_fragment_kind)]
+pub(crate) struct WrongFragmentKind<'a> {
+ #[primary_span]
+ pub span: Span,
+ pub kind: &'a str,
+ pub name: &'a ast::Path,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_unsupported_key_value)]
+pub(crate) struct UnsupportedKeyValue {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_incomplete_parse)]
+#[note]
+pub(crate) struct IncompleteParse<'a> {
+ #[primary_span]
+ pub span: Span,
+ pub token: Cow<'a, str>,
+ #[label]
+ pub label_span: Span,
+ pub macro_path: &'a ast::Path,
+ pub kind_name: &'a str,
+
+ #[suggestion(
+ suggestion_add_semi,
+ style = "verbose",
+ code = ";",
+ applicability = "maybe-incorrect"
+ )]
+ pub add_semicolon: Option<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_remove_node_not_supported)]
+pub(crate) struct RemoveNodeNotSupported {
+ #[primary_span]
+ pub span: Span,
+ pub descr: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_module_circular)]
+pub(crate) struct ModuleCircular {
+ #[primary_span]
+ pub span: Span,
+ pub modules: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_module_in_block)]
+pub(crate) struct ModuleInBlock {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub name: Option<ModuleInBlockName>,
+}
+
+#[derive(Subdiagnostic)]
+#[note(note)]
+pub(crate) struct ModuleInBlockName {
+ #[primary_span]
+ pub span: Span,
+ pub name: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_module_file_not_found, code = "E0583")]
+#[help]
+pub(crate) struct ModuleFileNotFound {
+ #[primary_span]
+ pub span: Span,
+ pub name: Ident,
+ pub default_path: String,
+ pub secondary_path: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_module_multiple_candidates, code = "E0761")]
+#[help]
+pub(crate) struct ModuleMultipleCandidates {
+ #[primary_span]
+ pub span: Span,
+ pub name: Ident,
+ pub default_path: String,
+ pub secondary_path: String,
+}
use crate::base::*;
use crate::config::StripUnconfigured;
+use crate::errors::{
+ IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported,
+ UnsupportedKeyValue, WrongFragmentKind,
+};
use crate::hygiene::SyntaxContext;
use crate::mbe::diagnostics::annotate_err_with_kind;
use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
use rustc_ast_pretty::pprust;
use rustc_data_structures::map_in_place::MapInPlace;
use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, PResult};
+use rustc_errors::PResult;
use rustc_feature::Features;
use rustc_parse::parser::{
AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
Limit(0) => Limit(2),
limit => limit * 2,
};
- self.cx
- .struct_span_err(
- expn_data.call_site,
- &format!("recursion limit reached while expanding `{}`", expn_data.kind.descr()),
- )
- .help(&format!(
- "consider increasing the recursion limit by adding a \
- `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
- suggested_limit, self.cx.ecfg.crate_name,
- ))
- .emit();
+
+ self.cx.emit_err(RecursionLimitReached {
+ span: expn_data.call_site,
+ descr: expn_data.kind.descr(),
+ suggested_limit,
+ crate_name: &self.cx.ecfg.crate_name,
+ });
+
self.cx.trace_macros_diag();
}
/// A macro's expansion does not fit in this fragment kind.
/// For example, a non-type macro in a type position.
fn error_wrong_fragment_kind(&mut self, kind: AstFragmentKind, mac: &ast::MacCall, span: Span) {
- let msg = format!(
- "non-{kind} macro in {kind} position: {path}",
- kind = kind.name(),
- path = pprust::path_to_string(&mac.path),
- );
- self.cx.span_err(span, &msg);
+ self.cx.emit_err(WrongFragmentKind { span, kind: kind.name(), name: &mac.path });
+
self.cx.trace_macros_diag();
}
};
let attr_item = attr.unwrap_normal_item();
if let AttrArgs::Eq(..) = attr_item.args {
- self.cx.span_err(span, "key-value macro attributes are not supported");
+ self.cx.emit_err(UnsupportedKeyValue { span });
}
let inner_tokens = attr_item.args.inner_tokens();
let Ok(tok_result) = expander.expand(self.cx, span, inner_tokens, tokens) else {
}
};
if fragment_kind == AstFragmentKind::Expr && items.is_empty() {
- let msg =
- "removing an expression is not supported in this position";
- self.cx.span_err(span, msg);
+ self.cx.emit_err(RemoveExprNotSupported { span });
fragment_kind.dummy(span)
} else {
fragment_kind.expect_from_annotatables(items)
}
pub fn ensure_complete_parse<'a>(
- this: &mut Parser<'a>,
+ parser: &mut Parser<'a>,
macro_path: &ast::Path,
kind_name: &str,
span: Span,
) {
- if this.token != token::Eof {
- let token = pprust::token_to_string(&this.token);
- let msg = format!("macro expansion ignores token `{}` and any following", token);
+ if parser.token != token::Eof {
+ let token = pprust::token_to_string(&parser.token);
// Avoid emitting backtrace info twice.
- let def_site_span = this.token.span.with_ctxt(SyntaxContext::root());
- let mut err = this.struct_span_err(def_site_span, &msg);
- err.span_label(span, "caused by the macro expansion here");
- let msg = format!(
- "the usage of `{}!` is likely invalid in {} context",
- pprust::path_to_string(macro_path),
- kind_name,
- );
- err.note(&msg);
+ let def_site_span = parser.token.span.with_ctxt(SyntaxContext::root());
- let semi_span = this.sess.source_map().next_point(span);
- match this.sess.source_map().span_to_snippet(semi_span) {
+ let semi_span = parser.sess.source_map().next_point(span);
+ let add_semicolon = match parser.sess.source_map().span_to_snippet(semi_span) {
Ok(ref snippet) if &snippet[..] != ";" && kind_name == "expression" => {
- err.span_suggestion(
- span.shrink_to_hi(),
- "you might be missing a semicolon here",
- ";",
- Applicability::MaybeIncorrect,
- );
+ Some(span.shrink_to_hi())
}
- _ => {}
- }
- err.emit();
+ _ => None,
+ };
+
+ parser.sess.emit_err(IncompleteParse {
+ span: def_site_span,
+ token,
+ label_span: span,
+ macro_path,
+ kind_name,
+ add_semicolon,
+ });
}
}
ecx.current_expansion.lint_node_id,
&attrs,
&items,
- ident.name.as_str(),
+ ident.name,
);
}
if self.expand_cfg_true(node, attr, pos) {
continue;
}
- let msg =
- format!("removing {} is not supported in this position", Node::descr());
- self.cx.span_err(span, &msg);
+
+ self.cx.emit_err(RemoveNodeNotSupported { span, descr: Node::descr() });
continue;
}
sym::cfg_attr => {
#![feature(rustc_attrs)]
#![feature(try_blocks)]
#![recursion_limit = "256"]
+#![deny(rustc::untranslatable_diagnostic)]
#[macro_use]
extern crate rustc_macros;
pub mod errors;
pub mod expand;
pub mod module;
+
+// FIXME(Nilstrieb) Translate proc_macro diagnostics
+#[allow(rustc::untranslatable_diagnostic)]
pub mod proc_macro;
+// FIXME(Nilstrieb) Translate macro_rules diagnostics
+#[allow(rustc::untranslatable_diagnostic)]
pub(crate) mod mbe;
// HACK(Centril, #64197): These shouldn't really be here.
use crate::base::ModuleData;
+use crate::errors::{
+ ModuleCircular, ModuleFileNotFound, ModuleInBlock, ModuleInBlockName, ModuleMultipleCandidates,
+};
use rustc_ast::ptr::P;
use rustc_ast::{token, AttrVec, Attribute, Inline, Item, ModSpans};
-use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
use rustc_parse::new_parser_from_file;
use rustc_parse::validate_attr;
use rustc_session::parse::ParseSess;
use rustc_session::Session;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
+use std::iter::once;
use std::path::{self, Path, PathBuf};
impl ModError<'_> {
fn report(self, sess: &Session, span: Span) -> ErrorGuaranteed {
- let diag = &sess.parse_sess.span_diagnostic;
match self {
ModError::CircularInclusion(file_paths) => {
- let mut msg = String::from("circular modules: ");
- for file_path in &file_paths {
- msg.push_str(&file_path.display().to_string());
- msg.push_str(" -> ");
- }
- msg.push_str(&file_paths[0].display().to_string());
- diag.struct_span_err(span, &msg)
- }
- ModError::ModInBlock(ident) => {
- let msg = "cannot declare a non-inline module inside a block unless it has a path attribute";
- let mut err = diag.struct_span_err(span, msg);
- if let Some(ident) = ident {
- let note =
- format!("maybe `use` the module `{}` instead of redeclaring it", ident);
- err.span_note(span, ¬e);
- }
- err
+ let path_to_string = |path: &PathBuf| path.display().to_string();
+
+ let paths = file_paths
+ .iter()
+ .map(path_to_string)
+ .chain(once(path_to_string(&file_paths[0])))
+ .collect::<Vec<_>>();
+
+ let modules = paths.join(" -> ");
+
+ sess.emit_err(ModuleCircular { span, modules })
}
- ModError::FileNotFound(ident, default_path, secondary_path) => {
- let mut err = struct_span_err!(
- diag,
+ ModError::ModInBlock(ident) => sess.emit_err(ModuleInBlock {
+ span,
+ name: ident.map(|name| ModuleInBlockName { span, name }),
+ }),
+ ModError::FileNotFound(name, default_path, secondary_path) => {
+ sess.emit_err(ModuleFileNotFound {
span,
- E0583,
- "file not found for module `{}`",
- ident,
- );
- err.help(&format!(
- "to create the module `{}`, create file \"{}\" or \"{}\"",
- ident,
- default_path.display(),
- secondary_path.display(),
- ));
- err
+ name,
+ default_path: default_path.display().to_string(),
+ secondary_path: secondary_path.display().to_string(),
+ })
}
- ModError::MultipleCandidates(ident, default_path, secondary_path) => {
- let mut err = struct_span_err!(
- diag,
+ ModError::MultipleCandidates(name, default_path, secondary_path) => {
+ sess.emit_err(ModuleMultipleCandidates {
span,
- E0761,
- "file for module `{}` found at both \"{}\" and \"{}\"",
- ident,
- default_path.display(),
- secondary_path.display(),
- );
- err.help("delete or rename one of them to remove the ambiguity");
- err
+ name,
+ default_path: default_path.display().to_string(),
+ secondary_path: secondary_path.display().to_string(),
+ })
}
- ModError::ParserError(err) => err,
- }.emit()
+ ModError::ParserError(mut err) => err.emit(),
+ }
}
}
false,
);
let handler = Handler::with_emitter(true, None, Box::new(emitter));
+ #[allow(rustc::untranslatable_diagnostic)]
handler.span_err(msp, "foo");
assert!(
ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
gated!(
no_sanitize, Normal,
- template!(List: "address, memory, thread"), DuplicatesOk,
+ template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
experimental!(no_sanitize)
),
gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
pub fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<LinkOrCopy> {
let p = p.as_ref();
let q = q.as_ref();
- match fs::remove_file(&q) {
+ match fs::remove_file(q) {
Ok(()) => (),
Err(err) if err.kind() == io::ErrorKind::NotFound => (),
Err(err) => return Err(err),
}
pub fn as_slice(&'a self) -> &'a str {
- &*self.name
+ &self.name
}
}
pub fn to_dot_string(&self) -> String {
match *self {
LabelStr(ref s) => format!("\"{}\"", s.escape_default()),
- EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s)),
+ EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(s)),
HtmlStr(ref s) => format!("<{}>", s),
}
}
EscStr(s) => s,
LabelStr(s) => {
if s.contains('\\') {
- (&*s).escape_default().to_string().into()
+ s.escape_default().to_string().into()
} else {
s
}
[] param: rustc_hir::Param<'tcx>,
[] pat: rustc_hir::Pat<'tcx>,
[] path: rustc_hir::Path<'tcx>,
+ [] use_path: rustc_hir::UsePath<'tcx>,
[] path_segment: rustc_hir::PathSegment<'tcx>,
[] poly_trait_ref: rustc_hir::PolyTraitRef<'tcx>,
[] qpath: rustc_hir::QPath<'tcx>,
/// `std::cmp::PartialEq`. It's represented as a sequence of identifiers,
/// along with a bunch of supporting information.
#[derive(Debug, HashStable_Generic)]
-pub struct Path<'hir> {
+pub struct Path<'hir, R = Res> {
pub span: Span,
/// The resolution for the path.
- pub res: Res,
+ pub res: R,
/// The segments in the path: the things separated by `::`.
pub segments: &'hir [PathSegment<'hir>],
}
+/// Up to three resolutions for type, value and macro namespaces.
+pub type UsePath<'hir> = Path<'hir, SmallVec<[Res; 3]>>;
+
impl Path<'_> {
pub fn is_global(&self) -> bool {
!self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot
pub struct OwnerNodes<'tcx> {
/// Pre-computed hash of the full HIR.
pub hash_including_bodies: Fingerprint,
- /// Pre-computed hash of the item signature, sithout recursing into the body.
+ /// Pre-computed hash of the item signature, without recursing into the body.
pub hash_without_bodies: Fingerprint,
/// Full HIR for the current owner.
// The zeroth node's parent should never be accessed: the owner's parent is computed by the
pub bound_generic_params: &'hir [GenericParam<'hir>],
pub fn_decl: &'hir FnDecl<'hir>,
pub body: BodyId,
+ /// The span of the declaration block: 'move |...| -> ...'
pub fn_decl_span: Span,
+ /// The span of the argument block `|...|`
+ pub fn_arg_span: Option<Span>,
pub movability: Option<Movability>,
}
pub fn peel_refs(&self) -> &Self {
let mut final_ty = self;
while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind {
- final_ty = &ty;
+ final_ty = ty;
}
final_ty
}
/// or just
///
/// `use foo::bar::baz;` (with `as baz` implicitly on the right).
- Use(&'hir Path<'hir>, UseKind),
+ Use(&'hir UsePath<'hir>, UseKind),
/// A `static` item.
Static(&'hir Ty<'hir>, Mutability, BodyId),
use crate::def_id::{DefId, DefIndex, LocalDefId, CRATE_DEF_ID};
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey};
use rustc_span::{def_id::DefPathHash, HashStableContext};
use std::fmt;
impl PartialOrd for HirId {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
- Some(self.cmp(&other))
+ Some(self.cmp(other))
}
}
pub const INVALID: ItemLocalId = ItemLocalId::MAX;
}
+// Safety: Ord is implement as just comparing the LocalItemId's numerical
+// values and these are not changed by (de-)serialization.
+unsafe impl StableOrd for ItemLocalId {}
+
/// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`.
pub const CRATE_HIR_ID: HirId =
HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::from_u32(0) };
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl<'v>, b: BodyId, _: Span, id: HirId) {
walk_fn(self, fk, fd, b, id)
}
- fn visit_use(&mut self, path: &'v Path<'v>, hir_id: HirId) {
+ fn visit_use(&mut self, path: &'v UsePath<'v>, hir_id: HirId) {
walk_use(self, path, hir_id)
}
fn visit_trait_item(&mut self, ti: &'v TraitItem<'v>) {
fn visit_qpath(&mut self, qpath: &'v QPath<'v>, id: HirId, _span: Span) {
walk_qpath(self, qpath, id)
}
- fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) {
+ fn visit_path(&mut self, path: &Path<'v>, _id: HirId) {
walk_path(self, path)
}
fn visit_path_segment(&mut self, path_segment: &'v PathSegment<'v>) {
pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) {
visitor.visit_id(param.hir_id);
- visitor.visit_pat(¶m.pat);
+ visitor.visit_pat(param.pat);
}
pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
}
ItemKind::Fn(ref sig, ref generics, body_id) => visitor.visit_fn(
FnKind::ItemFn(item.ident, generics, sig.header),
- &sig.decl,
+ sig.decl,
body_id,
item.span,
item.hir_id(),
pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body<'v>) {
walk_list!(visitor, visit_param, body.params);
- visitor.visit_expr(&body.value);
+ visitor.visit_expr(body.value);
}
pub fn walk_ident<'v, V: Visitor<'v>>(visitor: &mut V, ident: Ident) {
// dominates the local's definition.
walk_list!(visitor, visit_expr, &local.init);
visitor.visit_id(local.hir_id);
- visitor.visit_pat(&local.pat);
+ visitor.visit_pat(local.pat);
if let Some(els) = local.els {
visitor.visit_block(els);
}
pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) {
visitor.visit_id(arm.hir_id);
- visitor.visit_pat(&arm.pat);
+ visitor.visit_pat(arm.pat);
if let Some(ref g) = arm.guard {
match g {
Guard::If(ref e) => visitor.visit_expr(e),
}
}
}
- visitor.visit_expr(&arm.body);
+ visitor.visit_expr(arm.body);
}
pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) {
pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<'v>) {
visitor.visit_id(field.hir_id);
visitor.visit_ident(field.ident);
- visitor.visit_pat(&field.pat)
+ visitor.visit_pat(field.pat)
}
pub fn walk_array_len<'v, V: Visitor<'v>>(visitor: &mut V, len: &'v ArrayLen) {
body,
capture_clause: _,
fn_decl_span: _,
+ fn_arg_span: _,
movability: _,
}) => {
walk_list!(visitor, visit_generic_param, bound_generic_params);
pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField<'v>) {
visitor.visit_id(field.hir_id);
visitor.visit_ident(field.ident);
- visitor.visit_expr(&field.expr)
+ visitor.visit_expr(field.expr)
}
pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) {
match typ.kind {
TyKind::Slice(ref ty) => visitor.visit_ty(ty),
- TyKind::Ptr(ref mutable_type) => visitor.visit_ty(&mutable_type.ty),
+ TyKind::Ptr(ref mutable_type) => visitor.visit_ty(mutable_type.ty),
TyKind::Rptr(ref lifetime, ref mutable_type) => {
visitor.visit_lifetime(lifetime);
- visitor.visit_ty(&mutable_type.ty)
+ visitor.visit_ty(mutable_type.ty)
}
TyKind::Never => {}
TyKind::Tup(tuple_element_types) => {
}
TyKind::BareFn(ref function_declaration) => {
walk_list!(visitor, visit_generic_param, function_declaration.generic_params);
- visitor.visit_fn_decl(&function_declaration.decl);
+ visitor.visit_fn_decl(function_declaration.decl);
}
TyKind::Path(ref qpath) => {
visitor.visit_qpath(qpath, typ.hir_id, typ.span);
}
}
-pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>, hir_id: HirId) {
+pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v UsePath<'v>, hir_id: HirId) {
visitor.visit_id(hir_id);
- visitor.visit_path(path, hir_id);
+ let UsePath { segments, ref res, span } = *path;
+ for &res in res {
+ visitor.visit_path(&Path { segments, res, span }, hir_id);
+ }
}
pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v TraitItem<'v>) {
let TraitItem { ident, generics, ref defaultness, ref kind, span, owner_id: _ } = *trait_item;
let hir_id = trait_item.hir_id();
visitor.visit_ident(ident);
- visitor.visit_generics(&generics);
- visitor.visit_defaultness(&defaultness);
+ visitor.visit_generics(generics);
+ visitor.visit_defaultness(defaultness);
match *kind {
TraitItemKind::Const(ref ty, default) => {
visitor.visit_id(hir_id);
}
TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => {
visitor.visit_id(hir_id);
- visitor.visit_fn_decl(&sig.decl);
+ visitor.visit_fn_decl(sig.decl);
for ¶m_name in param_names {
visitor.visit_ident(param_name);
}
}
TraitItemKind::Fn(ref sig, TraitFn::Provided(body_id)) => {
- visitor.visit_fn(FnKind::Method(ident, sig), &sig.decl, body_id, span, hir_id);
+ visitor.visit_fn(FnKind::Method(ident, sig), sig.decl, body_id, span, hir_id);
}
TraitItemKind::Type(bounds, ref default) => {
visitor.visit_id(hir_id);
ImplItemKind::Fn(ref sig, body_id) => {
visitor.visit_fn(
FnKind::Method(impl_item.ident, sig),
- &sig.decl,
+ sig.decl,
body_id,
impl_item.span,
impl_item.hir_id(),
pub fn walk_trait_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_ref: &'v TraitRef<'v>) {
visitor.visit_id(trait_ref.hir_ref_id);
- visitor.visit_path(&trait_ref.path, trait_ref.hir_ref_id)
+ visitor.visit_path(trait_ref.path, trait_ref.hir_ref_id)
}
pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericBound<'v>) {
pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) {
visitor.visit_id(field.hir_id);
visitor.visit_ident(field.ident);
- visitor.visit_ty(&field.ty);
+ visitor.visit_ty(field.ty);
}
pub fn walk_enum_def<'v, V: Visitor<'v>>(
}
}
-pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>) {
+pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &Path<'v>) {
for segment in path.segments {
visitor.visit_path_segment(segment);
}
// FIXME(swatinem): the following lang items are used for async lowering and
// should become obsolete eventually.
- ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
IdentityFuture, sym::identity_future, identity_future_fn, Target::Fn, GenericRequirement::None;
- GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
+ Context, sym::Context, context, Target::Struct, GenericRequirement::None;
FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
FromFrom, sym::from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
use rustc_span::symbol::Ident;
use rustc_span::Span;
-use std::iter::{Enumerate, ExactSizeIterator};
+use std::iter::Enumerate;
pub struct EnumerateAndAdjust<I> {
enumerate: Enumerate<I>,
use crate::definitions::{DefKey, DefPathData, DisambiguatedDefPathData};
use rustc_span::def_id::{DefPathHash, StableCrateId};
+use rustc_span::edition::Edition;
+use rustc_span::{create_session_if_not_set_then, Symbol};
#[test]
fn def_path_hash_depends_on_crate_id() {
// the crate by changing the crate disambiguator (e.g. via bumping the
// crate's version number).
- let id0 = StableCrateId::new("foo", false, vec!["1".to_string()]);
- let id1 = StableCrateId::new("foo", false, vec!["2".to_string()]);
+ create_session_if_not_set_then(Edition::Edition2024, |_| {
+ let id0 = StableCrateId::new(Symbol::intern("foo"), false, vec!["1".to_string()]);
+ let id1 = StableCrateId::new(Symbol::intern("foo"), false, vec!["2".to_string()]);
- let h0 = mk_test_hash(id0);
- let h1 = mk_test_hash(id1);
+ let h0 = mk_test_hash(id0);
+ let h1 = mk_test_hash(id1);
- assert_ne!(h0.stable_crate_id(), h1.stable_crate_id());
- assert_ne!(h0.local_hash(), h1.local_hash());
+ assert_ne!(h0.stable_crate_id(), h1.stable_crate_id());
+ assert_ne!(h0.local_hash(), h1.local_hash());
- fn mk_test_hash(stable_crate_id: StableCrateId) -> DefPathHash {
- let parent_hash = DefPathHash::new(stable_crate_id, 0);
+ fn mk_test_hash(stable_crate_id: StableCrateId) -> DefPathHash {
+ let parent_hash = DefPathHash::new(stable_crate_id, 0);
- let key = DefKey {
- parent: None,
- disambiguated_data: DisambiguatedDefPathData {
- data: DefPathData::CrateRoot,
- disambiguator: 0,
- },
- };
+ let key = DefKey {
+ parent: None,
+ disambiguated_data: DisambiguatedDefPathData {
+ data: DefPathData::CrateRoot,
+ disambiguator: 0,
+ },
+ };
- key.compute_stable_hash(parent_hash)
- }
+ key.compute_stable_hash(parent_hash)
+ }
+ })
}
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
-use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::ty::{
self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
};
Res::Def(DefKind::TyParam, src_def_id) => {
if let Some(param_local_id) = param.def_id.as_local() {
let param_name = tcx.hir().ty_param_name(param_local_id);
- let infcx = tcx.infer_ctxt().build();
- let param_type =
- infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id));
+ let param_type = tcx.type_of(param.def_id);
if param_type.is_suggestable(tcx, false) {
err.span_suggestion(
tcx.def_span(src_def_id),
assert!(self_ty.is_some());
}
} else {
- assert!(self_ty.is_none() && parent_substs.is_empty());
+ assert!(self_ty.is_none());
}
let arg_count = Self::check_generic_arg_count(
// Check if we have an enum variant.
let mut variant_resolution = None;
- if let ty::Adt(adt_def, _) = qself_ty.kind() {
+ if let ty::Adt(adt_def, adt_substs) = qself_ty.kind() {
if adt_def.is_enum() {
let variant_def = adt_def
.variants()
let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, impl_) else {
continue;
};
- // FIXME(inherent_associated_types): This does not substitute parameters.
- let ty = tcx.type_of(assoc_ty_did);
+ let item_substs = self.create_substs_for_associated_item(
+ span,
+ assoc_ty_did,
+ assoc_segment,
+ adt_substs,
+ );
+ let ty = tcx.bound_type_of(assoc_ty_did).subst(tcx, item_substs);
+ let ty = self.normalize_ty(span, ty);
return Ok((ty, DefKind::AssocTy, assoc_ty_did));
}
}
use crate::check::intrinsicck::InlineAsmCtxt;
+use crate::errors::LinkageType;
use super::compare_method::check_type_bounds;
use super::compare_method::{compare_impl_method, compare_ty_impl};
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::util::{Discr, IntTypeExt};
-use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
+use rustc_middle::ty::{self, AdtDef, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
use rustc_span::symbol::sym;
use rustc_span::{self, Span};
let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
}
+fn is_enum_of_nonnullable_ptr<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ adt_def: AdtDef<'tcx>,
+ substs: SubstsRef<'tcx>,
+) -> bool {
+ if adt_def.repr().inhibit_enum_layout_opt() {
+ return false;
+ }
+
+ let [var_one, var_two] = &adt_def.variants().raw[..] else {
+ return false;
+ };
+ let (([], [field]) | ([field], [])) = (&var_one.fields[..], &var_two.fields[..]) else {
+ return false;
+ };
+ matches!(field.ty(tcx, substs).kind(), ty::FnPtr(..) | ty::Ref(..))
+}
+
+fn check_static_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
+ if tcx.codegen_fn_attrs(def_id).import_linkage.is_some() {
+ if match tcx.type_of(def_id).kind() {
+ ty::RawPtr(_) => false,
+ ty::Adt(adt_def, substs) => !is_enum_of_nonnullable_ptr(tcx, *adt_def, *substs),
+ _ => true,
+ } {
+ tcx.sess.emit_err(LinkageType { span: tcx.def_span(def_id) });
+ }
+ }
+}
+
fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
debug!(
"check_item_type(it.def_id={:?}, it.name={})",
tcx.ensure().typeck(id.owner_id.def_id);
maybe_check_static_with_link_section(tcx, id.owner_id.def_id);
check_static_inhabited(tcx, id.owner_id.def_id);
+ check_static_linkage(tcx, id.owner_id.def_id);
}
DefKind::Const => {
tcx.ensure().typeck(id.owner_id.def_id);
}
hir::ForeignItemKind::Static(..) => {
check_static_inhabited(tcx, def_id);
+ check_static_linkage(tcx, def_id);
}
_ => {}
}
impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs);
debug!("compare_impl_method: trait_to_placeholder_substs={:?}", trait_to_placeholder_substs);
- let impl_m_generics = tcx.generics_of(impl_m.def_id);
- let trait_m_generics = tcx.generics_of(trait_m.def_id);
let impl_m_predicates = tcx.predicates_of(impl_m.def_id);
let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
// Check region bounds.
- check_region_bounds_on_impl_item(tcx, impl_m, trait_m, &trait_m_generics, &impl_m_generics)?;
+ check_region_bounds_on_impl_item(tcx, impl_m, trait_m, false)?;
// Create obligations for each predicate declared by the impl
// definition in the context of the trait's parameter
// First, check a few of the same thing as `compare_impl_method`, just so we don't ICE during substitutions later.
compare_number_of_generics(tcx, impl_m, trait_m, tcx.hir().span_if_local(impl_m.def_id), true)?;
compare_generic_param_kinds(tcx, impl_m, trait_m, true)?;
+ check_region_bounds_on_impl_item(tcx, impl_m, trait_m, true)?;
let trait_to_impl_substs = impl_trait_ref.substs;
tcx: TyCtxt<'tcx>,
impl_m: &ty::AssocItem,
trait_m: &ty::AssocItem,
- trait_generics: &ty::Generics,
- impl_generics: &ty::Generics,
+ delay: bool,
) -> Result<(), ErrorGuaranteed> {
- let trait_params = trait_generics.own_counts().lifetimes;
+ let impl_generics = tcx.generics_of(impl_m.def_id);
let impl_params = impl_generics.own_counts().lifetimes;
+ let trait_generics = tcx.generics_of(trait_m.def_id);
+ let trait_params = trait_generics.own_counts().lifetimes;
+
debug!(
"check_region_bounds_on_impl_item: \
trait_generics={:?} \
.get_generics(impl_m.def_id.expect_local())
.expect("expected impl item to have generics or else we can't compare them")
.span;
- let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() {
- Some(
- tcx.hir()
- .get_generics(local_def_id)
- .expect("expected trait item to have generics or else we can't compare them")
- .span,
- )
- } else {
- None
- };
- let reported = tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait {
- span,
- item_kind: assoc_item_kind_str(impl_m),
- ident: impl_m.ident(tcx),
- generics_span,
- });
+ let mut generics_span = None;
+ let mut bounds_span = vec![];
+ let mut where_span = None;
+ if let Some(trait_node) = tcx.hir().get_if_local(trait_m.def_id)
+ && let Some(trait_generics) = trait_node.generics()
+ {
+ generics_span = Some(trait_generics.span);
+ // FIXME: we could potentially look at the impl's bounds to not point at bounds that
+ // *are* present in the impl.
+ for p in trait_generics.predicates {
+ if let hir::WherePredicate::BoundPredicate(pred) = p {
+ for b in pred.bounds {
+ if let hir::GenericBound::Outlives(lt) = b {
+ bounds_span.push(lt.ident.span);
+ }
+ }
+ }
+ }
+ if let Some(impl_node) = tcx.hir().get_if_local(impl_m.def_id)
+ && let Some(impl_generics) = impl_node.generics()
+ {
+ let mut impl_bounds = 0;
+ for p in impl_generics.predicates {
+ if let hir::WherePredicate::BoundPredicate(pred) = p {
+ for b in pred.bounds {
+ if let hir::GenericBound::Outlives(_) = b {
+ impl_bounds += 1;
+ }
+ }
+ }
+ }
+ if impl_bounds == bounds_span.len() {
+ bounds_span = vec![];
+ } else if impl_generics.has_where_clause_predicates {
+ where_span = Some(impl_generics.where_clause_span);
+ }
+ }
+ }
+ let reported = tcx
+ .sess
+ .create_err(LifetimesOrBoundsMismatchOnTrait {
+ span,
+ item_kind: assoc_item_kind_str(impl_m),
+ ident: impl_m.ident(tcx),
+ generics_span,
+ bounds_span,
+ where_span,
+ })
+ .emit_unless(delay);
return Err(reported);
}
let trait_to_impl_substs =
impl_substs.rebase_onto(tcx, impl_ty.container_id(tcx), impl_trait_ref.substs);
- let impl_ty_generics = tcx.generics_of(impl_ty.def_id);
- let trait_ty_generics = tcx.generics_of(trait_ty.def_id);
let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id);
let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id);
- check_region_bounds_on_impl_item(
- tcx,
- impl_ty,
- trait_ty,
- &trait_ty_generics,
- &impl_ty_generics,
- )?;
+ check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?;
let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_substs);
// scopes, meaning that temporaries cannot outlive them.
// This ensures fixed size stacks.
hir::ExprKind::Binary(
- source_map::Spanned { node: hir::BinOpKind::And, .. },
- _,
- ref r,
- )
- | hir::ExprKind::Binary(
- source_map::Spanned { node: hir::BinOpKind::Or, .. },
- _,
+ source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
+ ref l,
ref r,
) => {
- // For shortcircuiting operators, mark the RHS as a terminating
- // scope since it only executes conditionally.
+ // expr is a short circuiting operator (|| or &&). As its
+ // functionality can't be overridden by traits, it always
+ // processes bool sub-expressions. bools are Copy and thus we
+ // can drop any temporaries in evaluation (read) order
+ // (with the exception of potentially failing let expressions).
+ // We achieve this by enclosing the operands in a terminating
+ // scope, both the LHS and the RHS.
+
+ // We optimize this a little in the presence of chains.
+ // Chains like a && b && c get lowered to AND(AND(a, b), c).
+ // In here, b and c are RHS, while a is the only LHS operand in
+ // that chain. This holds true for longer chains as well: the
+ // leading operand is always the only LHS operand that is not a
+ // binop itself. Putting a binop like AND(a, b) into a
+ // terminating scope is not useful, thus we only put the LHS
+ // into a terminating scope if it is not a binop.
+
+ let terminate_lhs = match l.kind {
+ // let expressions can create temporaries that live on
+ hir::ExprKind::Let(_) => false,
+ // binops already drop their temporaries, so there is no
+ // need to put them into a terminating scope.
+ // This is purely an optimization to reduce the number of
+ // terminating scopes.
+ hir::ExprKind::Binary(
+ source_map::Spanned {
+ node: hir::BinOpKind::And | hir::BinOpKind::Or, ..
+ },
+ ..,
+ ) => false,
+ // otherwise: mark it as terminating
+ _ => true,
+ };
+ if terminate_lhs {
+ terminating(l.hir_id.local_id);
+ }
// `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries
// should live beyond the immediate expression
};
use std::cell::LazyCell;
-use std::convert::TryInto;
use std::iter;
use std::ops::{ControlFlow, Deref};
check_where_clauses(wfcx, span, def_id);
check_return_position_impl_trait_in_trait_bounds(
- tcx,
+ wfcx,
def_id,
sig.output(),
hir_decl.output.span(),
/// Basically `check_associated_type_bounds`, but separated for now and should be
/// deduplicated when RPITITs get lowered into real associated items.
-#[tracing::instrument(level = "trace", skip(tcx))]
+#[tracing::instrument(level = "trace", skip(wfcx))]
fn check_return_position_impl_trait_in_trait_bounds<'tcx>(
- tcx: TyCtxt<'tcx>,
+ wfcx: &WfCheckingCtxt<'_, 'tcx>,
fn_def_id: LocalDefId,
fn_output: Ty<'tcx>,
span: Span,
) {
+ let tcx = wfcx.tcx();
if let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id())
&& assoc_item.container == ty::AssocItemContainer::TraitContainer
{
&& tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder
&& tcx.impl_trait_in_trait_parent(proj.item_def_id) == fn_def_id.to_def_id()
{
- // Create a new context, since we want the opaque's ParamEnv and not the parent's.
let span = tcx.def_span(proj.item_def_id);
- enter_wf_checking_ctxt(tcx, span, proj.item_def_id.expect_local(), |wfcx| {
- let bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id);
- let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| {
- let normalized_bound = wfcx.normalize(span, None, bound);
- traits::wf::predicate_obligations(
- wfcx.infcx,
- wfcx.param_env,
- wfcx.body_id,
- normalized_bound,
- bound_span,
- )
- });
- wfcx.register_obligations(wf_obligations);
+ let bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id);
+ let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| {
+ let bound = ty::EarlyBinder(bound).subst(tcx, proj.substs);
+ let normalized_bound = wfcx.normalize(span, None, bound);
+ traits::wf::predicate_obligations(
+ wfcx.infcx,
+ wfcx.param_env,
+ wfcx.body_id,
+ normalized_bound,
+ bound_span,
+ )
});
+ wfcx.register_obligations(wf_obligations);
}
}
}
let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx
.maybe_unused_extern_crates(())
.iter()
- .filter(|&&(def_id, _)| {
- // The `def_id` here actually was calculated during resolution (at least
- // at the time of this writing) and is being shipped to us via a side
- // channel of the tcx. There may have been extra expansion phases,
- // however, which ended up removing the `def_id` *after* expansion.
- //
- // As a result we need to verify that `def_id` is indeed still valid for
- // our AST and actually present in the HIR map. If it's not there then
- // there's safely nothing to warn about, and otherwise we carry on with
- // our execution.
- //
- // Note that if we carry through to the `extern_mod_stmt_cnum` query
- // below it'll cause a panic because `def_id` is actually bogus at this
- // point in time otherwise.
- if tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)).is_none() {
- return false;
- }
- true
- })
.filter(|&&(def_id, _)| {
tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| {
!tcx.is_compiler_builtins(cnum)
.struct_span_err(
attr.span,
"the `#[rustc_must_implement_one_of]` attribute must be \
- used with at least 2 args",
+ used with at least 2 args",
)
.emit();
tcx.sess
.struct_span_err(
item.span,
- "This function doesn't have a default implementation",
+ "function doesn't have a default implementation",
)
.span_note(attr_span, "required by this annotation")
.emit();
}
Some(item) => {
tcx.sess
- .struct_span_err(item.span, "Not a function")
+ .struct_span_err(item.span, "not a function")
.span_note(attr_span, "required by this annotation")
.note(
- "All `#[rustc_must_implement_one_of]` arguments \
- must be associated function names",
+ "all `#[rustc_must_implement_one_of]` arguments must be associated \
+ function names",
)
.emit();
}
None => {
tcx.sess
- .struct_span_err(ident.span, "Function not found in this trait")
+ .struct_span_err(ident.span, "function not found in this trait")
.emit();
}
}
for ident in &*list {
if let Some(dup) = set.insert(ident.name, ident.span) {
tcx.sess
- .struct_span_err(vec![dup, ident.span], "Functions names are duplicated")
- .note(
- "All `#[rustc_must_implement_one_of]` arguments \
- must be unique",
- )
+ .struct_span_err(vec![dup, ident.span], "functions names are duplicated")
+ .note("all `#[rustc_must_implement_one_of]` arguments must be unique")
.emit();
no_dups = false;
);
} else if attr.has_name(sym::linkage) {
if let Some(val) = attr.value_str() {
- codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, did, val.as_str()));
+ let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
+ if tcx.is_foreign_item(did) {
+ codegen_fn_attrs.import_linkage = linkage;
+ } else {
+ codegen_fn_attrs.linkage = linkage;
+ }
}
} else if attr.has_name(sym::link_section) {
if let Some(val) = attr.value_str() {
codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS;
} else if item.has_name(sym::cfi) {
codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
+ } else if item.has_name(sym::kcfi) {
+ codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI;
} else if item.has_name(sym::memory) {
codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
} else if item.has_name(sym::memtag) {
} else {
tcx.sess
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
- .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
+ .note("expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
.emit();
}
}
}
}
+ if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
+ codegen_fn_attrs.inline = InlineAttr::Never;
+ }
+
// Weak lang items have the same semantics as "std internal" symbols in the
// sense that they're preserved through all our LTO passes and only
// strippable by the linker.
GenericParamKind, HirId, Node,
};
use rustc_hir as hir;
-use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint;
let generics = tcx.generics_of(parent_def_id.to_def_id());
let param_def_idx = generics.param_def_id_to_index[¶m_id.to_def_id()];
// In the above example this would be .params[..N#0]
- let params = generics.params[..param_def_idx as usize].to_owned();
+ let params = generics.params_to(param_def_idx as usize, tcx).to_owned();
let param_def_id_to_index =
params.iter().map(|param| (param.def_id, param.index)).collect();
Some(tcx.typeck_root_def_id(def_id))
}
Node::Item(item) => match item.kind {
- ItemKind::OpaqueTy(hir::OpaqueTy {
- origin:
- hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
- in_trait,
- ..
- }) => {
- if in_trait {
- assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn))
- } else {
- assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn))
- }
- Some(fn_def_id.to_def_id())
- }
- ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => {
+ ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => {
let parent_id = tcx.hir().get_parent_item(hir_id);
assert_ne!(parent_id, hir::CRATE_OWNER_ID);
debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id);
}
}
- fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+ fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
for (i, segment) in path.segments.iter().enumerate() {
let depth = path.segments.len() - i - 1;
if let Some(ref args) = segment.args {
pub span: Span,
#[label(generics_label)]
pub generics_span: Option<Span>,
+ #[label(where_label)]
+ pub where_span: Option<Span>,
+ #[label(bounds_label)]
+ pub bounds_span: Vec<Span>,
pub item_kind: &'static str,
pub ident: Ident,
}
#[note]
pub note: (),
}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_linkage_type, code = "E0791")]
+pub(crate) struct LinkageType {
+ #[primary_span]
+ pub span: Span,
+}
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt, TypeSuperVisitable, TypeVisitable};
+use rustc_middle::ty::{self, CrateVariancesMap, SubstsRef, Ty, TyCtxt};
+use rustc_middle::ty::{DefIdTree, TypeSuperVisitable, TypeVisitable};
use std::ops::ControlFlow;
/// Defines the `TermsContext` basically houses an arena where we can
// type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b;
// ```
// we may not use `'c` in the hidden type.
- struct OpaqueTypeLifetimeCollector {
+ struct OpaqueTypeLifetimeCollector<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ root_def_id: DefId,
variances: Vec<ty::Variance>,
}
- impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector {
+ impl<'tcx> OpaqueTypeLifetimeCollector<'tcx> {
+ #[instrument(level = "trace", skip(self), ret)]
+ fn visit_opaque(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) -> ControlFlow<!> {
+ if def_id != self.root_def_id && self.tcx.is_descendant_of(def_id, self.root_def_id) {
+ let child_variances = self.tcx.variances_of(def_id);
+ for (a, v) in substs.iter().zip(child_variances) {
+ if *v != ty::Bivariant {
+ a.visit_with(self)?;
+ }
+ }
+ ControlFlow::CONTINUE
+ } else {
+ substs.visit_with(self)
+ }
+ }
+ }
+
+ impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector<'tcx> {
#[instrument(level = "trace", skip(self), ret)]
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::RegionKind::ReEarlyBound(ebr) = r.kind() {
}
r.super_visit_with(self)
}
+
+ #[instrument(level = "trace", skip(self), ret)]
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ match t.kind() {
+ ty::Opaque(def_id, substs) => self.visit_opaque(*def_id, substs),
+ ty::Projection(proj)
+ if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder =>
+ {
+ self.visit_opaque(proj.item_def_id, proj.substs)
+ }
+ _ => t.super_visit_with(self),
+ }
+ }
}
// By default, RPIT are invariant wrt type and const generics, but they are bivariant wrt
}
}
- let mut collector = OpaqueTypeLifetimeCollector { variances };
+ let mut collector =
+ OpaqueTypeLifetimeCollector { tcx, root_def_id: item_def_id.to_def_id(), variances };
let id_substs = ty::InternalSubsts::identity_for_item(tcx, item_def_id.to_def_id());
for pred in tcx.bound_explicit_item_bounds(item_def_id.to_def_id()).transpose_iter() {
let pred = pred.map_bound(|(pred, _)| *pred).subst(tcx, id_substs);
self.head("trait");
self.print_ident(item.ident);
self.print_generic_params(generics.params);
- let mut real_bounds = Vec::with_capacity(bounds.len());
- // FIXME(durka) this seems to be some quite outdated syntax
- for b in bounds {
- if let GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = b {
- self.space();
- self.word_space("for ?");
- self.print_trait_ref(&ptr.trait_ref);
- } else {
- real_bounds.push(b);
- }
- }
self.nbsp();
- self.print_bounds("=", real_bounds);
+ self.print_bounds("=", bounds);
self.print_where_clause(generics);
self.word(";");
self.end(); // end inner head-block
fn_decl,
body,
fn_decl_span: _,
+ fn_arg_span: _,
movability: _,
def_id: _,
}) => {
self.print_ident(Ident::with_dummy_span(name))
}
- pub fn print_path(&mut self, path: &hir::Path<'_>, colons_before_params: bool) {
+ pub fn print_path<R>(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) {
self.maybe_print_comment(path.span.lo());
for (i, segment) in path.segments.iter().enumerate() {
callee_expr,
call_expr,
callee_ty,
- pick,
+ &pick,
segment,
);
if pick.illegal_sized_bound.is_some() {
use crate::coercion::CoerceMany;
use crate::gather_locals::GatherLocalsVisitor;
use crate::FnCtxt;
-use crate::{GeneratorTypes, UnsafetyState};
+use crate::GeneratorTypes;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::intravisit::Visitor;
can_be_generator: Option<hir::Movability>,
) -> Option<GeneratorTypes<'tcx>> {
let fn_id = fcx.tcx.hir().local_def_id_to_hir_id(fn_def_id);
- fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id));
let tcx = fcx.tcx;
let hir = tcx.hir();
.iter()
.map(|ty| ArgKind::from_expected_ty(*ty, None))
.collect();
- let (closure_span, found_args) = match self.get_fn_like_arguments(expr_map_node) {
- Some((sp, args)) => (Some(sp), args),
- None => (None, Vec::new()),
- };
+ let (closure_span, closure_arg_span, found_args) =
+ match self.get_fn_like_arguments(expr_map_node) {
+ Some((sp, arg_sp, args)) => (Some(sp), arg_sp, args),
+ None => (None, None, Vec::new()),
+ };
let expected_span =
expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id));
self.report_arg_count_mismatch(
expected_args,
found_args,
true,
+ closure_arg_span,
)
.emit();
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
- error: Option<TypeError<'_>>,
+ error: Option<TypeError<'tcx>>,
) {
let parent = self.tcx.hir().get_parent_node(expr.hir_id);
match (self.tcx.hir().find(parent), error) {
err.downgrade_to_delayed_bug();
}
}
+ (
+ Some(hir::Node::Expr(hir::Expr {
+ kind: hir::ExprKind::Binary(_, lhs, rhs), ..
+ })),
+ Some(TypeError::Sorts(ExpectedFound { expected, .. })),
+ ) if rhs.hir_id == expr.hir_id
+ && self.typeck_results.borrow().expr_ty_adjusted_opt(lhs) == Some(expected) =>
+ {
+ err.span_label(lhs.span, &format!("expected because this is `{expected}`"));
+ }
_ => {}
}
}
self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span);
let ty = match res {
Res::Err => {
+ self.suggest_assoc_method_call(segs);
let e =
self.tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted");
self.set_tainted_by_errors(e);
// the fields with the base_expr. This could cause us to hit errors later
// when certain fields are assumed to exist that in fact do not.
if error_happened {
+ if let Some(base_expr) = base_expr {
+ self.check_expr(base_expr);
+ }
return;
}
// Consume the expressions supplying values for each field.
for field in fields {
self.consume_expr(field.expr);
+
+ // The struct path probably didn't resolve
+ if self.mc.typeck_results.opt_field_index(field.hir_id).is_none() {
+ self.tcx().sess.delay_span_bug(field.span, "couldn't resolve index for field");
+ }
}
let with_expr = match *opt_with {
ty::Adt(adt, substs) if adt.is_struct() => {
// Consume those fields of the with expression that are needed.
for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() {
- let is_mentioned = fields.iter().any(|f| {
- self.tcx().field_index(f.hir_id, self.mc.typeck_results) == f_index
- });
+ let is_mentioned = fields
+ .iter()
+ .any(|f| self.mc.typeck_results.opt_field_index(f.hir_id) == Some(f_index));
if !is_mentioned {
let field_place = self.mc.cat_projection(
&*with_expr,
blk: &'tcx hir::Block<'tcx>,
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
- let prev = self.ps.replace(self.ps.get().recurse(blk));
-
// In some cases, blocks have just one exit, but other blocks
// can be targeted by multiple breaks. This can happen both
// with labeled blocks as well as when we desugar
self.write_ty(blk.hir_id, ty);
- self.ps.set(prev);
ty
}
receiver: Option<&'tcx hir::Expr<'tcx>>,
args: &'tcx [hir::Expr<'tcx>],
) -> bool {
- let sig = self.tcx.fn_sig(def_id).skip_binder();
+ let ty = self.tcx.type_of(def_id);
+ if !ty.is_fn() {
+ return false;
+ }
+ let sig = ty.fn_sig(self.tcx).skip_binder();
let args_referencing_param: Vec<_> = sig
.inputs()
.iter()
pub use suggestions::*;
use crate::coercion::DynamicCoerceMany;
-use crate::{Diverges, EnclosingBreakables, Inherited, UnsafetyState};
+use crate::{Diverges, EnclosingBreakables, Inherited};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir_analysis::astconv::AstConv;
use rustc_session::Session;
use rustc_span::symbol::Ident;
use rustc_span::{self, Span};
-use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
+use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
use std::cell::{Cell, RefCell};
use std::ops::Deref;
pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
- pub(super) ps: Cell<UnsafetyState>,
-
/// Whether the last checked node generates a divergence (e.g.,
/// `return` will set this to `Always`). In general, when entering
/// an expression or other node in the tree, the initial value
ret_coercion: None,
ret_coercion_span: Cell::new(None),
resume_yield_tys: None,
- ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
diverges: Cell::new(Diverges::Maybe),
enclosing_breakables: RefCell::new(EnclosingBreakables {
stack: Vec::new(),
infcx: &self.infcx,
typeck_results: Some(self.typeck_results.borrow()),
fallback_has_occurred: self.fallback_has_occurred.get(),
+ normalize_fn_sig: Box::new(|fn_sig| {
+ if fn_sig.has_escaping_bound_vars() {
+ return fn_sig;
+ }
+ self.probe(|_| {
+ let ocx = ObligationCtxt::new_in_snapshot(self);
+ let normalized_fn_sig =
+ ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig);
+ if ocx.select_all_or_error().is_empty() {
+ let normalized_fn_sig = self.resolve_vars_if_possible(normalized_fn_sig);
+ if !normalized_fn_sig.needs_infer() {
+ return normalized_fn_sig;
+ }
+ }
+ fn_sig
+ })
+ }),
}
}
Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
};
use rustc_hir_analysis::astconv::AstConv;
-use rustc_infer::infer::{self, TyCtxtInferExt};
+use rustc_infer::infer;
use rustc_infer::traits::{self, StatementAsExpression};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty};
}
pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) {
+ // This suggestion is incorrect for
+ // fn foo() -> bool { match () { () => true } || match () { () => true } }
err.span_suggestion_short(
span.shrink_to_hi(),
"consider using a semicolon here",
";",
- Applicability::MachineApplicable,
+ Applicability::MaybeIncorrect,
);
}
let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
let bound_vars = self.tcx.late_bound_vars(fn_id);
let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
- let ty = self.normalize(expr.span, ty);
let ty = match self.tcx.asyncness(fn_id.owner) {
- hir::IsAsync::Async => {
- let infcx = self.tcx.infer_ctxt().build();
- infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| {
- span_bug!(
- fn_decl.output.span(),
- "failed to get output type of async function"
- )
- })
- }
+ hir::IsAsync::Async => self.get_impl_future_output_ty(ty).unwrap_or_else(|| {
+ span_bug!(fn_decl.output.span(), "failed to get output type of async function")
+ }),
hir::IsAsync::NotAsync => ty,
};
+ let ty = self.normalize(expr.span, ty);
if self.can_coerce(found, ty) {
err.multipart_suggestion(
"you might have meant to return this value",
use super::callee::DeferredCallResolution;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::sync::Lrc;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::HirIdMap;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::LocalDefIdMap;
use rustc_span::{self, Span};
-use rustc_trait_selection::traits::{
- self, ObligationCause, ObligationCtxt, TraitEngine, TraitEngineExt as _,
-};
+use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt as _};
use std::cell::RefCell;
use std::ops::Deref;
infcx: tcx
.infer_ctxt()
.ignoring_regions()
- .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id))
- .with_normalize_fn_sig_for_diagnostic(Lrc::new(move |infcx, fn_sig| {
- if fn_sig.has_escaping_bound_vars() {
- return fn_sig;
- }
- infcx.probe(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
- let normalized_fn_sig = ocx.normalize(
- &ObligationCause::dummy(),
- // FIXME(compiler-errors): This is probably not the right param-env...
- infcx.tcx.param_env(def_id),
- fn_sig,
- );
- if ocx.select_all_or_error().is_empty() {
- let normalized_fn_sig =
- infcx.resolve_vars_if_possible(normalized_fn_sig);
- if !normalized_fn_sig.needs_infer() {
- return normalized_fn_sig;
- }
- }
- fn_sig
- })
- })),
+ .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)),
def_id,
typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)),
}
revealed_ty: Ty<'tcx>,
}
-#[derive(Copy, Clone)]
-pub struct UnsafetyState {
- pub def: hir::HirId,
- pub unsafety: hir::Unsafety,
- from_fn: bool,
-}
-
-impl UnsafetyState {
- pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState {
- UnsafetyState { def, unsafety, from_fn: true }
- }
-
- pub fn recurse(self, blk: &hir::Block<'_>) -> UnsafetyState {
- use hir::BlockCheckMode;
- match self.unsafety {
- // If this unsafe, then if the outer function was already marked as
- // unsafe we shouldn't attribute the unsafe'ness to the block. This
- // way the block can be warned about instead of ignoring this
- // extraneous block (functions are never warned about).
- hir::Unsafety::Unsafe if self.from_fn => self,
-
- unsafety => {
- let (unsafety, def) = match blk.rules {
- BlockCheckMode::UnsafeBlock(..) => (hir::Unsafety::Unsafe, blk.hir_id),
- BlockCheckMode::DefaultBlock => (unsafety, self.def),
- };
- UnsafetyState { def, unsafety, from_fn: false }
- }
- }
- }
-}
-
/// If this `DefId` is a "primary tables entry", returns
/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`.
///
self_expr: &'tcx hir::Expr<'tcx>,
call_expr: &'tcx hir::Expr<'tcx>,
unadjusted_self_ty: Ty<'tcx>,
- pick: probe::Pick<'tcx>,
+ pick: &probe::Pick<'tcx>,
segment: &hir::PathSegment<'_>,
) -> ConfirmResult<'tcx> {
debug!(
fn confirm(
&mut self,
unadjusted_self_ty: Ty<'tcx>,
- pick: probe::Pick<'tcx>,
+ pick: &probe::Pick<'tcx>,
segment: &hir::PathSegment<'_>,
) -> ConfirmResult<'tcx> {
// Adjust the self expression the user provided and obtain the adjusted type.
use rustc_infer::infer::{self, InferOk};
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
-use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, Ty, TypeVisitable};
+use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitable};
use rustc_span::symbol::Ident;
use rustc_span::Span;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None);
- let result =
- self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment);
+ let result = self.confirm_method(span, self_expr, call_expr, self_ty, &pick, segment);
debug!("result = {:?}", result);
if let Some(span) = result.illegal_sized_bound {
}
// We probe again, taking all traits into account (not only those in scope).
- let mut candidates =
+ let candidates =
match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) {
// If we find a different result the caller probably forgot to import a trait.
Ok(ref new_pick) if pick.differs_from(new_pick) => {
.collect(),
_ => Vec::new(),
};
- candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id));
return Err(IllegalSizedBound(candidates, needs_mut, span));
}
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
-use rustc_hir::def::Namespace;
use rustc_infer::infer::canonical::OriginalQueryValues;
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_trait_selection::traits::query::CanonicalTyGoal;
use rustc_trait_selection::traits::NormalizeExt;
use rustc_trait_selection::traits::{self, ObligationCause};
+use std::cell::RefCell;
use std::cmp::max;
use std::iter;
-use std::mem;
use std::ops::Deref;
use smallvec::{smallvec, SmallVec};
/// This is the OriginalQueryValues for the steps queries
/// that are answered in steps.
- orig_steps_var_values: OriginalQueryValues<'tcx>,
+ orig_steps_var_values: &'a OriginalQueryValues<'tcx>,
steps: &'tcx [CandidateStep<'tcx>],
inherent_candidates: Vec<Candidate<'tcx>>,
extension_candidates: Vec<Candidate<'tcx>>,
impl_dups: FxHashSet<DefId>,
- /// Collects near misses when the candidate functions are missing a `self` keyword and is only
- /// used for error reporting
- static_candidates: Vec<CandidateSource>,
-
/// When probing for names, include names that are close to the
- /// requested name (by Levensthein distance)
+ /// requested name (by Levenshtein distance)
allow_similar_names: bool,
/// Some(candidate) if there is a private candidate
private_candidate: Option<(DefKind, DefId)>,
+ /// Collects near misses when the candidate functions are missing a `self` keyword and is only
+ /// used for error reporting
+ static_candidates: RefCell<Vec<CandidateSource>>,
+
/// Collects near misses when trait bounds for type parameters are unsatisfied and is only used
/// for error reporting
- unsatisfied_predicates:
+ unsatisfied_predicates: RefCell<
Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
+ >,
scope_expr_id: hir::HirId,
}
op: OP,
) -> Result<R, MethodError<'tcx>>
where
- OP: FnOnce(ProbeContext<'a, 'tcx>) -> Result<R, MethodError<'tcx>>,
+ OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result<R, MethodError<'tcx>>,
{
let mut orig_values = OriginalQueryValues::default();
let param_env_and_self_ty = self.canonicalize_query(
mode,
method_name,
return_type,
- orig_values,
+ &orig_values,
steps.steps,
scope_expr_id,
);
mode: Mode,
method_name: Option<Ident>,
return_type: Option<Ty<'tcx>>,
- orig_steps_var_values: OriginalQueryValues<'tcx>,
+ orig_steps_var_values: &'a OriginalQueryValues<'tcx>,
steps: &'tcx [CandidateStep<'tcx>],
scope_expr_id: hir::HirId,
) -> ProbeContext<'a, 'tcx> {
impl_dups: FxHashSet::default(),
orig_steps_var_values,
steps,
- static_candidates: Vec::new(),
allow_similar_names: false,
private_candidate: None,
- unsatisfied_predicates: Vec::new(),
+ static_candidates: RefCell::new(Vec::new()),
+ unsatisfied_predicates: RefCell::new(Vec::new()),
scope_expr_id,
}
}
self.inherent_candidates.clear();
self.extension_candidates.clear();
self.impl_dups.clear();
- self.static_candidates.clear();
self.private_candidate = None;
+ self.static_candidates.borrow_mut().clear();
+ self.unsatisfied_predicates.borrow_mut().clear();
}
///////////////////////////////////////////////////////////////////////////
debug!("pick: actual search failed, assemble diagnostics");
- let static_candidates = mem::take(&mut self.static_candidates);
+ let static_candidates = std::mem::take(self.static_candidates.get_mut());
let private_candidate = self.private_candidate.take();
- let unsatisfied_predicates = mem::take(&mut self.unsatisfied_predicates);
+ let unsatisfied_predicates = std::mem::take(self.unsatisfied_predicates.get_mut());
// things failed, so lets look at all traits, for diagnostic purposes now:
self.reset();
}))
}
- fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
+ fn pick_core(&self) -> Option<PickResult<'tcx>> {
let pick = self.pick_all_method(Some(&mut vec![]));
// In this case unstable picking is done by `pick_method`.
}
fn pick_all_method(
- &mut self,
+ &self,
mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
) -> Option<PickResult<'tcx>> {
- let steps = self.steps.clone();
- steps
+ self.steps
.iter()
.filter(|step| {
debug!("pick_all_method: step={:?}", step);
/// to transparently pass `&mut` pointers, in particular, without consuming
/// them for their entire lifetime.
fn pick_by_value_method(
- &mut self,
+ &self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
}
fn pick_autorefd_method(
- &mut self,
+ &self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
mutbl: hir::Mutability,
/// special case for this is because going from `*mut T` to `*const T` with autoderefs and
/// autorefs would require dereferencing the pointer, which is not safe.
fn pick_const_ptr_method(
- &mut self,
+ &self,
step: &CandidateStep<'tcx>,
self_ty: Ty<'tcx>,
unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
})
}
- fn pick_method_with_unstable(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> {
+ fn pick_method_with_unstable(&self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> {
debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty));
let mut possibly_unsatisfied_predicates = Vec::new();
debug!("searching {} candidates", kind);
let res = self.consider_candidates(
self_ty,
- candidates.iter(),
+ candidates,
&mut possibly_unsatisfied_predicates,
Some(&mut vec![]),
);
}
}
- debug!("searching unstable candidates");
- let res = self.consider_candidates(
- self_ty,
- self.inherent_candidates.iter().chain(&self.extension_candidates),
- &mut possibly_unsatisfied_predicates,
- None,
- );
- if res.is_none() {
- self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates);
+ for (kind, candidates) in
+ &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)]
+ {
+ debug!("searching unstable {kind} candidates");
+ let res = self.consider_candidates(
+ self_ty,
+ candidates,
+ &mut possibly_unsatisfied_predicates,
+ None,
+ );
+ if res.is_some() {
+ return res;
+ }
}
- res
+
+ self.unsatisfied_predicates.borrow_mut().extend(possibly_unsatisfied_predicates);
+ None
}
fn pick_method(
- &mut self,
+ &self,
self_ty: Ty<'tcx>,
mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
) -> Option<PickResult<'tcx>> {
debug!("searching {} candidates", kind);
let res = self.consider_candidates(
self_ty,
- candidates.iter(),
+ candidates,
&mut possibly_unsatisfied_predicates,
unstable_candidates.as_deref_mut(),
);
// `pick_method` may be called twice for the same self_ty if no stable methods
// match. Only extend once.
if unstable_candidates.is_some() {
- self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates);
+ self.unsatisfied_predicates.borrow_mut().extend(possibly_unsatisfied_predicates);
}
None
}
- fn consider_candidates<'b, ProbesIter>(
+ fn consider_candidates(
&self,
self_ty: Ty<'tcx>,
- probes: ProbesIter,
+ candidates: &[Candidate<'tcx>],
possibly_unsatisfied_predicates: &mut Vec<(
ty::Predicate<'tcx>,
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)>,
mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
- ) -> Option<PickResult<'tcx>>
- where
- ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone,
- 'tcx: 'b,
- {
- let mut applicable_candidates: Vec<_> = probes
- .clone()
+ ) -> Option<PickResult<'tcx>> {
+ let mut applicable_candidates: Vec<_> = candidates
+ .iter()
.map(|probe| {
(probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates))
})
}
if let Some(uc) = &mut unstable_candidates {
- applicable_candidates.retain(|&(p, _)| {
+ applicable_candidates.retain(|&(candidate, _)| {
if let stability::EvalResult::Deny { feature, .. } =
- self.tcx.eval_stability(p.item.def_id, None, self.span, None)
+ self.tcx.eval_stability(candidate.item.def_id, None, self.span, None)
{
- uc.push((p.clone(), feature));
+ uc.push((candidate.clone(), feature));
return false;
}
true
}
if applicable_candidates.len() > 1 {
- let sources = probes.map(|p| self.candidate_source(p, self_ty)).collect();
+ let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect();
return Some(Err(MethodError::Ambiguity(sources)));
}
self.mode,
self.method_name,
self.return_type,
- self.orig_steps_var_values.clone(),
+ &self.orig_steps_var_values,
steps,
self.scope_expr_id,
);
// -- but this could be overcome.
}
- fn record_static_candidate(&mut self, source: CandidateSource) {
- self.static_candidates.push(source);
+ fn record_static_candidate(&self, source: CandidateSource) {
+ self.static_candidates.borrow_mut().push(source);
}
#[instrument(level = "debug", skip(self))]
self.tcx.erase_late_bound_regions(value)
}
+ /// Determine if the given associated item type is relevant in the current context.
+ fn is_relevant_kind_for_mode(&self, kind: ty::AssocKind) -> bool {
+ match (self.mode, kind) {
+ (Mode::MethodCall, ty::AssocKind::Fn) => true,
+ (Mode::Path, ty::AssocKind::Const | ty::AssocKind::Fn) => true,
+ _ => false,
+ }
+ }
+
/// Finds the method with the appropriate name (or return type, as the case may be). If
/// `allow_similar_names` is set, find methods with close-matching names.
// The length of the returned iterator is nearly always 0 or 1 and this
.associated_items(def_id)
.in_definition_order()
.filter(|x| {
- if x.kind.namespace() != Namespace::ValueNS {
+ if !self.is_relevant_kind_for_mode(x.kind) {
return false;
}
match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist)
} else {
self.fcx
.associated_value(def_id, name)
+ .filter(|x| self.is_relevant_kind_for_mode(x.kind))
.map_or_else(SmallVec::new, |x| SmallVec::from_buf([x]))
}
} else {
- self.tcx.associated_items(def_id).in_definition_order().copied().collect()
+ self.tcx
+ .associated_items(def_id)
+ .in_definition_order()
+ .filter(|x| self.is_relevant_kind_for_mode(x.kind))
+ .copied()
+ .collect()
}
}
}
use crate::FnCtxt;
use rustc_ast::ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::StashKey;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
MultiSpan,
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
+use rustc_hir::PatKind::Binding;
+use rustc_hir::PathSegment;
use rustc_hir::{ExprKind, Node, QPath};
use rustc_infer::infer::{
type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
};
-use std::cmp::Ordering;
-use std::iter;
-
use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
use super::{CandidateSource, MethodError, NoMatchData};
+use rustc_hir::intravisit::Visitor;
+use std::cmp::Ordering;
+use std::iter;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
false
}
+ /// For code `rect::area(...)`,
+ /// if `rect` is a local variable and `area` is a valid assoc method for it,
+ /// we try to suggest `rect.area()`
+ pub(crate) fn suggest_assoc_method_call(&self, segs: &[PathSegment<'_>]) {
+ debug!("suggest_assoc_method_call segs: {:?}", segs);
+ let [seg1, seg2] = segs else { return; };
+ let Some(mut diag) =
+ self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod)
+ else { return };
+
+ let map = self.infcx.tcx.hir();
+ let body = map.body(rustc_hir::BodyId { hir_id: self.body_id });
+ struct LetVisitor<'a> {
+ result: Option<&'a hir::Expr<'a>>,
+ ident_name: Symbol,
+ }
+
+ // FIXME: This really should be taking scoping, etc into account.
+ impl<'v> Visitor<'v> for LetVisitor<'v> {
+ fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
+ if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind
+ && let Binding(_, _, ident, ..) = pat.kind
+ && ident.name == self.ident_name
+ {
+ self.result = *init;
+ } else {
+ hir::intravisit::walk_stmt(self, ex);
+ }
+ }
+ }
+
+ let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
+ visitor.visit_body(&body);
+
+ let parent = self.tcx.hir().get_parent_node(seg1.hir_id);
+ if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent)
+ && let Some(expr) = visitor.result
+ && let Some(self_ty) = self.node_ty_opt(expr.hir_id)
+ {
+ let probe = self.lookup_probe(
+ seg2.ident,
+ self_ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ );
+ if probe.is_ok() {
+ let sm = self.infcx.tcx.sess.source_map();
+ diag.span_suggestion_verbose(
+ sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(),
+ "you may have meant to call an instance method",
+ ".".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ diag.emit();
+ }
+
/// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()`
fn suggest_calling_method_on_field(
&self,
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
-use std::iter::FromIterator;
-use std::vec::Vec;
const LOADED_FROM_DISK: Symbol = sym::loaded_from_disk;
const EXCEPT: Symbol = sym::except;
use rustc_errors::ErrorGuaranteed;
use rustc_fs_util::{link_or_copy, LinkOrCopy};
use rustc_session::{Session, StableCrateId};
+use rustc_span::Symbol;
use std::fs as std_fs;
use std::io::{self, ErrorKind};
-use std::mem;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
/// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph
pub fn prepare_session_directory(
sess: &Session,
- crate_name: &str,
+ crate_name: Symbol,
stable_crate_id: StableCrateId,
) -> Result<(), ErrorGuaranteed> {
if sess.opts.incremental.is_none() {
}
delete_session_dir_lock_file(sess, &lock_file_path);
- mem::drop(directory_lock);
+ drop(directory_lock);
}
}
}
Ok(UNIX_EPOCH + duration)
}
-fn crate_path(sess: &Session, crate_name: &str, stable_crate_id: StableCrateId) -> PathBuf {
+fn crate_path(sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId) -> PathBuf {
let incr_dir = sess.opts.incremental.as_ref().unwrap().clone();
let stable_crate_id = base_n::encode(stable_crate_id.to_u64() as u128, INT_ENCODE_BASE);
// Let's make it explicit that the file lock is released at this point,
// or rather, that we held on to it until here
- mem::drop(lock);
+ drop(lock);
}
Err(_) => {
debug!(
// Let's make it explicit that the file lock is released at this point,
// or rather, that we held on to it until here
- mem::drop(lock);
+ drop(lock);
}
Ok(())
use std::fmt;
use std::fmt::Debug;
use std::hash::Hash;
-use std::iter::FromIterator;
use std::marker::PhantomData;
use std::ops::{Index, IndexMut, RangeBounds};
use std::slice;
err_count_on_creation: self.err_count_on_creation,
in_snapshot: self.in_snapshot.clone(),
universe: self.universe.clone(),
- normalize_fn_sig_for_diagnostic: self
- .normalize_fn_sig_for_diagnostic
- .as_ref()
- .map(|f| f.clone()),
intercrate: self.intercrate,
}
}
use crate::traits::error_reporting::report_object_safety_error;
use crate::traits::{
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
- StatementAsExpression,
};
-use crate::errors::SuggAddLetForLetChains;
-use hir::intravisit::{walk_expr, walk_stmt};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, DefKind};
+use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::Node;
use rustc_middle::dep_graph::DepContext;
-use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
use rustc_middle::ty::{
self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
use std::ops::{ControlFlow, Deref};
+use std::path::PathBuf;
use std::{cmp, fmt, iter};
mod note;
+mod suggest;
pub(crate) mod need_type_info;
pub use need_type_info::TypeAnnotationNeeded;
pub struct TypeErrCtxt<'a, 'tcx> {
pub infcx: &'a InferCtxt<'tcx>,
pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
+ pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>,
pub fallback_has_occurred: bool,
}
impl<'tcx> InferCtxt<'tcx> {
pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
- let ty::Opaque(def_id, substs) = *ty.kind() else { return None; };
+ let (def_id, substs) = match *ty.kind() {
+ ty::Opaque(def_id, substs) => (def_id, substs),
+ ty::Projection(data)
+ if self.tcx.def_kind(data.item_def_id) == DefKind::ImplTraitPlaceholder =>
+ {
+ (data.item_def_id, data.substs)
+ }
+ _ => return None,
+ };
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
}
}
- fn suggest_remove_semi_or_return_binding(
- &self,
- err: &mut Diagnostic,
- first_id: Option<hir::HirId>,
- first_ty: Ty<'tcx>,
- first_span: Span,
- second_id: Option<hir::HirId>,
- second_ty: Ty<'tcx>,
- second_span: Span,
- ) {
- let remove_semicolon = [
- (first_id, self.resolve_vars_if_possible(second_ty)),
- (second_id, self.resolve_vars_if_possible(first_ty)),
- ]
- .into_iter()
- .find_map(|(id, ty)| {
- let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None };
- self.could_remove_semicolon(blk, ty)
- });
- match remove_semicolon {
- Some((sp, StatementAsExpression::NeedsBoxing)) => {
- err.multipart_suggestion(
- "consider removing this semicolon and boxing the expressions",
- vec![
- (first_span.shrink_to_lo(), "Box::new(".to_string()),
- (first_span.shrink_to_hi(), ")".to_string()),
- (second_span.shrink_to_lo(), "Box::new(".to_string()),
- (second_span.shrink_to_hi(), ")".to_string()),
- (sp, String::new()),
- ],
- Applicability::MachineApplicable,
- );
- }
- Some((sp, StatementAsExpression::CorrectType)) => {
- err.span_suggestion_short(
- sp,
- "consider removing this semicolon",
- "",
- Applicability::MachineApplicable,
- );
- }
- None => {
- for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
- if let Some(id) = id
- && let hir::Node::Block(blk) = self.tcx.hir().get(id)
- && self.consider_returning_binding(blk, ty, err)
- {
- break;
- }
- }
- }
- }
- }
-
- fn suggest_boxing_for_return_impl_trait(
- &self,
- err: &mut Diagnostic,
- return_sp: Span,
- arm_spans: impl Iterator<Item = Span>,
- ) {
- err.multipart_suggestion(
- "you could change the return type to be a boxed trait object",
- vec![
- (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
- (return_sp.shrink_to_hi(), ">".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- let sugg = arm_spans
- .flat_map(|sp| {
- [(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
- .into_iter()
- })
- .collect::<Vec<_>>();
- err.multipart_suggestion(
- "if you change the return type to expect trait objects, box the returned expressions",
- sugg,
- Applicability::MaybeIncorrect,
- );
- }
-
/// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
/// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
/// populate `other_value` with `other_ty`.
}
}
- fn normalize_fn_sig_for_diagnostic(&self, sig: ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> {
- if let Some(normalize) = &self.normalize_fn_sig_for_diagnostic {
- normalize(self, sig)
- } else {
- sig
- }
- }
-
/// Given two `fn` signatures highlight only sub-parts that are different.
fn cmp_fn_sig(
&self,
sig1: &ty::PolyFnSig<'tcx>,
sig2: &ty::PolyFnSig<'tcx>,
) -> (DiagnosticStyledString, DiagnosticStyledString) {
- let sig1 = &self.normalize_fn_sig_for_diagnostic(*sig1);
- let sig2 = &self.normalize_fn_sig_for_diagnostic(*sig2);
+ let sig1 = &(self.normalize_fn_sig)(*sig1);
+ let sig2 = &(self.normalize_fn_sig)(*sig2);
let get_lifetimes = |sig| {
use rustc_hir::def::Namespace;
let num_display_types = consts_offset - regions_len;
for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() {
let i = i + regions_len;
- if ta1 == ta2 {
+ if ta1 == ta2 && !self.tcx.sess.verbose() {
values.0.push_normal("_");
values.1.push_normal("_");
} else {
let const_arguments = sub1.consts().zip(sub2.consts());
for (i, (ca1, ca2)) in const_arguments.enumerate() {
let i = i + consts_offset;
- if ca1 == ca2 {
+ if ca1 == ca2 && !self.tcx.sess.verbose() {
values.0.push_normal("_");
values.1.push_normal("_");
} else {
.map(|(mod_str, _)| mod_str.len() + separator_len)
.sum();
- debug!(
- "cmp: separator_len={}, split_idx={}, min_len={}",
- separator_len, split_idx, min_len
- );
+ debug!(?separator_len, ?split_idx, ?min_len, "cmp");
if split_idx >= min_len {
// paths are identical, highlight everything
} else {
let (common, uniq1) = t1_str.split_at(split_idx);
let (_, uniq2) = t2_str.split_at(split_idx);
- debug!("cmp: common={}, uniq1={}, uniq2={}", common, uniq1, uniq2);
+ debug!(?common, ?uniq1, ?uniq2, "cmp");
values.0.push_normal(common);
values.0.push_highlighted(uniq1);
(ty::FnPtr(sig1), ty::FnPtr(sig2)) => self.cmp_fn_sig(sig1, sig2),
_ => {
- if t1 == t2 {
+ if t1 == t2 && !self.tcx.sess.verbose() {
// The two types are the same, elide and don't highlight.
(DiagnosticStyledString::normal("_"), DiagnosticStyledString::normal("_"))
} else {
}
ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")),
};
- let vals = match self.values_str(values) {
- Some((expected, found)) => Some((expected, found)),
- None => {
- // Derived error. Cancel the emitter.
- // NOTE(eddyb) this was `.cancel()`, but `diag`
- // is borrowed, so we can't fully defuse it.
- diag.downgrade_to_delayed_bug();
- return;
- }
+ let Some(vals) = self.values_str(values) else {
+ // Derived error. Cancel the emitter.
+ // NOTE(eddyb) this was `.cancel()`, but `diag`
+ // is borrowed, so we can't fully defuse it.
+ diag.downgrade_to_delayed_bug();
+ return;
};
- (vals, exp_found, is_simple_error, Some(values))
+ (Some(vals), exp_found, is_simple_error, Some(values))
}
};
label_or_note(span, &terr.to_string());
}
- if let Some((expected, found)) = expected_found {
+ if let Some((expected, found, exp_p, found_p)) = expected_found {
let (expected_label, found_label, exp_found) = match exp_found {
Mismatch::Variable(ef) => (
ef.expected.prefix_string(self.tcx),
}
TypeError::Sorts(values) => {
let extra = expected == found;
- let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {
- (true, ty::Opaque(def_id, _)) => {
- let sm = self.tcx.sess.source_map();
- let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo());
- format!(
- " (opaque type at <{}:{}:{}>)",
- sm.filename_for_diagnostics(&pos.file.name),
- pos.line,
- pos.col.to_usize() + 1,
- )
- }
- (true, ty::Projection(proj))
- if self.tcx.def_kind(proj.item_def_id)
- == DefKind::ImplTraitPlaceholder =>
- {
- let sm = self.tcx.sess.source_map();
- let pos = sm.lookup_char_pos(self.tcx.def_span(proj.item_def_id).lo());
- format!(
- " (trait associated opaque type at <{}:{}:{}>)",
- sm.filename_for_diagnostics(&pos.file.name),
- pos.line,
- pos.col.to_usize() + 1,
- )
+ let sort_string = |ty: Ty<'tcx>, path: Option<PathBuf>| {
+ let mut s = match (extra, ty.kind()) {
+ (true, ty::Opaque(def_id, _)) => {
+ let sm = self.tcx.sess.source_map();
+ let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo());
+ format!(
+ " (opaque type at <{}:{}:{}>)",
+ sm.filename_for_diagnostics(&pos.file.name),
+ pos.line,
+ pos.col.to_usize() + 1,
+ )
+ }
+ (true, ty::Projection(proj))
+ if self.tcx.def_kind(proj.item_def_id)
+ == DefKind::ImplTraitPlaceholder =>
+ {
+ let sm = self.tcx.sess.source_map();
+ let pos = sm.lookup_char_pos(self.tcx.def_span(proj.item_def_id).lo());
+ format!(
+ " (trait associated opaque type at <{}:{}:{}>)",
+ sm.filename_for_diagnostics(&pos.file.name),
+ pos.line,
+ pos.col.to_usize() + 1,
+ )
+ }
+ (true, _) => format!(" ({})", ty.sort_string(self.tcx)),
+ (false, _) => "".to_string(),
+ };
+ if let Some(path) = path {
+ s.push_str(&format!(
+ "\nthe full type name has been written to '{}'",
+ path.display(),
+ ));
}
- (true, _) => format!(" ({})", ty.sort_string(self.tcx)),
- (false, _) => "".to_string(),
+ s
};
if !(values.expected.is_simple_text() && values.found.is_simple_text())
|| (exp_found.map_or(false, |ef| {
expected,
&found_label,
found,
- &sort_string(values.expected),
- &sort_string(values.found),
+ &sort_string(values.expected, exp_p),
+ &sort_string(values.found, found_p),
);
}
}
debug!(?diag);
}
- fn suggest_tuple_pattern(
- &self,
- cause: &ObligationCause<'tcx>,
- exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
- diag: &mut Diagnostic,
- ) {
- // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
- // some modifications due to that being in typeck and this being in infer.
- if let ObligationCauseCode::Pattern { .. } = cause.code() {
- if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
- let compatible_variants: Vec<_> = expected_adt
- .variants()
- .iter()
- .filter(|variant| {
- variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
- })
- .filter_map(|variant| {
- let sole_field = &variant.fields[0];
- let sole_field_ty = sole_field.ty(self.tcx, substs);
- if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
- let variant_path =
- with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
- // FIXME #56861: DRYer prelude filtering
- if let Some(path) = variant_path.strip_prefix("std::prelude::") {
- if let Some((_, path)) = path.split_once("::") {
- return Some(path.to_string());
- }
- }
- Some(variant_path)
- } else {
- None
- }
- })
- .collect();
- match &compatible_variants[..] {
- [] => {}
- [variant] => {
- diag.multipart_suggestion_verbose(
- &format!("try wrapping the pattern in `{}`", variant),
- vec![
- (cause.span.shrink_to_lo(), format!("{}(", variant)),
- (cause.span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- _ => {
- // More than one matching variant.
- diag.multipart_suggestions(
- &format!(
- "try wrapping the pattern in a variant of `{}`",
- self.tcx.def_path_str(expected_adt.did())
- ),
- compatible_variants.into_iter().map(|variant| {
- vec![
- (cause.span.shrink_to_lo(), format!("{}(", variant)),
- (cause.span.shrink_to_hi(), ")".to_string()),
- ]
- }),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- }
- }
-
- /// A possible error is to forget to add `.await` when using futures:
- ///
- /// ```compile_fail,E0308
- /// async fn make_u32() -> u32 {
- /// 22
- /// }
- ///
- /// fn take_u32(x: u32) {}
- ///
- /// async fn foo() {
- /// let x = make_u32();
- /// take_u32(x);
- /// }
- /// ```
- ///
- /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
- /// expected type. If this is the case, and we are inside of an async body, it suggests adding
- /// `.await` to the tail of the expression.
- fn suggest_await_on_expect_found(
- &self,
- cause: &ObligationCause<'tcx>,
- exp_span: Span,
- exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
- diag: &mut Diagnostic,
- ) {
- debug!(
- "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
- exp_span, exp_found.expected, exp_found.found,
- );
-
- if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() {
- return;
- }
-
- match (
- self.get_impl_future_output_ty(exp_found.expected),
- self.get_impl_future_output_ty(exp_found.found),
- ) {
- (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
- .code()
- {
- ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
- let then_span = self.find_block_span_from_hir_id(*then_id);
- diag.multipart_suggestion(
- "consider `await`ing on both `Future`s",
- vec![
- (then_span.shrink_to_hi(), ".await".to_string()),
- (exp_span.shrink_to_hi(), ".await".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
- prior_arms,
- ..
- }) => {
- if let [.., arm_span] = &prior_arms[..] {
- diag.multipart_suggestion(
- "consider `await`ing on both `Future`s",
- vec![
- (arm_span.shrink_to_hi(), ".await".to_string()),
- (exp_span.shrink_to_hi(), ".await".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- } else {
- diag.help("consider `await`ing on both `Future`s");
- }
- }
- _ => {
- diag.help("consider `await`ing on both `Future`s");
- }
- },
- (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
- diag.span_suggestion_verbose(
- exp_span.shrink_to_hi(),
- "consider `await`ing on the `Future`",
- ".await",
- Applicability::MaybeIncorrect,
- );
- }
- (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
- {
- ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
- diag.span_suggestion_verbose(
- then_span.shrink_to_hi(),
- "consider `await`ing on the `Future`",
- ".await",
- Applicability::MaybeIncorrect,
- );
- }
- ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
- let then_span = self.find_block_span_from_hir_id(*then_id);
- diag.span_suggestion_verbose(
- then_span.shrink_to_hi(),
- "consider `await`ing on the `Future`",
- ".await",
- Applicability::MaybeIncorrect,
- );
- }
- ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
- ref prior_arms,
- ..
- }) => {
- diag.multipart_suggestion_verbose(
- "consider `await`ing on the `Future`",
- prior_arms
- .iter()
- .map(|arm| (arm.shrink_to_hi(), ".await".to_string()))
- .collect(),
- Applicability::MaybeIncorrect,
- );
- }
- _ => {}
- },
- _ => {}
- }
- }
-
- fn suggest_accessing_field_where_appropriate(
- &self,
- cause: &ObligationCause<'tcx>,
- exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
- diag: &mut Diagnostic,
- ) {
- debug!(
- "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
- cause, exp_found
- );
- if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
- if expected_def.is_enum() {
- return;
- }
-
- if let Some((name, ty)) = expected_def
- .non_enum_variant()
- .fields
- .iter()
- .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
- .map(|field| (field.name, field.ty(self.tcx, expected_substs)))
- .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
- {
- if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
- let suggestion = if expected_def.is_struct() {
- format!("{}.{}", snippet, name)
- } else if expected_def.is_union() {
- format!("unsafe {{ {}.{} }}", snippet, name)
- } else {
- return;
- };
- diag.span_suggestion(
- span,
- &format!(
- "you might have meant to use field `{}` whose type is `{}`",
- name, ty
- ),
- suggestion,
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- }
- }
-
- /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
- /// suggests it.
- fn suggest_as_ref_where_appropriate(
- &self,
- span: Span,
- exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
- diag: &mut Diagnostic,
- ) {
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
- && let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
- {
- diag.span_suggestion(
- span,
- msg,
- // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
- format!("{}.as_ref()", snippet.trim_start_matches('&')),
- Applicability::MachineApplicable,
- );
- }
- }
-
- pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
- if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
- (expected.kind(), found.kind())
- {
- if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
- if exp_def == &found_def {
- let have_as_ref = &[
- (
- sym::Option,
- "you can convert from `&Option<T>` to `Option<&T>` using \
- `.as_ref()`",
- ),
- (
- sym::Result,
- "you can convert from `&Result<T, E>` to \
- `Result<&T, &E>` using `.as_ref()`",
- ),
- ];
- if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
- self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
- }) {
- let mut show_suggestion = true;
- for (exp_ty, found_ty) in
- iter::zip(exp_substs.types(), found_substs.types())
- {
- match *exp_ty.kind() {
- ty::Ref(_, exp_ty, _) => {
- match (exp_ty.kind(), found_ty.kind()) {
- (_, ty::Param(_))
- | (_, ty::Infer(_))
- | (ty::Param(_), _)
- | (ty::Infer(_), _) => {}
- _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
- _ => show_suggestion = false,
- };
- }
- ty::Param(_) | ty::Infer(_) => {}
- _ => show_suggestion = false,
- }
- }
- if show_suggestion {
- return Some(*msg);
- }
- }
- }
- }
- }
- None
- }
-
pub fn report_and_explain_type_error(
&self,
trace: TypeTrace<'tcx>,
let code = trace.cause.code();
if let &MatchExpressionArm(box MatchExpressionArmCause { source, .. }) = code
&& let hir::MatchSource::TryDesugar = source
- && let Some((expected_ty, found_ty)) = self.values_str(trace.values)
+ && let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values)
{
err.note(&format!(
"`?` operator cannot convert from `{}` to `{}`",
diag
}
- /// Try to find code with pattern `if Some(..) = expr`
- /// use a `visitor` to mark the `if` which its span contains given error span,
- /// and then try to find a assignment in the `cond` part, which span is equal with error span
- fn suggest_let_for_letchains(
- &self,
- err: &mut Diagnostic,
- cause: &ObligationCause<'_>,
- span: Span,
- ) {
- let hir = self.tcx.hir();
- let fn_hir_id = hir.get_parent_node(cause.body_id);
- if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
- let hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Fn(_sig, _, body_id), ..
- }) = node {
- let body = hir.body(*body_id);
-
- /// Find the if expression with given span
- struct IfVisitor {
- pub result: bool,
- pub found_if: bool,
- pub err_span: Span,
- }
-
- impl<'v> Visitor<'v> for IfVisitor {
- fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
- if self.result { return; }
- match ex.kind {
- hir::ExprKind::If(cond, _, _) => {
- self.found_if = true;
- walk_expr(self, cond);
- self.found_if = false;
- }
- _ => walk_expr(self, ex),
- }
- }
-
- fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
- if let hir::StmtKind::Local(hir::Local {
- span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
- }) = &ex.kind
- && self.found_if
- && span.eq(&self.err_span) {
- self.result = true;
- }
- walk_stmt(self, ex);
- }
-
- fn visit_body(&mut self, body: &'v hir::Body<'v>) {
- hir::intravisit::walk_body(self, body);
- }
- }
-
- let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
- visitor.visit_body(&body);
- if visitor.result {
- err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
- }
- }
- }
-
fn emit_tuple_wrap_err(
&self,
err: &mut Diagnostic,
fn values_str(
&self,
values: ValuePairs<'tcx>,
- ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
+ ) -> Option<(DiagnosticStyledString, DiagnosticStyledString, Option<PathBuf>, Option<PathBuf>)>
+ {
match values {
infer::Regions(exp_found) => self.expected_found_str(exp_found),
infer::Terms(exp_found) => self.expected_found_str_term(exp_found),
found: exp_found.found.print_only_trait_path(),
};
match self.expected_found_str(pretty_exp_found) {
- Some((expected, found)) if expected == found => {
+ Some((expected, found, _, _)) if expected == found => {
self.expected_found_str(exp_found)
}
ret => ret,
found: exp_found.found.print_only_trait_path(),
};
match self.expected_found_str(pretty_exp_found) {
- Some((expected, found)) if expected == found => {
+ Some((expected, found, _, _)) if expected == found => {
self.expected_found_str(exp_found)
}
ret => ret,
fn expected_found_str_term(
&self,
exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>,
- ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
+ ) -> Option<(DiagnosticStyledString, DiagnosticStyledString, Option<PathBuf>, Option<PathBuf>)>
+ {
let exp_found = self.resolve_vars_if_possible(exp_found);
if exp_found.references_error() {
return None;
}
Some(match (exp_found.expected.unpack(), exp_found.found.unpack()) {
- (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => self.cmp(expected, found),
+ (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {
+ let (mut exp, mut fnd) = self.cmp(expected, found);
+ // Use the terminal width as the basis to determine when to compress the printed
+ // out type, but give ourselves some leeway to avoid ending up creating a file for
+ // a type that is somewhat shorter than the path we'd write to.
+ let len = self.tcx.sess().diagnostic_width() + 40;
+ let exp_s = exp.content();
+ let fnd_s = fnd.content();
+ let mut exp_p = None;
+ let mut fnd_p = None;
+ if exp_s.len() > len {
+ let (exp_s, exp_path) = self.tcx.short_ty_string(expected);
+ exp = DiagnosticStyledString::highlighted(exp_s);
+ exp_p = exp_path;
+ }
+ if fnd_s.len() > len {
+ let (fnd_s, fnd_path) = self.tcx.short_ty_string(found);
+ fnd = DiagnosticStyledString::highlighted(fnd_s);
+ fnd_p = fnd_path;
+ }
+ (exp, fnd, exp_p, fnd_p)
+ }
_ => (
DiagnosticStyledString::highlighted(exp_found.expected.to_string()),
DiagnosticStyledString::highlighted(exp_found.found.to_string()),
+ None,
+ None,
),
})
}
fn expected_found_str<T: fmt::Display + TypeFoldable<'tcx>>(
&self,
exp_found: ty::error::ExpectedFound<T>,
- ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
+ ) -> Option<(DiagnosticStyledString, DiagnosticStyledString, Option<PathBuf>, Option<PathBuf>)>
+ {
let exp_found = self.resolve_vars_if_possible(exp_found);
if exp_found.references_error() {
return None;
Some((
DiagnosticStyledString::highlighted(exp_found.expected.to_string()),
DiagnosticStyledString::highlighted(exp_found.found.to_string()),
+ None,
+ None,
))
}
debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
- if let (&infer::Subtype(ref sup_trace), &infer::Subtype(ref sub_trace)) =
- (&sup_origin, &sub_origin)
+ if let infer::Subtype(ref sup_trace) = sup_origin
+ && let infer::Subtype(ref sub_trace) = sub_origin
+ && let Some((sup_expected, sup_found, _, _)) = self.values_str(sup_trace.values)
+ && let Some((sub_expected, sub_found, _, _)) = self.values_str(sub_trace.values)
+ && sub_expected == sup_expected
+ && sub_found == sup_found
{
- debug!("report_sub_sup_conflict: sup_trace={:?}", sup_trace);
- debug!("report_sub_sup_conflict: sub_trace={:?}", sub_trace);
- debug!("report_sub_sup_conflict: sup_trace.values={:?}", sup_trace.values);
- debug!("report_sub_sup_conflict: sub_trace.values={:?}", sub_trace.values);
-
- if let (Some((sup_expected, sup_found)), Some((sub_expected, sub_found))) =
- (self.values_str(sup_trace.values), self.values_str(sub_trace.values))
- {
- if sub_expected == sup_expected && sub_found == sup_found {
- note_and_explain_region(
- self.tcx,
- &mut err,
- "...but the lifetime must also be valid for ",
- sub_region,
- "...",
- None,
- );
- err.span_note(
- sup_trace.cause.span,
- &format!("...so that the {}", sup_trace.cause.as_requirement_str()),
- );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...but the lifetime must also be valid for ",
+ sub_region,
+ "...",
+ None,
+ );
+ err.span_note(
+ sup_trace.cause.span,
+ &format!("...so that the {}", sup_trace.cause.as_requirement_str()),
+ );
- err.note_expected_found(&"", sup_expected, &"", sup_found);
- err.emit();
- return;
- }
- }
+ err.note_expected_found(&"", sup_expected, &"", sup_found);
+ err.emit();
+ return;
}
self.note_region_origin(&mut err, &sup_origin);
}
}
}
-
-impl<'tcx> TypeErrCtxt<'_, 'tcx> {
- /// Be helpful when the user wrote `{... expr; }` and taking the `;` off
- /// is enough to fix the error.
- pub fn could_remove_semicolon(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected_ty: Ty<'tcx>,
- ) -> Option<(Span, StatementAsExpression)> {
- let blk = blk.innermost_block();
- // Do not suggest if we have a tail expr.
- if blk.expr.is_some() {
- return None;
- }
- let last_stmt = blk.stmts.last()?;
- let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
- return None;
- };
- let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?;
- let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
- _ if last_expr_ty.references_error() => return None,
- _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
- StatementAsExpression::CorrectType
- }
- (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
- if last_def_id == exp_def_id =>
- {
- StatementAsExpression::CorrectType
- }
- (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
- debug!(
- "both opaque, likely future {:?} {:?} {:?} {:?}",
- last_def_id, last_bounds, exp_def_id, exp_bounds
- );
-
- let last_local_id = last_def_id.as_local()?;
- let exp_local_id = exp_def_id.as_local()?;
-
- match (
- &self.tcx.hir().expect_item(last_local_id).kind,
- &self.tcx.hir().expect_item(exp_local_id).kind,
- ) {
- (
- hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
- hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
- ) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
- match (left, right) {
- (
- hir::GenericBound::Trait(tl, ml),
- hir::GenericBound::Trait(tr, mr),
- ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
- && ml == mr =>
- {
- true
- }
- (
- hir::GenericBound::LangItemTrait(langl, _, _, argsl),
- hir::GenericBound::LangItemTrait(langr, _, _, argsr),
- ) if langl == langr => {
- // FIXME: consider the bounds!
- debug!("{:?} {:?}", argsl, argsr);
- true
- }
- _ => false,
- }
- }) =>
- {
- StatementAsExpression::NeedsBoxing
- }
- _ => StatementAsExpression::CorrectType,
- }
- }
- _ => return None,
- };
- let span = if last_stmt.span.from_expansion() {
- let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
- self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
- } else {
- last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
- };
- Some((span, needs_box))
- }
-
- /// Suggest returning a local binding with a compatible type if the block
- /// has no return expression.
- pub fn consider_returning_binding(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected_ty: Ty<'tcx>,
- err: &mut Diagnostic,
- ) -> bool {
- let blk = blk.innermost_block();
- // Do not suggest if we have a tail expr.
- if blk.expr.is_some() {
- return false;
- }
- let mut shadowed = FxIndexSet::default();
- let mut candidate_idents = vec![];
- let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
- if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
- && let Some(pat_ty) = self
- .typeck_results
- .as_ref()
- .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
- {
- let pat_ty = self.resolve_vars_if_possible(pat_ty);
- if self.same_type_modulo_infer(pat_ty, expected_ty)
- && !(pat_ty, expected_ty).references_error()
- && shadowed.insert(ident.name)
- {
- candidate_idents.push((*ident, pat_ty));
- }
- }
- true
- };
-
- let hir = self.tcx.hir();
- for stmt in blk.stmts.iter().rev() {
- let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
- local.pat.walk(&mut find_compatible_candidates);
- }
- match hir.find(hir.get_parent_node(blk.hir_id)) {
- Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
- match hir.find(hir.get_parent_node(*hir_id)) {
- Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
- pat.walk(&mut find_compatible_candidates);
- }
- Some(
- hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
- | hir::Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Fn(_, body),
- ..
- })
- | hir::Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
- ..
- })
- | hir::Node::Expr(hir::Expr {
- kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
- ..
- }),
- ) => {
- for param in hir.body(*body).params {
- param.pat.walk(&mut find_compatible_candidates);
- }
- }
- Some(hir::Node::Expr(hir::Expr {
- kind:
- hir::ExprKind::If(
- hir::Expr { kind: hir::ExprKind::Let(let_), .. },
- then_block,
- _,
- ),
- ..
- })) if then_block.hir_id == *hir_id => {
- let_.pat.walk(&mut find_compatible_candidates);
- }
- _ => {}
- }
- }
- _ => {}
- }
-
- match &candidate_idents[..] {
- [(ident, _ty)] => {
- let sm = self.tcx.sess.source_map();
- if let Some(stmt) = blk.stmts.last() {
- let stmt_span = sm.stmt_span(stmt.span, blk.span);
- let sugg = if sm.is_multiline(blk.span)
- && let Some(spacing) = sm.indentation_before(stmt_span)
- {
- format!("\n{spacing}{ident}")
- } else {
- format!(" {ident}")
- };
- err.span_suggestion_verbose(
- stmt_span.shrink_to_hi(),
- format!("consider returning the local binding `{ident}`"),
- sugg,
- Applicability::MaybeIncorrect,
- );
- } else {
- let sugg = if sm.is_multiline(blk.span)
- && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
- {
- format!("\n{spacing} {ident}\n{spacing}")
- } else {
- format!(" {ident} ")
- };
- let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
- err.span_suggestion_verbose(
- sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
- format!("consider returning the local binding `{ident}`"),
- sugg,
- Applicability::MaybeIncorrect,
- );
- }
- true
- }
- values if (1..3).contains(&values.len()) => {
- let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
- err.span_note(spans, "consider returning one of these bindings");
- true
- }
- _ => false,
- }
- }
-}
infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
span: trace.cause.span,
requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
- expected_found: self.values_str(trace.values),
+ expected_found: self.values_str(trace.values).map(|(e, f, _, _)| (e, f)),
}
.add_to_diagnostic(err),
infer::Reborrow(span) => {
--- /dev/null
+use crate::errors::RegionOriginNote;
+use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
+use crate::infer::{self, SubregionOrigin};
+use rustc_errors::{
+ fluent, struct_span_err, AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+};
+use rustc_middle::traits::ObligationCauseCode;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::{self, Region};
+
+use super::ObligationCauseAsDiagArg;
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+ pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) {
+ match *origin {
+ infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
+ span: trace.cause.span,
+ requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
+ expected_found: self.values_str(trace.values),
+ }
+ .add_to_diagnostic(err),
+ infer::Reborrow(span) => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diagnostic(err)
+ }
+ infer::ReborrowUpvar(span, ref upvar_id) => {
+ let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
+ RegionOriginNote::WithName {
+ span,
+ msg: fluent::infer_reborrow,
+ name: &var_name.to_string(),
+ continues: false,
+ }
+ .add_to_diagnostic(err);
+ }
+ infer::RelateObjectBound(span) => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
+ .add_to_diagnostic(err);
+ }
+ infer::DataBorrowed(ty, span) => {
+ RegionOriginNote::WithName {
+ span,
+ msg: fluent::infer_data_borrowed,
+ name: &self.ty_to_string(ty),
+ continues: false,
+ }
+ .add_to_diagnostic(err);
+ }
+ infer::ReferenceOutlivesReferent(ty, span) => {
+ RegionOriginNote::WithName {
+ span,
+ msg: fluent::infer_reference_outlives_referent,
+ name: &self.ty_to_string(ty),
+ continues: false,
+ }
+ .add_to_diagnostic(err);
+ }
+ infer::RelateParamBound(span, ty, opt_span) => {
+ RegionOriginNote::WithName {
+ span,
+ msg: fluent::infer_relate_param_bound,
+ name: &self.ty_to_string(ty),
+ continues: opt_span.is_some(),
+ }
+ .add_to_diagnostic(err);
+ if let Some(span) = opt_span {
+ RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
+ .add_to_diagnostic(err);
+ }
+ }
+ infer::RelateRegionParamBound(span) => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
+ .add_to_diagnostic(err);
+ }
+ infer::CompareImplItemObligation { span, .. } => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
+ .add_to_diagnostic(err);
+ }
+ infer::CheckAssociatedTypeBounds { ref parent, .. } => {
+ self.note_region_origin(err, &parent);
+ }
+ infer::AscribeUserTypeProvePredicate(span) => {
+ RegionOriginNote::Plain {
+ span,
+ msg: fluent::infer_ascribe_user_type_prove_predicate,
+ }
+ .add_to_diagnostic(err);
+ }
+ }
+ }
+
+ pub(super) fn report_concrete_failure(
+ &self,
+ origin: SubregionOrigin<'tcx>,
+ sub: Region<'tcx>,
+ sup: Region<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ match origin {
+ infer::Subtype(box trace) => {
+ let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
+ let mut err = self.report_and_explain_type_error(trace, terr);
+ match (*sub, *sup) {
+ (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
+ (ty::RePlaceholder(_), _) => {
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "",
+ sup,
+ " doesn't meet the lifetime requirements",
+ None,
+ );
+ }
+ (_, ty::RePlaceholder(_)) => {
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "the required lifetime does not necessarily outlive ",
+ sub,
+ "",
+ None,
+ );
+ }
+ _ => {
+ note_and_explain_region(self.tcx, &mut err, "", sup, "...", None);
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...does not necessarily outlive ",
+ sub,
+ "",
+ None,
+ );
+ }
+ }
+ err
+ }
+ infer::Reborrow(span) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0312,
+ "lifetime of reference outlives lifetime of borrowed content..."
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...the reference is valid for ",
+ sub,
+ "...",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...but the borrowed content is only valid for ",
+ sup,
+ "",
+ None,
+ );
+ err
+ }
+ infer::ReborrowUpvar(span, ref upvar_id) => {
+ let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0313,
+ "lifetime of borrowed pointer outlives lifetime of captured variable `{}`...",
+ var_name
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...the borrowed pointer is valid for ",
+ sub,
+ "...",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ &format!("...but `{}` is only valid for ", var_name),
+ sup,
+ "",
+ None,
+ );
+ err
+ }
+ infer::RelateObjectBound(span) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0476,
+ "lifetime of the source pointer does not outlive lifetime bound of the \
+ object type"
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "object type is valid for ",
+ sub,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "source pointer is only valid for ",
+ sup,
+ "",
+ None,
+ );
+ err
+ }
+ infer::RelateParamBound(span, ty, opt_span) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0477,
+ "the type `{}` does not fulfill the required lifetime",
+ self.ty_to_string(ty)
+ );
+ match *sub {
+ ty::ReStatic => note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "type must satisfy ",
+ sub,
+ if opt_span.is_some() { " as required by this binding" } else { "" },
+ opt_span,
+ ),
+ _ => note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "type must outlive ",
+ sub,
+ if opt_span.is_some() { " as required by this binding" } else { "" },
+ opt_span,
+ ),
+ }
+ err
+ }
+ infer::RelateRegionParamBound(span) => {
+ let mut err =
+ struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "lifetime parameter instantiated with ",
+ sup,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "but lifetime parameter must outlive ",
+ sub,
+ "",
+ None,
+ );
+ err
+ }
+ infer::DataBorrowed(ty, span) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0490,
+ "a value of type `{}` is borrowed for too long",
+ self.ty_to_string(ty)
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "the type is valid for ",
+ sub,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "but the borrow lasts for ",
+ sup,
+ "",
+ None,
+ );
+ err
+ }
+ infer::ReferenceOutlivesReferent(ty, span) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0491,
+ "in type `{}`, reference has a longer lifetime than the data it references",
+ self.ty_to_string(ty)
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "the pointer is valid for ",
+ sub,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "but the referenced data is only valid for ",
+ sup,
+ "",
+ None,
+ );
+ err
+ }
+ infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => self
+ .report_extra_impl_obligation(
+ span,
+ impl_item_def_id,
+ trait_item_def_id,
+ &format!("`{}: {}`", sup, sub),
+ ),
+ infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
+ let mut err = self.report_concrete_failure(*parent, sub, sup);
+
+ let trait_item_span = self.tcx.def_span(trait_item_def_id);
+ let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
+ err.span_label(
+ trait_item_span,
+ format!("definition of `{}` from trait", item_name),
+ );
+
+ let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id);
+ let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id);
+
+ let impl_predicates: rustc_data_structures::fx::FxHashSet<_> =
+ impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect();
+ let clauses: Vec<_> = trait_predicates
+ .predicates
+ .into_iter()
+ .filter(|&(pred, _)| !impl_predicates.contains(pred))
+ .map(|(pred, _)| format!("{}", pred))
+ .collect();
+
+ if !clauses.is_empty() {
+ let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap();
+ let where_clause_span = generics.tail_span_for_predicate_suggestion();
+
+ let suggestion = format!(
+ "{} {}",
+ generics.add_where_or_trailing_comma(),
+ clauses.join(", "),
+ );
+ err.span_suggestion(
+ where_clause_span,
+ &format!(
+ "try copying {} from the trait",
+ if clauses.len() > 1 { "these clauses" } else { "this clause" }
+ ),
+ suggestion,
+ rustc_errors::Applicability::MaybeIncorrect,
+ );
+ }
+
+ err
+ }
+ infer::AscribeUserTypeProvePredicate(span) => {
+ let mut err =
+ struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "lifetime instantiated with ",
+ sup,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "but lifetime must outlive ",
+ sub,
+ "",
+ None,
+ );
+ err
+ }
+ }
+ }
+
+ pub(super) fn report_placeholder_failure(
+ &self,
+ placeholder_origin: SubregionOrigin<'tcx>,
+ sub: Region<'tcx>,
+ sup: Region<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ // I can't think how to do better than this right now. -nikomatsakis
+ debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
+ match placeholder_origin {
+ infer::Subtype(box ref trace)
+ if matches!(
+ &trace.cause.code().peel_derives(),
+ ObligationCauseCode::BindingObligation(..)
+ | ObligationCauseCode::ExprBindingObligation(..)
+ ) =>
+ {
+ // Hack to get around the borrow checker because trace.cause has an `Rc`.
+ if let ObligationCauseCode::BindingObligation(_, span)
+ | ObligationCauseCode::ExprBindingObligation(_, span, ..) =
+ &trace.cause.code().peel_derives()
+ {
+ let span = *span;
+ let mut err = self.report_concrete_failure(placeholder_origin, sub, sup);
+ err.span_note(span, "the lifetime requirement is introduced here");
+ err
+ } else {
+ unreachable!()
+ }
+ }
+ infer::Subtype(box trace) => {
+ let terr = TypeError::RegionsPlaceholderMismatch;
+ return self.report_and_explain_type_error(trace, terr);
+ }
+ _ => return self.report_concrete_failure(placeholder_origin, sub, sup),
+ }
+ }
+}
--- /dev/null
+use hir::def::CtorKind;
+use hir::intravisit::{walk_expr, walk_stmt, Visitor};
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::{Applicability, Diagnostic};
+use rustc_hir as hir;
+use rustc_middle::traits::{
+ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
+ StatementAsExpression,
+};
+use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_middle::ty::{self as ty, Ty, TypeVisitable};
+use rustc_span::{sym, BytePos, Span};
+
+use crate::errors::SuggAddLetForLetChains;
+
+use super::TypeErrCtxt;
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+ pub(super) fn suggest_remove_semi_or_return_binding(
+ &self,
+ err: &mut Diagnostic,
+ first_id: Option<hir::HirId>,
+ first_ty: Ty<'tcx>,
+ first_span: Span,
+ second_id: Option<hir::HirId>,
+ second_ty: Ty<'tcx>,
+ second_span: Span,
+ ) {
+ let remove_semicolon = [
+ (first_id, self.resolve_vars_if_possible(second_ty)),
+ (second_id, self.resolve_vars_if_possible(first_ty)),
+ ]
+ .into_iter()
+ .find_map(|(id, ty)| {
+ let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None };
+ self.could_remove_semicolon(blk, ty)
+ });
+ match remove_semicolon {
+ Some((sp, StatementAsExpression::NeedsBoxing)) => {
+ err.multipart_suggestion(
+ "consider removing this semicolon and boxing the expressions",
+ vec![
+ (first_span.shrink_to_lo(), "Box::new(".to_string()),
+ (first_span.shrink_to_hi(), ")".to_string()),
+ (second_span.shrink_to_lo(), "Box::new(".to_string()),
+ (second_span.shrink_to_hi(), ")".to_string()),
+ (sp, String::new()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ }
+ Some((sp, StatementAsExpression::CorrectType)) => {
+ err.span_suggestion_short(
+ sp,
+ "consider removing this semicolon",
+ "",
+ Applicability::MachineApplicable,
+ );
+ }
+ None => {
+ for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
+ if let Some(id) = id
+ && let hir::Node::Block(blk) = self.tcx.hir().get(id)
+ && self.consider_returning_binding(blk, ty, err)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ pub(super) fn suggest_boxing_for_return_impl_trait(
+ &self,
+ err: &mut Diagnostic,
+ return_sp: Span,
+ arm_spans: impl Iterator<Item = Span>,
+ ) {
+ err.multipart_suggestion(
+ "you could change the return type to be a boxed trait object",
+ vec![
+ (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
+ (return_sp.shrink_to_hi(), ">".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ let sugg = arm_spans
+ .flat_map(|sp| {
+ [(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
+ .into_iter()
+ })
+ .collect::<Vec<_>>();
+ err.multipart_suggestion(
+ "if you change the return type to expect trait objects, box the returned expressions",
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ pub(super) fn suggest_tuple_pattern(
+ &self,
+ cause: &ObligationCause<'tcx>,
+ exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+ diag: &mut Diagnostic,
+ ) {
+ // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
+ // some modifications due to that being in typeck and this being in infer.
+ if let ObligationCauseCode::Pattern { .. } = cause.code() {
+ if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
+ let compatible_variants: Vec<_> = expected_adt
+ .variants()
+ .iter()
+ .filter(|variant| {
+ variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
+ })
+ .filter_map(|variant| {
+ let sole_field = &variant.fields[0];
+ let sole_field_ty = sole_field.ty(self.tcx, substs);
+ if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
+ let variant_path =
+ with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
+ // FIXME #56861: DRYer prelude filtering
+ if let Some(path) = variant_path.strip_prefix("std::prelude::") {
+ if let Some((_, path)) = path.split_once("::") {
+ return Some(path.to_string());
+ }
+ }
+ Some(variant_path)
+ } else {
+ None
+ }
+ })
+ .collect();
+ match &compatible_variants[..] {
+ [] => {}
+ [variant] => {
+ diag.multipart_suggestion_verbose(
+ &format!("try wrapping the pattern in `{}`", variant),
+ vec![
+ (cause.span.shrink_to_lo(), format!("{}(", variant)),
+ (cause.span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {
+ // More than one matching variant.
+ diag.multipart_suggestions(
+ &format!(
+ "try wrapping the pattern in a variant of `{}`",
+ self.tcx.def_path_str(expected_adt.did())
+ ),
+ compatible_variants.into_iter().map(|variant| {
+ vec![
+ (cause.span.shrink_to_lo(), format!("{}(", variant)),
+ (cause.span.shrink_to_hi(), ")".to_string()),
+ ]
+ }),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /// A possible error is to forget to add `.await` when using futures:
+ ///
+ /// ```compile_fail,E0308
+ /// async fn make_u32() -> u32 {
+ /// 22
+ /// }
+ ///
+ /// fn take_u32(x: u32) {}
+ ///
+ /// async fn foo() {
+ /// let x = make_u32();
+ /// take_u32(x);
+ /// }
+ /// ```
+ ///
+ /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
+ /// expected type. If this is the case, and we are inside of an async body, it suggests adding
+ /// `.await` to the tail of the expression.
+ pub(super) fn suggest_await_on_expect_found(
+ &self,
+ cause: &ObligationCause<'tcx>,
+ exp_span: Span,
+ exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+ diag: &mut Diagnostic,
+ ) {
+ debug!(
+ "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
+ exp_span, exp_found.expected, exp_found.found,
+ );
+
+ if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() {
+ return;
+ }
+
+ match (
+ self.get_impl_future_output_ty(exp_found.expected),
+ self.get_impl_future_output_ty(exp_found.found),
+ ) {
+ (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
+ .code()
+ {
+ ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
+ let then_span = self.find_block_span_from_hir_id(*then_id);
+ diag.multipart_suggestion(
+ "consider `await`ing on both `Future`s",
+ vec![
+ (then_span.shrink_to_hi(), ".await".to_string()),
+ (exp_span.shrink_to_hi(), ".await".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+ prior_arms,
+ ..
+ }) => {
+ if let [.., arm_span] = &prior_arms[..] {
+ diag.multipart_suggestion(
+ "consider `await`ing on both `Future`s",
+ vec![
+ (arm_span.shrink_to_hi(), ".await".to_string()),
+ (exp_span.shrink_to_hi(), ".await".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ diag.help("consider `await`ing on both `Future`s");
+ }
+ }
+ _ => {
+ diag.help("consider `await`ing on both `Future`s");
+ }
+ },
+ (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
+ diag.span_suggestion_verbose(
+ exp_span.shrink_to_hi(),
+ "consider `await`ing on the `Future`",
+ ".await",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
+ {
+ ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
+ diag.span_suggestion_verbose(
+ then_span.shrink_to_hi(),
+ "consider `await`ing on the `Future`",
+ ".await",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
+ let then_span = self.find_block_span_from_hir_id(*then_id);
+ diag.span_suggestion_verbose(
+ then_span.shrink_to_hi(),
+ "consider `await`ing on the `Future`",
+ ".await",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+ ref prior_arms,
+ ..
+ }) => {
+ diag.multipart_suggestion_verbose(
+ "consider `await`ing on the `Future`",
+ prior_arms
+ .iter()
+ .map(|arm| (arm.shrink_to_hi(), ".await".to_string()))
+ .collect(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {}
+ },
+ _ => {}
+ }
+ }
+
+ pub(super) fn suggest_accessing_field_where_appropriate(
+ &self,
+ cause: &ObligationCause<'tcx>,
+ exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+ diag: &mut Diagnostic,
+ ) {
+ debug!(
+ "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
+ cause, exp_found
+ );
+ if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
+ if expected_def.is_enum() {
+ return;
+ }
+
+ if let Some((name, ty)) = expected_def
+ .non_enum_variant()
+ .fields
+ .iter()
+ .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
+ .map(|field| (field.name, field.ty(self.tcx, expected_substs)))
+ .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
+ {
+ if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
+ if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+ let suggestion = if expected_def.is_struct() {
+ format!("{}.{}", snippet, name)
+ } else if expected_def.is_union() {
+ format!("unsafe {{ {}.{} }}", snippet, name)
+ } else {
+ return;
+ };
+ diag.span_suggestion(
+ span,
+ &format!(
+ "you might have meant to use field `{}` whose type is `{}`",
+ name, ty
+ ),
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
+ /// suggests it.
+ pub(super) fn suggest_as_ref_where_appropriate(
+ &self,
+ span: Span,
+ exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+ diag: &mut Diagnostic,
+ ) {
+ if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+ && let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
+ {
+ diag.span_suggestion(
+ span,
+ msg,
+ // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
+ format!("{}.as_ref()", snippet.trim_start_matches('&')),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
+ pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
+ if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
+ (expected.kind(), found.kind())
+ {
+ if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
+ if exp_def == &found_def {
+ let have_as_ref = &[
+ (
+ sym::Option,
+ "you can convert from `&Option<T>` to `Option<&T>` using \
+ `.as_ref()`",
+ ),
+ (
+ sym::Result,
+ "you can convert from `&Result<T, E>` to \
+ `Result<&T, &E>` using `.as_ref()`",
+ ),
+ ];
+ if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
+ self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
+ }) {
+ let mut show_suggestion = true;
+ for (exp_ty, found_ty) in
+ std::iter::zip(exp_substs.types(), found_substs.types())
+ {
+ match *exp_ty.kind() {
+ ty::Ref(_, exp_ty, _) => {
+ match (exp_ty.kind(), found_ty.kind()) {
+ (_, ty::Param(_))
+ | (_, ty::Infer(_))
+ | (ty::Param(_), _)
+ | (ty::Infer(_), _) => {}
+ _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
+ _ => show_suggestion = false,
+ };
+ }
+ ty::Param(_) | ty::Infer(_) => {}
+ _ => show_suggestion = false,
+ }
+ }
+ if show_suggestion {
+ return Some(*msg);
+ }
+ }
+ }
+ }
+ }
+ None
+ }
+
+ /// Try to find code with pattern `if Some(..) = expr`
+ /// use a `visitor` to mark the `if` which its span contains given error span,
+ /// and then try to find a assignment in the `cond` part, which span is equal with error span
+ pub(super) fn suggest_let_for_letchains(
+ &self,
+ err: &mut Diagnostic,
+ cause: &ObligationCause<'_>,
+ span: Span,
+ ) {
+ let hir = self.tcx.hir();
+ let fn_hir_id = hir.get_parent_node(cause.body_id);
+ if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
+ let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Fn(_sig, _, body_id), ..
+ }) = node {
+ let body = hir.body(*body_id);
+
+ /// Find the if expression with given span
+ struct IfVisitor {
+ pub result: bool,
+ pub found_if: bool,
+ pub err_span: Span,
+ }
+
+ impl<'v> Visitor<'v> for IfVisitor {
+ fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+ if self.result { return; }
+ match ex.kind {
+ hir::ExprKind::If(cond, _, _) => {
+ self.found_if = true;
+ walk_expr(self, cond);
+ self.found_if = false;
+ }
+ _ => walk_expr(self, ex),
+ }
+ }
+
+ fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
+ if let hir::StmtKind::Local(hir::Local {
+ span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
+ }) = &ex.kind
+ && self.found_if
+ && span.eq(&self.err_span) {
+ self.result = true;
+ }
+ walk_stmt(self, ex);
+ }
+
+ fn visit_body(&mut self, body: &'v hir::Body<'v>) {
+ hir::intravisit::walk_body(self, body);
+ }
+ }
+
+ let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
+ visitor.visit_body(&body);
+ if visitor.result {
+ err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
+ }
+ }
+ }
+}
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+ /// Be helpful when the user wrote `{... expr; }` and taking the `;` off
+ /// is enough to fix the error.
+ pub fn could_remove_semicolon(
+ &self,
+ blk: &'tcx hir::Block<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) -> Option<(Span, StatementAsExpression)> {
+ let blk = blk.innermost_block();
+ // Do not suggest if we have a tail expr.
+ if blk.expr.is_some() {
+ return None;
+ }
+ let last_stmt = blk.stmts.last()?;
+ let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
+ return None;
+ };
+ let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?;
+ let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
+ _ if last_expr_ty.references_error() => return None,
+ _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
+ StatementAsExpression::CorrectType
+ }
+ (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
+ if last_def_id == exp_def_id =>
+ {
+ StatementAsExpression::CorrectType
+ }
+ (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
+ debug!(
+ "both opaque, likely future {:?} {:?} {:?} {:?}",
+ last_def_id, last_bounds, exp_def_id, exp_bounds
+ );
+
+ let last_local_id = last_def_id.as_local()?;
+ let exp_local_id = exp_def_id.as_local()?;
+
+ match (
+ &self.tcx.hir().expect_item(last_local_id).kind,
+ &self.tcx.hir().expect_item(exp_local_id).kind,
+ ) {
+ (
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
+ ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
+ match (left, right) {
+ (
+ hir::GenericBound::Trait(tl, ml),
+ hir::GenericBound::Trait(tr, mr),
+ ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
+ && ml == mr =>
+ {
+ true
+ }
+ (
+ hir::GenericBound::LangItemTrait(langl, _, _, argsl),
+ hir::GenericBound::LangItemTrait(langr, _, _, argsr),
+ ) if langl == langr => {
+ // FIXME: consider the bounds!
+ debug!("{:?} {:?}", argsl, argsr);
+ true
+ }
+ _ => false,
+ }
+ }) =>
+ {
+ StatementAsExpression::NeedsBoxing
+ }
+ _ => StatementAsExpression::CorrectType,
+ }
+ }
+ _ => return None,
+ };
+ let span = if last_stmt.span.from_expansion() {
+ let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
+ self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
+ } else {
+ last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
+ };
+ Some((span, needs_box))
+ }
+
+ /// Suggest returning a local binding with a compatible type if the block
+ /// has no return expression.
+ pub fn consider_returning_binding(
+ &self,
+ blk: &'tcx hir::Block<'tcx>,
+ expected_ty: Ty<'tcx>,
+ err: &mut Diagnostic,
+ ) -> bool {
+ let blk = blk.innermost_block();
+ // Do not suggest if we have a tail expr.
+ if blk.expr.is_some() {
+ return false;
+ }
+ let mut shadowed = FxIndexSet::default();
+ let mut candidate_idents = vec![];
+ let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
+ if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
+ && let Some(pat_ty) = self
+ .typeck_results
+ .as_ref()
+ .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
+ {
+ let pat_ty = self.resolve_vars_if_possible(pat_ty);
+ if self.same_type_modulo_infer(pat_ty, expected_ty)
+ && !(pat_ty, expected_ty).references_error()
+ && shadowed.insert(ident.name)
+ {
+ candidate_idents.push((*ident, pat_ty));
+ }
+ }
+ true
+ };
+
+ let hir = self.tcx.hir();
+ for stmt in blk.stmts.iter().rev() {
+ let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
+ local.pat.walk(&mut find_compatible_candidates);
+ }
+ match hir.find(hir.get_parent_node(blk.hir_id)) {
+ Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
+ match hir.find(hir.get_parent_node(*hir_id)) {
+ Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
+ pat.walk(&mut find_compatible_candidates);
+ }
+ Some(
+ hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
+ | hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Fn(_, body),
+ ..
+ })
+ | hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
+ ..
+ })
+ | hir::Node::Expr(hir::Expr {
+ kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
+ ..
+ }),
+ ) => {
+ for param in hir.body(*body).params {
+ param.pat.walk(&mut find_compatible_candidates);
+ }
+ }
+ Some(hir::Node::Expr(hir::Expr {
+ kind:
+ hir::ExprKind::If(
+ hir::Expr { kind: hir::ExprKind::Let(let_), .. },
+ then_block,
+ _,
+ ),
+ ..
+ })) if then_block.hir_id == *hir_id => {
+ let_.pat.walk(&mut find_compatible_candidates);
+ }
+ _ => {}
+ }
+ }
+ _ => {}
+ }
+
+ match &candidate_idents[..] {
+ [(ident, _ty)] => {
+ let sm = self.tcx.sess.source_map();
+ if let Some(stmt) = blk.stmts.last() {
+ let stmt_span = sm.stmt_span(stmt.span, blk.span);
+ let sugg = if sm.is_multiline(blk.span)
+ && let Some(spacing) = sm.indentation_before(stmt_span)
+ {
+ format!("\n{spacing}{ident}")
+ } else {
+ format!(" {ident}")
+ };
+ err.span_suggestion_verbose(
+ stmt_span.shrink_to_hi(),
+ format!("consider returning the local binding `{ident}`"),
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ let sugg = if sm.is_multiline(blk.span)
+ && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
+ {
+ format!("\n{spacing} {ident}\n{spacing}")
+ } else {
+ format!(" {ident} ")
+ };
+ let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
+ err.span_suggestion_verbose(
+ sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
+ format!("consider returning the local binding `{ident}`"),
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ true
+ }
+ values if (1..3).contains(&values.len()) => {
+ let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
+ err.span_note(spans, "consider returning one of these bindings");
+ true
+ }
+ _ => false,
+ }
+ }
+}
}
pub type InferResult<'tcx, T> = Result<InferOk<'tcx, T>, TypeError<'tcx>>;
-pub type Bound<T> = Option<T>;
pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result"
pub type FixupResult<'tcx, T> = Result<T, FixupError<'tcx>>; // "fixup result"
/// bound.
universe: Cell<ty::UniverseIndex>,
- normalize_fn_sig_for_diagnostic:
- Option<Lrc<dyn Fn(&InferCtxt<'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>>,
-
/// During coherence we have to assume that other crates may add
/// additional impls which we currently don't know about.
///
considering_regions: bool,
/// Whether we are in coherence mode.
intercrate: bool,
- normalize_fn_sig_for_diagnostic:
- Option<Lrc<dyn Fn(&InferCtxt<'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>>,
}
pub trait TyCtxtInferExt<'tcx> {
tcx: self,
defining_use_anchor: DefiningAnchor::Error,
considering_regions: true,
- normalize_fn_sig_for_diagnostic: None,
intercrate: false,
}
}
self
}
- pub fn with_normalize_fn_sig_for_diagnostic(
- mut self,
- fun: Lrc<dyn Fn(&InferCtxt<'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>,
- ) -> Self {
- self.normalize_fn_sig_for_diagnostic = Some(fun);
- self
- }
-
/// Given a canonical value `C` as a starting point, create an
/// inference context that contains each of the bound values
/// within instantiated as a fresh variable. The `f` closure is
}
pub fn build(&mut self) -> InferCtxt<'tcx> {
- let InferCtxtBuilder {
- tcx,
- defining_use_anchor,
- considering_regions,
- ref normalize_fn_sig_for_diagnostic,
- intercrate,
- } = *self;
+ let InferCtxtBuilder { tcx, defining_use_anchor, considering_regions, intercrate } = *self;
InferCtxt {
tcx,
defining_use_anchor,
in_snapshot: Cell::new(false),
skip_leak_check: Cell::new(false),
universe: Cell::new(ty::UniverseIndex::ROOT),
- normalize_fn_sig_for_diagnostic: normalize_fn_sig_for_diagnostic
- .as_ref()
- .map(|f| f.clone()),
intercrate,
}
}
/// Creates a `TypeErrCtxt` for emitting various inference errors.
/// During typeck, use `FnCtxt::err_ctxt` instead.
pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
- TypeErrCtxt { infcx: self, typeck_results: None, fallback_has_occurred: false }
+ TypeErrCtxt {
+ infcx: self,
+ typeck_results: None,
+ fallback_has_occurred: false,
+ normalize_fn_sig: Box::new(|fn_sig| fn_sig),
+ }
}
pub fn is_in_snapshot(&self) -> bool {
--- /dev/null
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+ fn note_error_origin(
+ &self,
+ err: &mut Diagnostic,
+ cause: &ObligationCause<'tcx>,
+ exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
+ terr: TypeError<'tcx>,
+ ) {
+ match *cause.code() {
+ ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => {
+ let ty = self.resolve_vars_if_possible(root_ty);
+ if !matches!(ty.kind(), ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_)))
+ {
+ // don't show type `_`
+ if span.desugaring_kind() == Some(DesugaringKind::ForLoop)
+ && let ty::Adt(def, substs) = ty.kind()
+ && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option)
+ {
+ err.span_label(span, format!("this is an iterator with items of type `{}`", substs.type_at(0)));
+ } else {
+ err.span_label(span, format!("this expression has type `{}`", ty));
+ }
+ }
+ if let Some(ty::error::ExpectedFound { found, .. }) = exp_found
+ && ty.is_box() && ty.boxed_ty() == found
+ && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+ {
+ err.span_suggestion(
+ span,
+ "consider dereferencing the boxed value",
+ format!("*{}", snippet),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => {
+ err.span_label(span, "expected due to this");
+ }
+ ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+ arm_block_id,
+ arm_span,
+ arm_ty,
+ prior_arm_block_id,
+ prior_arm_span,
+ prior_arm_ty,
+ source,
+ ref prior_arms,
+ scrut_hir_id,
+ opt_suggest_box_span,
+ scrut_span,
+ ..
+ }) => match source {
+ hir::MatchSource::TryDesugar => {
+ if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
+ let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
+ let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {
+ let arg_expr = args.first().expect("try desugaring call w/out arg");
+ self.typeck_results.as_ref().and_then(|typeck_results| {
+ typeck_results.expr_ty_opt(arg_expr)
+ })
+ } else {
+ bug!("try desugaring w/out call expr as scrutinee");
+ };
+
+ match scrut_ty {
+ Some(ty) if expected == ty => {
+ let source_map = self.tcx.sess.source_map();
+ err.span_suggestion(
+ source_map.end_point(cause.span),
+ "try removing this `?`",
+ "",
+ Applicability::MachineApplicable,
+ );
+ }
+ _ => {}
+ }
+ }
+ }
+ _ => {
+ // `prior_arm_ty` can be `!`, `expected` will have better info when present.
+ let t = self.resolve_vars_if_possible(match exp_found {
+ Some(ty::error::ExpectedFound { expected, .. }) => expected,
+ _ => prior_arm_ty,
+ });
+ 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);
+ self.suggest_remove_semi_or_return_binding(
+ err,
+ prior_arm_block_id,
+ prior_arm_ty,
+ prior_arm_span,
+ arm_block_id,
+ arm_ty,
+ arm_span,
+ );
+ if let Some(ret_sp) = opt_suggest_box_span {
+ // Get return type span and point to it.
+ self.suggest_boxing_for_return_impl_trait(
+ err,
+ ret_sp,
+ prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s),
+ );
+ }
+ }
+ },
+ ObligationCauseCode::IfExpression(box IfExpressionCause {
+ then_id,
+ else_id,
+ then_ty,
+ else_ty,
+ outer_span,
+ opt_suggest_box_span,
+ }) => {
+ let then_span = self.find_block_span_from_hir_id(then_id);
+ let else_span = self.find_block_span_from_hir_id(else_id);
+ err.span_label(then_span, "expected because of this");
+ if let Some(sp) = outer_span {
+ err.span_label(sp, "`if` and `else` have incompatible types");
+ }
+ self.suggest_remove_semi_or_return_binding(
+ err,
+ Some(then_id),
+ then_ty,
+ then_span,
+ Some(else_id),
+ else_ty,
+ else_span,
+ );
+ if let Some(ret_sp) = opt_suggest_box_span {
+ self.suggest_boxing_for_return_impl_trait(
+ err,
+ ret_sp,
+ [then_span, else_span].into_iter(),
+ );
+ }
+ }
+ ObligationCauseCode::LetElse => {
+ err.help("try adding a diverging expression, such as `return` or `panic!(..)`");
+ err.help("...or use `match` instead of `let...else`");
+ }
+ _ => {
+ if let ObligationCauseCode::BindingObligation(_, span)
+ | ObligationCauseCode::ExprBindingObligation(_, span, ..)
+ = cause.code().peel_derives()
+ && let TypeError::RegionsPlaceholderMismatch = terr
+ {
+ err.span_note( * span,
+ "the lifetime requirement is introduced here");
+ }
+ }
+ }
+ }
+}
+
+impl<'tcx> InferCtxt<'tcx> {
+ /// Given a [`hir::Block`], get the span of its last expression or
+ /// statement, peeling off any inner blocks.
+ pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span {
+ let block = block.innermost_block();
+ if let Some(expr) = &block.expr {
+ expr.span
+ } else if let Some(stmt) = block.stmts.last() {
+ // possibly incorrect trailing `;` in the else arm
+ stmt.span
+ } else {
+ // empty block; point at its entirety
+ block.span
+ }
+ }
+
+ /// Given a [`hir::HirId`] for a block, get the span of its last expression
+ /// or statement, peeling off any inner blocks.
+ pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span {
+ match self.tcx.hir().get(hir_id) {
+ hir::Node::Block(blk) => self.find_block_span(blk),
+ // The parser was in a weird state if either of these happen, but
+ // it's better not to panic.
+ hir::Node::Expr(e) => e.span,
+ _ => rustc_span::DUMMY_SP,
+ }
+ }
+}
use rustc_borrowck as mir_borrowck;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_data_structures::parallel;
+use rustc_data_structures::steal::Steal;
use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
use rustc_errors::{ErrorGuaranteed, PResult};
use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand};
sess: Lrc<Session>,
metadata_loader: Box<MetadataLoaderDyn>,
krate: &ast::Crate,
- crate_name: &str,
+ crate_name: Symbol,
) -> BoxedResolver {
trace!("create_resolver");
BoxedResolver::new(sess, move |sess, resolver_arenas| {
metadata_loader: &'a dyn MetadataLoader,
register_lints: impl Fn(&Session, &mut LintStore),
mut krate: ast::Crate,
- crate_name: &str,
+ crate_name: Symbol,
) -> Result<(ast::Crate, LintStore)> {
krate = sess.time("attributes_injection", || {
rustc_builtin_macros::cmdline_attrs::inject(
});
}
- let mut lint_store = rustc_lint::new_lint_store(
- sess.opts.unstable_opts.no_interleave_lints,
- sess.enable_internal_lints(),
- );
+ let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints());
register_lints(sess, &mut lint_store);
let registrars =
lint_store: &LintStore,
registered_tools: &RegisteredTools,
check_node: impl EarlyCheckNode<'a>,
- node_name: &str,
+ node_name: Symbol,
) {
- sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", node_name).run(|| {
- rustc_lint::check_ast_node(
- sess,
- true,
- lint_store,
- registered_tools,
- None,
- rustc_lint::BuiltinCombinedPreExpansionLintPass::new(),
- check_node,
- );
- });
+ sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", node_name.as_str()).run(
+ || {
+ rustc_lint::check_ast_node(
+ sess,
+ true,
+ lint_store,
+ registered_tools,
+ None,
+ rustc_lint::BuiltinCombinedPreExpansionLintPass::new(),
+ check_node,
+ );
+ },
+ );
}
// Cannot implement directly for `LintStore` due to trait coherence.
node_id: ast::NodeId,
attrs: &[ast::Attribute],
items: &[rustc_ast::ptr::P<ast::Item>],
- name: &str,
+ name: Symbol,
) {
pre_expansion_lint(sess, self.0, registered_tools, (node_id, attrs, items), name);
}
sess: &Session,
lint_store: &LintStore,
mut krate: ast::Crate,
- crate_name: &str,
+ crate_name: Symbol,
resolver: &mut Resolver<'_>,
) -> Result<ast::Crate> {
trace!("configure_and_expand");
sess: &Session,
outputs: &OutputFilenames,
exact_name: bool,
- crate_name: &str,
+ crate_name: Symbol,
) -> Vec<PathBuf> {
let mut out_filenames = Vec::new();
for output_type in sess.opts.output_types.keys() {
compiler: &Compiler,
krate: &ast::Crate,
boxed_resolver: &RefCell<BoxedResolver>,
- crate_name: &str,
+ crate_name: Symbol,
) -> Result<OutputFilenames> {
let _timer = sess.timer("prepare_outputs");
dep_graph: DepGraph,
resolver: Rc<RefCell<BoxedResolver>>,
outputs: OutputFilenames,
- crate_name: &str,
+ crate_name: Symbol,
queries: &'tcx OnceCell<TcxQueries<'tcx>>,
global_ctxt: &'tcx OnceCell<GlobalCtxt<'tcx>>,
arena: &'tcx WorkerLocal<Arena<'tcx>>,
TcxQueries::new(local_providers, extern_providers, query_result_on_disk_cache)
});
+ let ty::ResolverOutputs {
+ global_ctxt: untracked_resolutions,
+ ast_lowering: untracked_resolver_for_lowering,
+ untracked,
+ } = resolver_outputs;
+
let gcx = sess.time("setup_global_ctxt", || {
global_ctxt.get_or_init(move || {
TyCtxt::create_global_ctxt(
lint_store,
arena,
hir_arena,
- resolver_outputs,
+ untracked_resolutions,
+ untracked,
krate,
dep_graph,
queries.on_disk_cache.as_ref().map(OnDiskCache::as_dyn),
})
});
- QueryContext { gcx }
+ let mut qcx = QueryContext { gcx };
+ qcx.enter(|tcx| {
+ tcx.feed_unit_query()
+ .resolver_for_lowering(tcx.arena.alloc(Steal::new(untracked_resolver_for_lowering)))
+ });
+ qcx
}
/// Runs the resolution, type-checking, region checking and other
pub fn start_codegen<'tcx>(
codegen_backend: &dyn CodegenBackend,
tcx: TyCtxt<'tcx>,
- outputs: &OutputFilenames,
) -> Box<dyn Any> {
info!("Pre-codegen\n{:?}", tcx.debug_stats());
- let (metadata, need_metadata_module) =
- rustc_metadata::fs::encode_and_write_metadata(tcx, outputs);
+ let (metadata, need_metadata_module) = rustc_metadata::fs::encode_and_write_metadata(tcx);
let codegen = tcx.sess.time("codegen_crate", move || {
codegen_backend.codegen_crate(tcx, metadata, need_metadata_module)
info!("Post-codegen\n{:?}", tcx.debug_stats());
if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) {
- if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx, outputs) {
+ if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) {
tcx.sess.emit_err(CantEmitMIR { error });
tcx.sess.abort_if_errors();
}
use rustc_session::config::{self, OutputFilenames, OutputType};
use rustc_session::{output::find_crate_name, Session};
use rustc_span::symbol::sym;
+use rustc_span::Symbol;
use std::any::Any;
use std::cell::{Ref, RefCell, RefMut};
use std::rc::Rc;
+use std::sync::Arc;
/// Represent the result of a query.
///
dep_graph_future: Query<Option<DepGraphFuture>>,
parse: Query<ast::Crate>,
- crate_name: Query<String>,
+ crate_name: Query<Symbol>,
register_plugins: Query<(ast::Crate, Lrc<LintStore>)>,
expansion: Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>,
dep_graph: Query<DepGraph>,
&*self.codegen_backend().metadata_loader(),
self.compiler.register_lints.as_deref().unwrap_or_else(|| empty),
krate,
- &crate_name,
+ crate_name,
)?;
// Compute the dependency graph (in the background). We want to do
})
}
- pub fn crate_name(&self) -> Result<&Query<String>> {
+ pub fn crate_name(&self) -> Result<&Query<Symbol>> {
self.crate_name.compute(|| {
Ok({
let parse_result = self.parse()?;
) -> Result<&Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>> {
trace!("expansion");
self.expansion.compute(|| {
- let crate_name = self.crate_name()?.peek().clone();
+ let crate_name = *self.crate_name()?.peek();
let (krate, lint_store) = self.register_plugins()?.take();
let _timer = self.session().timer("configure_and_expand");
let sess = self.session();
sess.clone(),
self.codegen_backend().metadata_loader(),
&krate,
- &crate_name,
+ crate_name,
);
let krate = resolver.access(|resolver| {
- passes::configure_and_expand(sess, &lint_store, krate, &crate_name, resolver)
+ passes::configure_and_expand(sess, &lint_store, krate, crate_name, resolver)
})?;
Ok((Lrc::new(krate), Rc::new(RefCell::new(resolver)), lint_store))
})
pub fn prepare_outputs(&self) -> Result<&Query<OutputFilenames>> {
self.prepare_outputs.compute(|| {
let (krate, boxed_resolver, _) = &*self.expansion()?.peek();
- let crate_name = self.crate_name()?.peek();
+ let crate_name = *self.crate_name()?.peek();
passes::prepare_outputs(
self.session(),
self.compiler,
krate,
&*boxed_resolver,
- &crate_name,
+ crate_name,
)
})
}
pub fn global_ctxt(&'tcx self) -> Result<&Query<QueryContext<'tcx>>> {
self.global_ctxt.compute(|| {
- let crate_name = self.crate_name()?.peek().clone();
- let outputs = self.prepare_outputs()?.peek().clone();
+ let crate_name = *self.crate_name()?.peek();
+ let outputs = self.prepare_outputs()?.take();
let dep_graph = self.dep_graph()?.peek().clone();
let (krate, resolver, lint_store) = self.expansion()?.take();
Ok(passes::create_global_ctxt(
dep_graph,
resolver,
outputs,
- &crate_name,
+ crate_name,
&self.queries,
&self.gcx,
&self.arena,
pub fn ongoing_codegen(&'tcx self) -> Result<&Query<Box<dyn Any>>> {
self.ongoing_codegen.compute(|| {
- let outputs = self.prepare_outputs()?;
self.global_ctxt()?.peek_mut().enter(|tcx| {
tcx.analysis(()).ok();
// Hook for UI tests.
Self::check_for_rustc_errors_attr(tcx);
- Ok(passes::start_codegen(&***self.codegen_backend(), tcx, &*outputs.peek()))
+ Ok(passes::start_codegen(&***self.codegen_backend(), tcx))
})
})
}
let codegen_backend = self.codegen_backend().clone();
let dep_graph = self.dep_graph()?.peek().clone();
- let prepare_outputs = self.prepare_outputs()?.take();
- let crate_hash = self.global_ctxt()?.peek_mut().enter(|tcx| tcx.crate_hash(LOCAL_CRATE));
+ let (crate_hash, prepare_outputs) = self
+ .global_ctxt()?
+ .peek_mut()
+ .enter(|tcx| (tcx.crate_hash(LOCAL_CRATE), tcx.output_filenames(()).clone()));
let ongoing_codegen = self.ongoing_codegen()?.take();
Ok(Linker {
// compilation outputs
dep_graph: DepGraph,
- prepare_outputs: OutputFilenames,
+ prepare_outputs: Arc<OutputFilenames>,
crate_hash: Svh,
ongoing_codegen: Box<dyn Any>,
}
use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel};
use std::collections::{BTreeMap, BTreeSet};
-use std::iter::FromIterator;
use std::num::NonZeroUsize;
use std::path::{Path, PathBuf};
untracked!(mir_pretty_relative_line_numbers, true);
untracked!(nll_facts, true);
untracked!(no_analysis, true);
- untracked!(no_interleave_lints, true);
untracked!(no_leak_check, true);
untracked!(no_parallel_llvm, true);
untracked!(parse_only, true);
tracked!(link_only, true);
tracked!(llvm_plugins, vec![String::from("plugin_name")]);
tracked!(location_detail, LocationDetail { file: true, line: false, column: false });
+ tracked!(maximal_hir_to_mir_coverage, true);
tracked!(merge_functions, Some(MergeFunctions::Disabled));
tracked!(mir_emit_retag, true);
tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]);
unic-emoji-char = "0.9.0"
[dev-dependencies]
-expect-test = "1.0"
+expect-test = "1.4.0"
use self::LiteralKind::*;
use self::TokenKind::*;
use crate::cursor::EOF_CHAR;
-use std::convert::TryFrom;
/// Parsed token.
/// It doesn't contain information about data that has been parsed,
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, InnerSpan, Span};
use rustc_target::abi::{Abi, VariantIdx};
-use rustc_trait_selection::traits::{self, misc::can_type_implement_copy};
+use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
+use rustc_trait_selection::traits::{self, misc::can_type_implement_copy, EvaluationResult};
use crate::nonstandard_style::{method_context, MethodLateContext};
}
impl EarlyLintPass for WhileTrue {
+ #[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
if let ast::ExprKind::While(cond, _, label) = &e.kind
&& let cond = pierce_parens(cond)
}
if let PatKind::Binding(binding_annot, _, ident, None) = fieldpat.pat.kind {
if cx.tcx.find_field_index(ident, &variant)
- == Some(cx.tcx.field_index(fieldpat.hir_id, cx.typeck_results()))
+ == Some(cx.typeck_results().field_index(fieldpat.hir_id))
{
cx.struct_span_lint(
NON_SHORTHAND_FIELD_PATTERNS,
}
}
+ #[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
if let ast::ExprKind::Block(ref blk, _) = e.kind {
// Don't warn about generated blocks; that'll just pollute the output.
}
impl<'tcx> LateLintPass<'tcx> for MissingDoc {
+ #[inline]
fn enter_lint_attrs(&mut self, _cx: &LateContext<'_>, attrs: &[ast::Attribute]) {
let doc_hidden = self.doc_hidden()
|| attrs.iter().any(|attr| {
if def.has_dtor(cx.tcx) {
return;
}
+
+ // If the type contains a raw pointer, it may represent something like a handle,
+ // and recommending Copy might be a bad idea.
+ for field in def.all_fields() {
+ let did = field.did;
+ if cx.tcx.type_of(did).is_unsafe_ptr() {
+ return;
+ }
+ }
let param_env = ty::ParamEnv::empty();
if ty.is_copy_modulo_regions(cx.tcx, param_env) {
return;
}
+
+ // We shouldn't recommend implementing `Copy` on stateful things,
+ // such as iterators.
+ if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) {
+ if cx.tcx.infer_ctxt().build().type_implements_trait(iter_trait, [ty], param_env)
+ == EvaluationResult::EvaluatedToOk
+ {
+ return;
+ }
+ }
+
+ // Default value of clippy::trivially_copy_pass_by_ref
+ const MAX_SIZE: u64 = 256;
+
+ if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()) {
+ if size > MAX_SIZE {
+ return;
+ }
+ }
+
if can_type_implement_copy(
cx.tcx,
param_env,
use rustc_span::symbol::Ident;
use rustc_span::Span;
-use std::slice;
-
-macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({
+macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
$cx.pass.$f(&$cx.context, $($args),*);
}) }
+/// Implements the AST traversal for early lint passes. `T` provides the the
+/// `check_*` methods.
pub struct EarlyContextAndPass<'a, T: EarlyLintPass> {
context: EarlyContext<'a>,
pass: T,
}
impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
- fn check_id(&mut self, id: ast::NodeId) {
+ // This always-inlined function is for the hot call site.
+ #[inline(always)]
+ fn inlined_check_id(&mut self, id: ast::NodeId) {
for early_lint in self.context.buffered.take(id) {
let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint;
self.context.lookup_with_diagnostics(
}
}
+ // This non-inlined function is for the cold call sites.
+ fn check_id(&mut self, id: ast::NodeId) {
+ self.inlined_check_id(id)
+ }
+
/// Merge the lints specified by any lint attributes into the
/// current lint context, call the provided function, then reset the
/// lints in effect to their previous state.
debug!(?id);
let push = self.context.builder.push(attrs, is_crate_node, None);
- self.check_id(id);
+ self.inlined_check_id(id);
debug!("early context: enter_attrs({:?})", attrs);
- run_early_pass!(self, enter_lint_attrs, attrs);
+ lint_callback!(self, enter_lint_attrs, attrs);
f(self);
debug!("early context: exit_attrs({:?})", attrs);
- run_early_pass!(self, exit_lint_attrs, attrs);
+ lint_callback!(self, exit_lint_attrs, attrs);
self.context.builder.pop(push);
}
}
impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> {
fn visit_param(&mut self, param: &'a ast::Param) {
self.with_lint_attrs(param.id, ¶m.attrs, |cx| {
- run_early_pass!(cx, check_param, param);
+ lint_callback!(cx, check_param, param);
ast_visit::walk_param(cx, param);
});
}
fn visit_item(&mut self, it: &'a ast::Item) {
self.with_lint_attrs(it.id, &it.attrs, |cx| {
- run_early_pass!(cx, check_item, it);
+ lint_callback!(cx, check_item, it);
ast_visit::walk_item(cx, it);
- run_early_pass!(cx, check_item_post, it);
+ lint_callback!(cx, check_item_post, it);
})
}
}
fn visit_pat(&mut self, p: &'a ast::Pat) {
- run_early_pass!(self, check_pat, p);
+ lint_callback!(self, check_pat, p);
self.check_id(p.id);
ast_visit::walk_pat(self, p);
- run_early_pass!(self, check_pat_post, p);
+ lint_callback!(self, check_pat_post, p);
}
fn visit_pat_field(&mut self, field: &'a ast::PatField) {
fn visit_expr(&mut self, e: &'a ast::Expr) {
self.with_lint_attrs(e.id, &e.attrs, |cx| {
- run_early_pass!(cx, check_expr, e);
+ lint_callback!(cx, check_expr, e);
ast_visit::walk_expr(cx, e);
})
}
// 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);
+ lint_callback!(cx, check_stmt, s);
cx.check_id(s.id);
});
// The visitor for the AST struct wrapped
}
fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, span: Span, id: ast::NodeId) {
- run_early_pass!(self, check_fn, fk, span, id);
+ lint_callback!(self, check_fn, fk, span, id);
self.check_id(id);
ast_visit::walk_fn(self, fk);
fn visit_variant(&mut self, v: &'a ast::Variant) {
self.with_lint_attrs(v.id, &v.attrs, |cx| {
- run_early_pass!(cx, check_variant, v);
+ lint_callback!(cx, check_variant, v);
ast_visit::walk_variant(cx, v);
})
}
fn visit_ty(&mut self, t: &'a ast::Ty) {
- run_early_pass!(self, check_ty, t);
+ lint_callback!(self, check_ty, t);
self.check_id(t.id);
ast_visit::walk_ty(self, t);
}
fn visit_ident(&mut self, ident: Ident) {
- run_early_pass!(self, check_ident, ident);
+ lint_callback!(self, check_ident, ident);
}
fn visit_local(&mut self, l: &'a ast::Local) {
self.with_lint_attrs(l.id, &l.attrs, |cx| {
- run_early_pass!(cx, check_local, l);
+ lint_callback!(cx, check_local, l);
ast_visit::walk_local(cx, l);
})
}
fn visit_block(&mut self, b: &'a ast::Block) {
- run_early_pass!(self, check_block, b);
+ lint_callback!(self, check_block, b);
self.check_id(b.id);
ast_visit::walk_block(self, b);
}
fn visit_arm(&mut self, a: &'a ast::Arm) {
self.with_lint_attrs(a.id, &a.attrs, |cx| {
- run_early_pass!(cx, check_arm, a);
+ lint_callback!(cx, check_arm, a);
ast_visit::walk_arm(cx, a);
})
}
}
fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) {
- run_early_pass!(self, check_generic_arg, arg);
+ lint_callback!(self, check_generic_arg, arg);
ast_visit::walk_generic_arg(self, arg);
}
fn visit_generic_param(&mut self, param: &'a ast::GenericParam) {
self.with_lint_attrs(param.id, ¶m.attrs, |cx| {
- run_early_pass!(cx, check_generic_param, param);
+ lint_callback!(cx, check_generic_param, param);
ast_visit::walk_generic_param(cx, param);
});
}
fn visit_generics(&mut self, g: &'a ast::Generics) {
- run_early_pass!(self, check_generics, g);
+ lint_callback!(self, check_generics, g);
ast_visit::walk_generics(self, g);
}
}
fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) {
- run_early_pass!(self, check_poly_trait_ref, t);
+ lint_callback!(self, check_poly_trait_ref, t);
ast_visit::walk_poly_trait_ref(self, t);
}
fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
self.with_lint_attrs(item.id, &item.attrs, |cx| match ctxt {
ast_visit::AssocCtxt::Trait => {
- run_early_pass!(cx, check_trait_item, item);
+ lint_callback!(cx, check_trait_item, item);
ast_visit::walk_assoc_item(cx, item, ctxt);
}
ast_visit::AssocCtxt::Impl => {
- run_early_pass!(cx, check_impl_item, item);
+ lint_callback!(cx, check_impl_item, item);
ast_visit::walk_assoc_item(cx, item, ctxt);
}
});
}
fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
- run_early_pass!(self, check_attribute, attr);
+ lint_callback!(self, check_attribute, attr);
}
fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) {
- run_early_pass!(self, check_mac_def, mac);
+ lint_callback!(self, check_mac_def, mac);
self.check_id(id);
}
fn visit_mac_call(&mut self, mac: &'a ast::MacCall) {
- run_early_pass!(self, check_mac, mac);
+ lint_callback!(self, check_mac, mac);
ast_visit::walk_mac(self, mac);
}
}
-struct EarlyLintPassObjects<'a> {
- lints: &'a mut [EarlyLintPassObject],
+// Combines multiple lint passes into a single pass, at runtime. Each
+// `check_foo` method in `$methods` within this pass simply calls `check_foo`
+// once per `$pass`. Compare with `declare_combined_early_lint_pass`, which is
+// similar, but combines lint passes at compile time.
+struct RuntimeCombinedEarlyLintPass<'a> {
+ passes: &'a mut [EarlyLintPassObject],
}
#[allow(rustc::lint_pass_impl_without_macro)]
-impl LintPass for EarlyLintPassObjects<'_> {
+impl LintPass for RuntimeCombinedEarlyLintPass<'_> {
fn name(&self) -> &'static str {
panic!()
}
}
-macro_rules! expand_early_lint_pass_impl_methods {
- ([$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
- $(fn $name(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) {
- for obj in self.lints.iter_mut() {
- obj.$name(context, $($param),*);
- }
- })*
- )
-}
-
-macro_rules! early_lint_pass_impl {
- ([], [$($methods:tt)*]) => (
- impl EarlyLintPass for EarlyLintPassObjects<'_> {
- expand_early_lint_pass_impl_methods!([$($methods)*]);
+macro_rules! impl_early_lint_pass {
+ ([], [$($(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => (
+ impl EarlyLintPass for RuntimeCombinedEarlyLintPass<'_> {
+ $(fn $f(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) {
+ for pass in self.passes.iter_mut() {
+ pass.$f(context, $($param),*);
+ }
+ })*
}
)
}
-crate::early_lint_methods!(early_lint_pass_impl, []);
+crate::early_lint_methods!(impl_early_lint_pass, []);
/// Early lints work on different nodes - either on the crate root, or on freshly loaded modules.
/// This trait generalizes over those nodes.
fn attrs<'b>(self) -> &'b [ast::Attribute]
where
'a: 'b;
- fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>)
+ fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
where
'a: 'b;
}
{
&self.attrs
}
- fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>)
+ fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
where
'a: 'b,
{
- run_early_pass!(cx, check_crate, self);
+ lint_callback!(cx, check_crate, self);
ast_visit::walk_crate(cx, self);
- run_early_pass!(cx, check_crate_post, self);
+ lint_callback!(cx, check_crate_post, self);
}
}
{
self.1
}
- fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>)
+ fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
where
'a: 'b,
{
}
}
-fn early_lint_node<'a>(
- sess: &Session,
- warn_about_weird_lints: bool,
- lint_store: &LintStore,
- registered_tools: &RegisteredTools,
- buffered: LintBuffer,
- pass: impl EarlyLintPass,
- check_node: impl EarlyCheckNode<'a>,
-) -> LintBuffer {
- let mut cx = EarlyContextAndPass {
- context: EarlyContext::new(
- sess,
- warn_about_weird_lints,
- lint_store,
- registered_tools,
- buffered,
- ),
- pass,
- };
-
- cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx));
- cx.context.buffered
-}
-
pub fn check_ast_node<'a>(
sess: &Session,
pre_expansion: bool,
lint_store: &LintStore,
registered_tools: &RegisteredTools,
lint_buffer: Option<LintBuffer>,
- builtin_lints: impl EarlyLintPass,
+ builtin_lints: impl EarlyLintPass + 'static,
check_node: impl EarlyCheckNode<'a>,
) {
+ let context = EarlyContext::new(
+ sess,
+ !pre_expansion,
+ lint_store,
+ registered_tools,
+ lint_buffer.unwrap_or_default(),
+ );
+
+ // Note: `passes` is often empty. In that case, it's faster to run
+ // `builtin_lints` directly rather than bundling it up into the
+ // `RuntimeCombinedEarlyLintPass`.
let passes =
if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes };
- let mut passes: Vec<_> = passes.iter().map(|p| (p)()).collect();
- let mut buffered = lint_buffer.unwrap_or_default();
-
- if sess.opts.unstable_opts.no_interleave_lints {
- for (i, pass) in passes.iter_mut().enumerate() {
- buffered =
- sess.prof.verbose_generic_activity_with_arg("run_lint", pass.name()).run(|| {
- early_lint_node(
- sess,
- !pre_expansion && i == 0,
- lint_store,
- registered_tools,
- buffered,
- EarlyLintPassObjects { lints: slice::from_mut(pass) },
- check_node,
- )
- });
- }
+ if passes.is_empty() {
+ check_ast_node_inner(sess, check_node, context, builtin_lints);
} else {
- buffered = early_lint_node(
- sess,
- !pre_expansion,
- lint_store,
- registered_tools,
- buffered,
- builtin_lints,
- check_node,
- );
-
- if !passes.is_empty() {
- buffered = early_lint_node(
- sess,
- false,
- lint_store,
- registered_tools,
- buffered,
- EarlyLintPassObjects { lints: &mut passes[..] },
- check_node,
- );
- }
+ let mut passes: Vec<_> = passes.iter().map(|mk_pass| (mk_pass)()).collect();
+ passes.push(Box::new(builtin_lints));
+ let pass = RuntimeCombinedEarlyLintPass { passes: &mut passes[..] };
+ check_ast_node_inner(sess, check_node, context, pass);
}
+}
+
+pub fn check_ast_node_inner<'a, T: EarlyLintPass>(
+ sess: &Session,
+ check_node: impl EarlyCheckNode<'a>,
+ context: EarlyContext<'_>,
+ pass: T,
+) {
+ let mut cx = EarlyContextAndPass { context, pass };
+
+ cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx));
// All of the buffered lints should have been emitted at this point.
// If not, that means that we somehow buffered a lint for a node id
// that was not lint-checked (perhaps it doesn't exist?). This is a bug.
- for (id, lints) in buffered.map {
+ for (id, lints) in cx.context.buffered.map {
for early_lint in lints {
sess.delay_span_bug(
early_lint.span,
}
}
+ #[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
// byte strings are already handled well enough by `EscapeError::NonAsciiCharInByteString`
match &expr.kind {
fn check_path(
&mut self,
cx: &LateContext<'tcx>,
- path: &'tcx rustc_hir::Path<'tcx>,
+ path: &rustc_hir::Path<'tcx>,
_: rustc_hir::HirId,
) {
if let Some(segment) = path.segments.iter().nth_back(1)
use std::any::Any;
use std::cell::Cell;
-use std::slice;
/// Extract the `LintStore` from the query context.
/// This function exists because we've erased `LintStore` as `dyn Any` in the context.
$cx.pass.$f(&$cx.context, $($args),*);
}) }
-struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>> {
+/// Implements the AST traversal for late lint passes. `T` provides the the
+/// `check_*` methods.
+pub struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>> {
context: LateContext<'tcx>,
pass: T,
}
hir_visit::walk_lifetime(self, lt);
}
- fn visit_path(&mut self, p: &'tcx hir::Path<'tcx>, id: hir::HirId) {
+ fn visit_path(&mut self, p: &hir::Path<'tcx>, id: hir::HirId) {
lint_callback!(self, check_path, p, id);
hir_visit::walk_path(self, p);
}
}
}
-struct LateLintPassObjects<'a, 'tcx> {
- lints: &'a mut [LateLintPassObject<'tcx>],
+// Combines multiple lint passes into a single pass, at runtime. Each
+// `check_foo` method in `$methods` within this pass simply calls `check_foo`
+// once per `$pass`. Compare with `declare_combined_late_lint_pass`, which is
+// similar, but combines lint passes at compile time.
+struct RuntimeCombinedLateLintPass<'a, 'tcx> {
+ passes: &'a mut [LateLintPassObject<'tcx>],
}
#[allow(rustc::lint_pass_impl_without_macro)]
-impl LintPass for LateLintPassObjects<'_, '_> {
+impl LintPass for RuntimeCombinedLateLintPass<'_, '_> {
fn name(&self) -> &'static str {
panic!()
}
}
-macro_rules! expand_late_lint_pass_impl_methods {
- ([$hir:tt], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
- $(fn $name(&mut self, context: &LateContext<$hir>, $($param: $arg),*) {
- for obj in self.lints.iter_mut() {
- obj.$name(context, $($param),*);
- }
- })*
- )
-}
-
-macro_rules! late_lint_pass_impl {
- ([], [$hir:tt], $methods:tt) => {
- impl<$hir> LateLintPass<$hir> for LateLintPassObjects<'_, $hir> {
- expand_late_lint_pass_impl_methods!([$hir], $methods);
+macro_rules! impl_late_lint_pass {
+ ([], [$($(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => {
+ impl<'tcx> LateLintPass<'tcx> for RuntimeCombinedLateLintPass<'_, 'tcx> {
+ $(fn $f(&mut self, context: &LateContext<'tcx>, $($param: $arg),*) {
+ for pass in self.passes.iter_mut() {
+ pass.$f(context, $($param),*);
+ }
+ })*
}
};
}
-crate::late_lint_methods!(late_lint_pass_impl, [], ['tcx]);
+crate::late_lint_methods!(impl_late_lint_pass, []);
-fn late_lint_mod_pass<'tcx, T: LateLintPass<'tcx>>(
+pub(super) fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
tcx: TyCtxt<'tcx>,
module_def_id: LocalDefId,
- pass: T,
+ builtin_lints: T,
) {
- let effective_visibilities = &tcx.effective_visibilities(());
-
let context = LateContext {
tcx,
enclosing_body: None,
cached_typeck_results: Cell::new(None),
param_env: ty::ParamEnv::empty(),
- effective_visibilities,
+ effective_visibilities: &tcx.effective_visibilities(()),
lint_store: unerased_lint_store(tcx),
last_node_with_lint_attrs: tcx.hir().local_def_id_to_hir_id(module_def_id),
generics: None,
only_module: true,
};
+ // Note: `passes` is often empty. In that case, it's faster to run
+ // `builtin_lints` directly rather than bundling it up into the
+ // `RuntimeCombinedLateLintPass`.
+ let mut passes: Vec<_> =
+ unerased_lint_store(tcx).late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
+ if passes.is_empty() {
+ late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
+ } else {
+ passes.push(Box::new(builtin_lints));
+ let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
+ late_lint_mod_inner(tcx, module_def_id, context, pass);
+ }
+}
+
+fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
+ tcx: TyCtxt<'tcx>,
+ module_def_id: LocalDefId,
+ context: LateContext<'tcx>,
+ pass: T,
+) {
let mut cx = LateContextAndPass { context, pass };
let (module, _span, hir_id) = tcx.hir().get_module(module_def_id);
}
}
-pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx>>(
- tcx: TyCtxt<'tcx>,
- module_def_id: LocalDefId,
- builtin_lints: T,
-) {
- if tcx.sess.opts.unstable_opts.no_interleave_lints {
- // These passes runs in late_lint_crate with -Z no_interleave_lints
- return;
- }
-
- late_lint_mod_pass(tcx, module_def_id, builtin_lints);
-
- let mut passes: Vec<_> =
- unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)(tcx)).collect();
-
- if !passes.is_empty() {
- late_lint_mod_pass(tcx, module_def_id, LateLintPassObjects { lints: &mut passes[..] });
- }
-}
-
-fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T) {
- let effective_visibilities = &tcx.effective_visibilities(());
-
+fn late_lint_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(tcx: TyCtxt<'tcx>, builtin_lints: T) {
let context = LateContext {
tcx,
enclosing_body: None,
cached_typeck_results: Cell::new(None),
param_env: ty::ParamEnv::empty(),
- effective_visibilities,
+ effective_visibilities: &tcx.effective_visibilities(()),
lint_store: unerased_lint_store(tcx),
last_node_with_lint_attrs: hir::CRATE_HIR_ID,
generics: None,
only_module: false,
};
+ // Note: `passes` is often empty. In that case, it's faster to run
+ // `builtin_lints` directly rather than bundling it up into the
+ // `RuntimeCombinedLateLintPass`.
+ let mut passes: Vec<_> =
+ unerased_lint_store(tcx).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
+ if passes.is_empty() {
+ late_lint_crate_inner(tcx, context, builtin_lints);
+ } else {
+ passes.push(Box::new(builtin_lints));
+ let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
+ late_lint_crate_inner(tcx, context, pass);
+ }
+}
+
+fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>(
+ tcx: TyCtxt<'tcx>,
+ context: LateContext<'tcx>,
+ pass: T,
+) {
let mut cx = LateContextAndPass { context, pass };
// Visit the whole crate.
cx.with_lint_attrs(hir::CRATE_HIR_ID, |cx| {
- // since the root module isn't visited as an item (because it isn't an
+ // Since the root module isn't visited as an item (because it isn't an
// item), warn for it here.
lint_callback!(cx, check_crate,);
tcx.hir().walk_toplevel_module(cx);
})
}
-fn late_lint_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, builtin_lints: T) {
- let mut passes =
- unerased_lint_store(tcx).late_passes.iter().map(|p| (p)(tcx)).collect::<Vec<_>>();
-
- if !tcx.sess.opts.unstable_opts.no_interleave_lints {
- if !passes.is_empty() {
- late_lint_pass_crate(tcx, LateLintPassObjects { lints: &mut passes[..] });
- }
-
- late_lint_pass_crate(tcx, builtin_lints);
- } else {
- for pass in &mut passes {
- tcx.sess.prof.verbose_generic_activity_with_arg("run_late_lint", pass.name()).run(
- || {
- late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
- },
- );
- }
-
- let mut passes: Vec<_> =
- unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)(tcx)).collect();
-
- for pass in &mut passes {
- tcx.sess
- .prof
- .verbose_generic_activity_with_arg("run_late_module_lint", pass.name())
- .run(|| {
- late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
- });
- }
- }
-}
-
/// Performs lint checking on a crate.
-pub fn check_crate<'tcx, T: LateLintPass<'tcx>>(
+pub fn check_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(
tcx: TyCtxt<'tcx>,
builtin_lints: impl FnOnce() -> T + Send,
) {
late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new());
}
-macro_rules! pre_expansion_lint_passes {
- ($macro:path, $args:tt) => {
- $macro!($args, [KeywordIdents: KeywordIdents,]);
- };
-}
-
-macro_rules! early_lint_passes {
- ($macro:path, $args:tt) => {
- $macro!(
- $args,
- [
- UnusedParens: UnusedParens,
- UnusedBraces: UnusedBraces,
- UnusedImportBraces: UnusedImportBraces,
- UnsafeCode: UnsafeCode,
- SpecialModuleName: SpecialModuleName,
- AnonymousParameters: AnonymousParameters,
- EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(),
- NonCamelCaseTypes: NonCamelCaseTypes,
- DeprecatedAttr: DeprecatedAttr::new(),
- WhileTrue: WhileTrue,
- NonAsciiIdents: NonAsciiIdents,
- HiddenUnicodeCodepoints: HiddenUnicodeCodepoints,
- IncompleteFeatures: IncompleteFeatures,
- RedundantSemicolons: RedundantSemicolons,
- UnusedDocComment: UnusedDocComment,
- UnexpectedCfgs: UnexpectedCfgs,
- ]
- );
- };
-}
-
-macro_rules! declare_combined_early_pass {
- ([$name:ident], $passes:tt) => (
- early_lint_methods!(declare_combined_early_lint_pass, [pub $name, $passes]);
- )
-}
-
-pre_expansion_lint_passes!(declare_combined_early_pass, [BuiltinCombinedPreExpansionLintPass]);
-early_lint_passes!(declare_combined_early_pass, [BuiltinCombinedEarlyLintPass]);
-
-macro_rules! late_lint_passes {
- ($macro:path, $args:tt) => {
- $macro!(
- $args,
- [
- // Tracks state across modules
- UnnameableTestItems: UnnameableTestItems::new(),
- // Tracks attributes of parents
- MissingDoc: MissingDoc::new(),
- // Builds a global list of all impls of `Debug`.
- // FIXME: Turn the computation of types which implement Debug into a query
- // and change this to a module lint pass
- MissingDebugImplementations: MissingDebugImplementations::default(),
- // Keeps a global list of foreign declarations.
- ClashingExternDeclarations: ClashingExternDeclarations::new(),
- ]
- );
- };
-}
-
-macro_rules! late_lint_mod_passes {
- ($macro:path, $args:tt) => {
- $macro!(
- $args,
- [
- ForLoopsOverFallibles: ForLoopsOverFallibles,
- DerefIntoDynSupertrait: DerefIntoDynSupertrait,
- HardwiredLints: HardwiredLints,
- ImproperCTypesDeclarations: ImproperCTypesDeclarations,
- ImproperCTypesDefinitions: ImproperCTypesDefinitions,
- VariantSizeDifferences: VariantSizeDifferences,
- BoxPointers: BoxPointers,
- PathStatements: PathStatements,
- LetUnderscore: LetUnderscore,
- // Depends on referenced function signatures in expressions
- UnusedResults: UnusedResults,
- NonUpperCaseGlobals: NonUpperCaseGlobals,
- NonShorthandFieldPatterns: NonShorthandFieldPatterns,
- UnusedAllocation: UnusedAllocation,
- // Depends on types used in type definitions
- MissingCopyImplementations: MissingCopyImplementations,
- // Depends on referenced function signatures in expressions
- MutableTransmutes: MutableTransmutes,
- TypeAliasBounds: TypeAliasBounds,
- TrivialConstraints: TrivialConstraints,
- TypeLimits: TypeLimits::new(),
- NonSnakeCase: NonSnakeCase,
- InvalidNoMangleItems: InvalidNoMangleItems,
- // Depends on effective visibilities
- UnreachablePub: UnreachablePub,
- ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
- InvalidValue: InvalidValue,
- DerefNullPtr: DerefNullPtr,
- // May Depend on constants elsewhere
- UnusedBrokenConst: UnusedBrokenConst,
- UnstableFeatures: UnstableFeatures,
- ArrayIntoIter: ArrayIntoIter::default(),
- DropTraitConstraints: DropTraitConstraints,
- TemporaryCStringAsPtr: TemporaryCStringAsPtr,
- NonPanicFmt: NonPanicFmt,
- NoopMethodCall: NoopMethodCall,
- EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums,
- InvalidAtomicOrdering: InvalidAtomicOrdering,
- NamedAsmLabels: NamedAsmLabels,
- OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
- ]
- );
- };
-}
-
-macro_rules! declare_combined_late_pass {
- ([$v:vis $name:ident], $passes:tt) => (
- late_lint_methods!(declare_combined_late_lint_pass, [$v $name, $passes], ['tcx]);
- )
-}
-
-// FIXME: Make a separate lint type which do not require typeck tables
-late_lint_passes!(declare_combined_late_pass, [pub BuiltinCombinedLateLintPass]);
-
-late_lint_mod_passes!(declare_combined_late_pass, [BuiltinCombinedModuleLateLintPass]);
-
-pub fn new_lint_store(no_interleave_lints: bool, internal_lints: bool) -> LintStore {
+early_lint_methods!(
+ declare_combined_early_lint_pass,
+ [
+ pub BuiltinCombinedPreExpansionLintPass,
+ [
+ KeywordIdents: KeywordIdents,
+ ]
+ ]
+);
+
+early_lint_methods!(
+ declare_combined_early_lint_pass,
+ [
+ pub BuiltinCombinedEarlyLintPass,
+ [
+ UnusedParens: UnusedParens,
+ UnusedBraces: UnusedBraces,
+ UnusedImportBraces: UnusedImportBraces,
+ UnsafeCode: UnsafeCode,
+ SpecialModuleName: SpecialModuleName,
+ AnonymousParameters: AnonymousParameters,
+ EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(),
+ NonCamelCaseTypes: NonCamelCaseTypes,
+ DeprecatedAttr: DeprecatedAttr::new(),
+ WhileTrue: WhileTrue,
+ NonAsciiIdents: NonAsciiIdents,
+ HiddenUnicodeCodepoints: HiddenUnicodeCodepoints,
+ IncompleteFeatures: IncompleteFeatures,
+ RedundantSemicolons: RedundantSemicolons,
+ UnusedDocComment: UnusedDocComment,
+ UnexpectedCfgs: UnexpectedCfgs,
+ ]
+ ]
+);
+
+// FIXME: Make a separate lint type which does not require typeck tables.
+
+late_lint_methods!(
+ declare_combined_late_lint_pass,
+ [
+ pub BuiltinCombinedLateLintPass,
+ [
+ // Tracks state across modules
+ UnnameableTestItems: UnnameableTestItems::new(),
+ // Tracks attributes of parents
+ MissingDoc: MissingDoc::new(),
+ // Builds a global list of all impls of `Debug`.
+ // FIXME: Turn the computation of types which implement Debug into a query
+ // and change this to a module lint pass
+ MissingDebugImplementations: MissingDebugImplementations::default(),
+ // Keeps a global list of foreign declarations.
+ ClashingExternDeclarations: ClashingExternDeclarations::new(),
+ ]
+ ]
+);
+
+late_lint_methods!(
+ declare_combined_late_lint_pass,
+ [
+ BuiltinCombinedModuleLateLintPass,
+ [
+ ForLoopsOverFallibles: ForLoopsOverFallibles,
+ DerefIntoDynSupertrait: DerefIntoDynSupertrait,
+ HardwiredLints: HardwiredLints,
+ ImproperCTypesDeclarations: ImproperCTypesDeclarations,
+ ImproperCTypesDefinitions: ImproperCTypesDefinitions,
+ VariantSizeDifferences: VariantSizeDifferences,
+ BoxPointers: BoxPointers,
+ PathStatements: PathStatements,
+ LetUnderscore: LetUnderscore,
+ // Depends on referenced function signatures in expressions
+ UnusedResults: UnusedResults,
+ NonUpperCaseGlobals: NonUpperCaseGlobals,
+ NonShorthandFieldPatterns: NonShorthandFieldPatterns,
+ UnusedAllocation: UnusedAllocation,
+ // Depends on types used in type definitions
+ MissingCopyImplementations: MissingCopyImplementations,
+ // Depends on referenced function signatures in expressions
+ MutableTransmutes: MutableTransmutes,
+ TypeAliasBounds: TypeAliasBounds,
+ TrivialConstraints: TrivialConstraints,
+ TypeLimits: TypeLimits::new(),
+ NonSnakeCase: NonSnakeCase,
+ InvalidNoMangleItems: InvalidNoMangleItems,
+ // Depends on effective visibilities
+ UnreachablePub: UnreachablePub,
+ ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
+ InvalidValue: InvalidValue,
+ DerefNullPtr: DerefNullPtr,
+ // May Depend on constants elsewhere
+ UnusedBrokenConst: UnusedBrokenConst,
+ UnstableFeatures: UnstableFeatures,
+ ArrayIntoIter: ArrayIntoIter::default(),
+ DropTraitConstraints: DropTraitConstraints,
+ TemporaryCStringAsPtr: TemporaryCStringAsPtr,
+ NonPanicFmt: NonPanicFmt,
+ NoopMethodCall: NoopMethodCall,
+ EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums,
+ InvalidAtomicOrdering: InvalidAtomicOrdering,
+ NamedAsmLabels: NamedAsmLabels,
+ OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
+ ]
+ ]
+);
+
+pub fn new_lint_store(internal_lints: bool) -> LintStore {
let mut lint_store = LintStore::new();
- register_builtins(&mut lint_store, no_interleave_lints);
+ register_builtins(&mut lint_store);
if internal_lints {
register_internals(&mut lint_store);
}
/// Tell the `LintStore` about all the built-in lints (the ones
/// defined in this crate and the ones defined in
/// `rustc_session::lint::builtin`).
-fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
+fn register_builtins(store: &mut LintStore) {
macro_rules! add_lint_group {
($name:expr, $($lint:ident),*) => (
store.register_group(false, $name, None, vec![$(LintId::of($lint)),*]);
)
}
- macro_rules! register_early_pass {
- ($method:ident, $ty:ident, $constructor:expr) => {
- store.register_lints(&$ty::get_lints());
- store.$method(|| Box::new($constructor));
- };
- }
-
- macro_rules! register_late_pass {
- ($method:ident, $ty:ident, $constructor:expr) => {
- store.register_lints(&$ty::get_lints());
- store.$method(|_| Box::new($constructor));
- };
- }
-
- macro_rules! register_early_passes {
- ($method:ident, [$($passes:ident: $constructor:expr,)*]) => (
- $(
- register_early_pass!($method, $passes, $constructor);
- )*
- )
- }
-
- macro_rules! register_late_passes {
- ($method:ident, [$($passes:ident: $constructor:expr,)*]) => (
- $(
- register_late_pass!($method, $passes, $constructor);
- )*
- )
- }
-
- if no_interleave_lints {
- pre_expansion_lint_passes!(register_early_passes, register_pre_expansion_pass);
- early_lint_passes!(register_early_passes, register_early_pass);
- late_lint_passes!(register_late_passes, register_late_pass);
- late_lint_mod_passes!(register_late_passes, register_late_mod_pass);
- } else {
- store.register_lints(&BuiltinCombinedPreExpansionLintPass::get_lints());
- store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints());
- store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints());
- store.register_lints(&BuiltinCombinedLateLintPass::get_lints());
- }
+ store.register_lints(&BuiltinCombinedPreExpansionLintPass::get_lints());
+ store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints());
+ store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints());
+ store.register_lints(&BuiltinCombinedLateLintPass::get_lints());
add_lint_group!(
"nonstandard_style",
/// The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to
/// always be passed by value. This is usually used for types that are thin wrappers around
/// references, so there is no benefit to an extra layer of indirection. (Example: `Ty` which
- /// is a reference to an `Interned<TyS>`)
+ /// is a reference to an `Interned<TyKind>`)
pub rustc::PASS_BY_VALUE,
Warn,
"pass by reference of a type flagged as `#[rustc_pass_by_value]`",
use crate::context::{EarlyContext, LateContext};
use rustc_ast as ast;
-use rustc_data_structures::sync;
use rustc_hir as hir;
use rustc_session::lint::builtin::HardwiredLints;
use rustc_session::lint::LintPass;
#[macro_export]
macro_rules! late_lint_methods {
- ($macro:path, $args:tt, [$hir:tt]) => (
- $macro!($args, [$hir], [
- fn check_body(a: &$hir hir::Body<$hir>);
- fn check_body_post(a: &$hir hir::Body<$hir>);
+ ($macro:path, $args:tt) => (
+ $macro!($args, [
+ fn check_body(a: &'tcx hir::Body<'tcx>);
+ fn check_body_post(a: &'tcx hir::Body<'tcx>);
fn check_crate();
fn check_crate_post();
- fn check_mod(a: &$hir hir::Mod<$hir>, b: hir::HirId);
- fn check_foreign_item(a: &$hir hir::ForeignItem<$hir>);
- fn check_item(a: &$hir hir::Item<$hir>);
- fn check_item_post(a: &$hir hir::Item<$hir>);
- fn check_local(a: &$hir hir::Local<$hir>);
- fn check_block(a: &$hir hir::Block<$hir>);
- fn check_block_post(a: &$hir hir::Block<$hir>);
- fn check_stmt(a: &$hir hir::Stmt<$hir>);
- fn check_arm(a: &$hir hir::Arm<$hir>);
- fn check_pat(a: &$hir hir::Pat<$hir>);
- 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_param(a: &$hir hir::GenericParam<$hir>);
- fn check_generics(a: &$hir hir::Generics<$hir>);
- fn check_poly_trait_ref(a: &$hir hir::PolyTraitRef<$hir>);
+ fn check_mod(a: &'tcx hir::Mod<'tcx>, b: hir::HirId);
+ fn check_foreign_item(a: &'tcx hir::ForeignItem<'tcx>);
+ fn check_item(a: &'tcx hir::Item<'tcx>);
+ fn check_item_post(a: &'tcx hir::Item<'tcx>);
+ fn check_local(a: &'tcx hir::Local<'tcx>);
+ fn check_block(a: &'tcx hir::Block<'tcx>);
+ fn check_block_post(a: &'tcx hir::Block<'tcx>);
+ fn check_stmt(a: &'tcx hir::Stmt<'tcx>);
+ fn check_arm(a: &'tcx hir::Arm<'tcx>);
+ fn check_pat(a: &'tcx hir::Pat<'tcx>);
+ fn check_expr(a: &'tcx hir::Expr<'tcx>);
+ fn check_expr_post(a: &'tcx hir::Expr<'tcx>);
+ fn check_ty(a: &'tcx hir::Ty<'tcx>);
+ fn check_generic_param(a: &'tcx hir::GenericParam<'tcx>);
+ fn check_generics(a: &'tcx hir::Generics<'tcx>);
+ fn check_poly_trait_ref(a: &'tcx hir::PolyTraitRef<'tcx>);
fn check_fn(
- a: rustc_hir::intravisit::FnKind<$hir>,
- b: &$hir hir::FnDecl<$hir>,
- c: &$hir hir::Body<$hir>,
+ a: rustc_hir::intravisit::FnKind<'tcx>,
+ b: &'tcx hir::FnDecl<'tcx>,
+ c: &'tcx hir::Body<'tcx>,
d: Span,
e: hir::HirId);
- fn check_trait_item(a: &$hir hir::TraitItem<$hir>);
- fn check_impl_item(a: &$hir hir::ImplItem<$hir>);
- fn check_impl_item_post(a: &$hir hir::ImplItem<$hir>);
- fn check_struct_def(a: &$hir hir::VariantData<$hir>);
- fn check_field_def(a: &$hir hir::FieldDef<$hir>);
- fn check_variant(a: &$hir hir::Variant<$hir>);
- fn check_path(a: &$hir hir::Path<$hir>, b: hir::HirId);
- fn check_attribute(a: &$hir ast::Attribute);
+ fn check_trait_item(a: &'tcx hir::TraitItem<'tcx>);
+ fn check_impl_item(a: &'tcx hir::ImplItem<'tcx>);
+ fn check_impl_item_post(a: &'tcx hir::ImplItem<'tcx>);
+ fn check_struct_def(a: &'tcx hir::VariantData<'tcx>);
+ fn check_field_def(a: &'tcx hir::FieldDef<'tcx>);
+ fn check_variant(a: &'tcx hir::Variant<'tcx>);
+ fn check_path(a: &hir::Path<'tcx>, b: hir::HirId);
+ fn check_attribute(a: &'tcx ast::Attribute);
/// Called when entering a syntax node that can have lint attributes such
/// as `#[allow(...)]`. Called with *all* the attributes of that node.
- fn enter_lint_attrs(a: &$hir [ast::Attribute]);
+ fn enter_lint_attrs(a: &'tcx [ast::Attribute]);
/// Counterpart to `enter_lint_attrs`.
- fn exit_lint_attrs(a: &$hir [ast::Attribute]);
+ fn exit_lint_attrs(a: &'tcx [ast::Attribute]);
]);
)
}
// FIXME: eliminate the duplication with `Visitor`. But this also
// contains a few lint-specific methods with no equivalent in `Visitor`.
-macro_rules! expand_lint_pass_methods {
- ($context:ty, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
- $(#[inline(always)] fn $name(&mut self, _: $context, $(_: $arg),*) {})*
- )
-}
-
macro_rules! declare_late_lint_pass {
- ([], [$hir:tt], [$($methods:tt)*]) => (
- pub trait LateLintPass<$hir>: LintPass {
- expand_lint_pass_methods!(&LateContext<$hir>, [$($methods)*]);
+ ([], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
+ pub trait LateLintPass<'tcx>: LintPass {
+ $(#[inline(always)] fn $name(&mut self, _: &LateContext<'tcx>, $(_: $arg),*) {})*
}
)
}
-late_lint_methods!(declare_late_lint_pass, [], ['tcx]);
+// Declare the `LateLintPass` trait, which contains empty default definitions
+// for all the `check_*` methods.
+late_lint_methods!(declare_late_lint_pass, []);
impl LateLintPass<'_> for HardwiredLints {}
#[macro_export]
macro_rules! expand_combined_late_lint_pass_method {
- ([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({
- $($self.$passes.$name $params;)*
+ ([$($pass:ident),*], $self: ident, $name: ident, $params:tt) => ({
+ $($self.$pass.$name $params;)*
})
}
)
}
+/// Combines multiple lints passes into a single lint pass, at compile time,
+/// for maximum speed. Each `check_foo` method in `$methods` within this pass
+/// simply calls `check_foo` once per `$pass`. Compare with
+/// `LateLintPassObjects`, which is similar, but combines lint passes at
+/// runtime.
#[macro_export]
macro_rules! declare_combined_late_lint_pass {
- ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], [$hir:tt], $methods:tt) => (
+ ([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => (
#[allow(non_snake_case)]
$v struct $name {
- $($passes: $passes,)*
+ $($pass: $pass,)*
}
impl $name {
$v fn new() -> Self {
Self {
- $($passes: $constructor,)*
+ $($pass: $constructor,)*
}
}
$v fn get_lints() -> LintArray {
let mut lints = Vec::new();
- $(lints.extend_from_slice(&$passes::get_lints());)*
+ $(lints.extend_from_slice(&$pass::get_lints());)*
lints
}
}
impl<'tcx> LateLintPass<'tcx> for $name {
- expand_combined_late_lint_pass_methods!([$($passes),*], $methods);
+ expand_combined_late_lint_pass_methods!([$($pass),*], $methods);
}
#[allow(rustc::lint_pass_impl_without_macro)]
)
}
-macro_rules! expand_early_lint_pass_methods {
- ($context:ty, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
- $(#[inline(always)] fn $name(&mut self, _: $context, $(_: $arg),*) {})*
- )
-}
-
macro_rules! declare_early_lint_pass {
- ([], [$($methods:tt)*]) => (
+ ([], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
pub trait EarlyLintPass: LintPass {
- expand_early_lint_pass_methods!(&EarlyContext<'_>, [$($methods)*]);
+ $(#[inline(always)] fn $name(&mut self, _: &EarlyContext<'_>, $(_: $arg),*) {})*
}
)
}
+// Declare the `EarlyLintPass` trait, which contains empty default definitions
+// for all the `check_*` methods.
early_lint_methods!(declare_early_lint_pass, []);
#[macro_export]
macro_rules! expand_combined_early_lint_pass_method {
- ([$($passes:ident),*], $self: ident, $name: ident, $params:tt) => ({
- $($self.$passes.$name $params;)*
+ ([$($pass:ident),*], $self: ident, $name: ident, $params:tt) => ({
+ $($self.$pass.$name $params;)*
})
}
)
}
+/// Combines multiple lints passes into a single lint pass, at compile time,
+/// for maximum speed. Each `check_foo` method in `$methods` within this pass
+/// simply calls `check_foo` once per `$pass`. Compare with
+/// `EarlyLintPassObjects`, which is similar, but combines lint passes at
+/// runtime.
#[macro_export]
macro_rules! declare_combined_early_lint_pass {
- ([$v:vis $name:ident, [$($passes:ident: $constructor:expr,)*]], $methods:tt) => (
+ ([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => (
#[allow(non_snake_case)]
$v struct $name {
- $($passes: $passes,)*
+ $($pass: $pass,)*
}
impl $name {
$v fn new() -> Self {
Self {
- $($passes: $constructor,)*
+ $($pass: $constructor,)*
}
}
$v fn get_lints() -> LintArray {
let mut lints = Vec::new();
- $(lints.extend_from_slice(&$passes::get_lints());)*
+ $(lints.extend_from_slice(&$pass::get_lints());)*
lints
}
}
impl EarlyLintPass for $name {
- expand_combined_early_lint_pass_methods!([$($passes),*], $methods);
+ expand_combined_early_lint_pass_methods!([$($pass),*], $methods);
}
#[allow(rustc::lint_pass_impl_without_macro)]
}
/// A lint pass boxed up as a trait object.
-pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + 'static>;
-pub type LateLintPassObject<'tcx> = Box<dyn LateLintPass<'tcx> + sync::Send + 'tcx>;
+pub type EarlyLintPassObject = Box<dyn EarlyLintPass + 'static>;
+pub type LateLintPassObject<'tcx> = Box<dyn LateLintPass<'tcx> + 'tcx>;
use rustc_target::abi::{Integer, TagEncoding, Variants};
use rustc_target::spec::abi::Abi as SpecAbi;
-use std::cmp;
use std::iter;
use std::ops::ControlFlow;
_ => {}
};
- fn is_valid<T: cmp::PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
+ fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
match binop.node {
hir::BinOpKind::Lt => v > min && v <= max,
hir::BinOpKind::Le => v >= min && v < max,
lhs_needs_parens
|| (followed_by_block
&& match &inner.kind {
- ExprKind::Ret(_) | ExprKind::Break(..) | ExprKind::Yield(..) => true,
+ ExprKind::Ret(_)
+ | ExprKind::Break(..)
+ | ExprKind::Yield(..)
+ | ExprKind::Yeet(..) => true,
ExprKind::Range(_lhs, Some(rhs), _limits) => {
matches!(rhs.kind, ExprKind::Block(..))
}
left_pos: Option<BytePos>,
right_pos: Option<BytePos>,
) {
+ // If `value` has `ExprKind::Err`, unused delim lint can be broken.
+ // For example, the following code caused ICE.
+ // This is because the `ExprKind::Call` in `value` has `ExprKind::Err` as its argument
+ // and this leads to wrong spans. #104897
+ //
+ // ```
+ // fn f(){(print!(á
+ // ```
+ use rustc_ast::visit::{walk_expr, Visitor};
+ struct ErrExprVisitor {
+ has_error: bool,
+ }
+ impl<'ast> Visitor<'ast> for ErrExprVisitor {
+ fn visit_expr(&mut self, expr: &'ast ast::Expr) {
+ if let ExprKind::Err = expr.kind {
+ self.has_error = true;
+ return;
+ }
+ walk_expr(self, expr)
+ }
+ }
+ let mut visitor = ErrExprVisitor { has_error: false };
+ visitor.visit_expr(value);
+ if visitor.has_error {
+ return;
+ }
let spans = match value.kind {
ast::ExprKind::Block(ref block, None) if block.stmts.len() == 1 => {
- if let StmtKind::Expr(expr) = &block.stmts[0].kind
- && let ExprKind::Err = expr.kind
- {
- return
- }
if let Some(span) = block.stmts[0].span.find_ancestor_inside(value.span) {
Some((value.span.with_hi(span.lo()), value.span.with_lo(span.hi())))
} else {
}
impl EarlyLintPass for UnusedParens {
+ #[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
match e.kind {
ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => {
<Self as UnusedDelimLint>::check_stmt(self, cx, s)
}
+ #[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
<Self as UnusedDelimLint>::check_expr(self, cx, e);
// Trigger the lint if the nested item is a non-self single item
let node_name = match items[0].0.kind {
- ast::UseTreeKind::Simple(rename, ..) => {
+ ast::UseTreeKind::Simple(rename) => {
let orig_ident = items[0].0.prefix.segments.last().unwrap().ident;
if orig_ident.name == kw::SelfLower {
return;
.file("llvm-wrapper/RustWrapper.cpp")
.file("llvm-wrapper/ArchiveWrapper.cpp")
.file("llvm-wrapper/CoverageMappingWrapper.cpp")
+ .file("llvm-wrapper/SymbolWrapper.cpp")
.file("llvm-wrapper/Linker.cpp")
.cpp(true)
.cpp_link_stdlib(None) // we handle this below
if !is_crossed {
cmd.arg("--system-libs");
- } else if target.contains("windows-gnu") {
- println!("cargo:rustc-link-lib=shell32");
- println!("cargo:rustc-link-lib=uuid");
- } else if target.contains("netbsd") || target.contains("haiku") || target.contains("darwin") {
- println!("cargo:rustc-link-lib=z");
- } else if target.starts_with("arm")
+ }
+
+ if (target.starts_with("arm") && !target.contains("freebsd"))
|| target.starts_with("mips-")
|| target.starts_with("mipsel-")
|| target.starts_with("powerpc-")
{
// 32-bit targets need to link libatomic.
println!("cargo:rustc-link-lib=atomic");
+ } else if target.contains("windows-gnu") {
+ println!("cargo:rustc-link-lib=shell32");
+ println!("cargo:rustc-link-lib=uuid");
+ } else if target.contains("netbsd") || target.contains("haiku") || target.contains("darwin") {
+ println!("cargo:rustc-link-lib=z");
}
cmd.args(&components);
None,
};
-static Optional<CodeModel::Model> fromRust(LLVMRustCodeModel Model) {
+#if LLVM_VERSION_LT(16, 0)
+static Optional<CodeModel::Model>
+#else
+static std::optional<CodeModel::Model>
+#endif
+fromRust(LLVMRustCodeModel Model) {
switch (Model) {
case LLVMRustCodeModel::Tiny:
return CodeModel::Tiny;
case LLVMRustCodeModel::Large:
return CodeModel::Large;
case LLVMRustCodeModel::None:
+#if LLVM_VERSION_LT(16, 0)
return None;
+#else
+ return std::nullopt;
+#endif
default:
report_fatal_error("Bad CodeModel.");
}
LLVMSelfProfileInitializeCallbacks(PIC,LlvmSelfProfiler,BeforePassCallback,AfterPassCallback);
}
+#if LLVM_VERSION_LT(16, 0)
Optional<PGOOptions> PGOOpt;
+#else
+ std::optional<PGOOptions> PGOOpt;
+#endif
if (PGOGenPath) {
assert(!PGOUsePath && !PGOSampleUsePath);
PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr,
#include "llvm/Pass.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/Support/Signals.h"
+#if LLVM_VERSION_LT(16, 0)
#include "llvm/ADT/Optional.h"
+#endif
#include <iostream>
}
extern "C" LLVMAttributeRef LLVMRustCreateAllocSizeAttr(LLVMContextRef C, uint32_t ElementSizeArg) {
- return wrap(Attribute::getWithAllocSizeArgs(*unwrap(C), ElementSizeArg, None));
+ return wrap(Attribute::getWithAllocSizeArgs(*unwrap(C), ElementSizeArg,
+#if LLVM_VERSION_LT(16, 0)
+ None
+#else
+ std::nullopt
+#endif
+ ));
}
#if LLVM_VERSION_GE(15, 0)
SHA256,
};
+#if LLVM_VERSION_LT(16, 0)
static Optional<DIFile::ChecksumKind> fromRust(LLVMRustChecksumKind Kind) {
+#else
+static std::optional<DIFile::ChecksumKind> fromRust(LLVMRustChecksumKind Kind) {
+#endif
switch (Kind) {
case LLVMRustChecksumKind::None:
+#if LLVM_VERSION_LT(16, 0)
return None;
+#else
+ return std::nullopt;
+#endif
case LLVMRustChecksumKind::MD5:
return DIFile::ChecksumKind::CSK_MD5;
case LLVMRustChecksumKind::SHA1:
const char *Filename, size_t FilenameLen,
const char *Directory, size_t DirectoryLen, LLVMRustChecksumKind CSKind,
const char *Checksum, size_t ChecksumLen) {
+
+#if LLVM_VERSION_LT(16, 0)
Optional<DIFile::ChecksumKind> llvmCSKind = fromRust(CSKind);
+#else
+ std::optional<DIFile::ChecksumKind> llvmCSKind = fromRust(CSKind);
+#endif
+
+#if LLVM_VERSION_LT(16, 0)
Optional<DIFile::ChecksumInfo<StringRef>> CSInfo{};
+#else
+ std::optional<DIFile::ChecksumInfo<StringRef>> CSInfo{};
+#endif
if (llvmCSKind)
CSInfo.emplace(*llvmCSKind, StringRef{Checksum, ChecksumLen});
return wrap(Builder->createFile(StringRef(Filename, FilenameLen),
extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
LLVMValueRef *Args, unsigned NumArgs,
- OperandBundleDef *Bundle) {
+ OperandBundleDef **OpBundles,
+ unsigned NumOpBundles) {
Value *Callee = unwrap(Fn);
FunctionType *FTy = unwrap<FunctionType>(Ty);
- unsigned Len = Bundle ? 1 : 0;
- ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
return wrap(unwrap(B)->CreateCall(
- FTy, Callee, makeArrayRef(unwrap(Args), NumArgs), Bundles));
+ FTy, Callee, makeArrayRef(unwrap(Args), NumArgs),
+ makeArrayRef(*OpBundles, NumOpBundles)));
}
extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) {
LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
LLVMValueRef *Args, unsigned NumArgs,
LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
- OperandBundleDef *Bundle, const char *Name) {
+ OperandBundleDef **OpBundles, unsigned NumOpBundles,
+ const char *Name) {
Value *Callee = unwrap(Fn);
FunctionType *FTy = unwrap<FunctionType>(Ty);
- unsigned Len = Bundle ? 1 : 0;
- ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch),
makeArrayRef(unwrap(Args), NumArgs),
- Bundles, Name));
+ makeArrayRef(*OpBundles, NumOpBundles),
+ Name));
}
extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B,
#endif
return -1;
}
+
+extern "C" bool LLVMRustIsBitcode(char *ptr, size_t len) {
+ return identify_magic(StringRef(ptr, len)) == file_magic::bitcode;
+}
--- /dev/null
+// Derived from code in LLVM, which is:
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+// Derived from:
+// * https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/include/llvm/Object/ArchiveWriter.h
+// * https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/lib/Object/ArchiveWriter.cpp
+
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/ADT/Optional.h"
+
+using namespace llvm;
+using namespace llvm::sys;
+using namespace llvm::object;
+
+static bool isArchiveSymbol(const object::BasicSymbolRef &S) {
+ Expected<uint32_t> SymFlagsOrErr = S.getFlags();
+ if (!SymFlagsOrErr)
+ // FIXME: Actually report errors helpfully.
+ report_fatal_error(SymFlagsOrErr.takeError());
+ if (*SymFlagsOrErr & object::SymbolRef::SF_FormatSpecific)
+ return false;
+ if (!(*SymFlagsOrErr & object::SymbolRef::SF_Global))
+ return false;
+ if (*SymFlagsOrErr & object::SymbolRef::SF_Undefined)
+ return false;
+ return true;
+}
+
+typedef void *(*LLVMRustGetSymbolsCallback)(void *, const char *);
+typedef void *(*LLVMRustGetSymbolsErrorCallback)(const char *);
+
+// Note: This is implemented in C++ instead of using the C api from Rust as IRObjectFile doesn't
+// implement getSymbolName, only printSymbolName, which is inaccessible from the C api.
+extern "C" void *LLVMRustGetSymbols(
+ char *BufPtr, size_t BufLen, void *State, LLVMRustGetSymbolsCallback Callback,
+ LLVMRustGetSymbolsErrorCallback ErrorCallback) {
+ std::unique_ptr<MemoryBuffer> Buf =
+ MemoryBuffer::getMemBuffer(StringRef(BufPtr, BufLen), StringRef("LLVMRustGetSymbolsObject"),
+ false);
+ SmallString<0> SymNameBuf;
+ raw_svector_ostream SymName(SymNameBuf);
+
+ // In the scenario when LLVMContext is populated SymbolicFile will contain a
+ // reference to it, thus SymbolicFile should be destroyed first.
+ LLVMContext Context;
+ std::unique_ptr<object::SymbolicFile> Obj;
+
+ const file_magic Type = identify_magic(Buf->getBuffer());
+ if (!object::SymbolicFile::isSymbolicFile(Type, &Context)) {
+ return 0;
+ }
+
+ if (Type == file_magic::bitcode) {
+ auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
+ Buf->getMemBufferRef(), file_magic::bitcode, &Context);
+ if (!ObjOrErr) {
+ Error E = ObjOrErr.takeError();
+ SmallString<0> ErrorBuf;
+ raw_svector_ostream Error(ErrorBuf);
+ Error << E << '\0';
+ return ErrorCallback(Error.str().data());
+ }
+ Obj = std::move(*ObjOrErr);
+ } else {
+ auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf->getMemBufferRef());
+ if (!ObjOrErr) {
+ Error E = ObjOrErr.takeError();
+ SmallString<0> ErrorBuf;
+ raw_svector_ostream Error(ErrorBuf);
+ Error << E << '\0';
+ return ErrorCallback(Error.str().data());
+ }
+ Obj = std::move(*ObjOrErr);
+ }
+
+
+ for (const object::BasicSymbolRef &S : Obj->symbols()) {
+ if (!isArchiveSymbol(S))
+ continue;
+ if (Error E = S.printName(SymName)) {
+ SmallString<0> ErrorBuf;
+ raw_svector_ostream Error(ErrorBuf);
+ Error << E << '\0';
+ return ErrorCallback(Error.str().data());
+ }
+ SymName << '\0';
+ if (void *E = Callback(State, SymNameBuf.str().data())) {
+ return E;
+ }
+ SymNameBuf.clear();
+ }
+ return 0;
+}
let DiagnosticDerive { mut structure, mut builder } = self;
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
- let preamble = builder.preamble(&variant);
- let body = builder.body(&variant);
+ let preamble = builder.preamble(variant);
+ let body = builder.body(variant);
let diag = &builder.parent.diag;
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else {
let init = match builder.slug.value_ref() {
None => {
span_err(builder.span, "diagnostic slug not specified")
- .help(&format!(
+ .help(format!(
"specify the slug as the first argument to the `#[diag(...)]` \
attribute, such as `#[diag(hir_analysis_example_error)]`",
))
}
Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => {
span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match")
- .note(&format!(
+ .note(format!(
"slug is `{slug_name}` but the crate name is `{crate_name}`"
))
- .help(&format!(
+ .help(format!(
"expected a slug starting with `{slug_prefix}_...`"
))
.emit();
let LintDiagnosticDerive { mut structure, mut builder } = self;
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
- let preamble = builder.preamble(&variant);
- let body = builder.body(&variant);
+ let preamble = builder.preamble(variant);
+ let body = builder.body(variant);
let diag = &builder.parent.diag;
let formatting_init = &builder.formatting_init;
let msg = builder.each_variant(&mut structure, |mut builder, variant| {
// Collect the slug by generating the preamble.
- let _ = builder.preamble(&variant);
+ let _ = builder.preamble(variant);
match builder.slug.value_ref() {
None => {
span_err(builder.span, "diagnostic slug not specified")
- .help(&format!(
+ .help(format!(
"specify the slug as the first argument to the attribute, such as \
`#[diag(compiletest_example)]`",
))
.emit();
- return DiagnosticDeriveError::ErrorHandled.to_compile_error();
+ DiagnosticDeriveError::ErrorHandled.to_compile_error()
}
Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => {
span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match")
- .note(&format!(
+ .note(format!(
"slug is `{slug_name}` but the crate name is `{crate_name}`"
))
- .help(&format!(
+ .help(format!(
"expected a slug starting with `{slug_prefix}_...`"
))
.emit();
- return DiagnosticDeriveError::ErrorHandled.to_compile_error();
+ DiagnosticDeriveError::ErrorHandled.to_compile_error()
}
Some(slug) => {
quote! {
_ => variant.ast().ident.span().unwrap(),
};
let builder = DiagnosticDeriveVariantBuilder {
- parent: &self,
+ parent: self,
span,
field_map: build_field_mapping(variant),
formatting_init: TokenStream::new(),
nested_iter.next();
}
Some(NestedMeta::Meta(Meta::NameValue { .. })) => {}
- Some(nested_attr) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| diag
+ Some(nested_attr) => throw_invalid_nested_attr!(attr, nested_attr, |diag| diag
.help("a diagnostic slug is required as the first argument")),
None => throw_invalid_attr!(attr, &meta, |diag| diag
.help("a diagnostic slug is required as the first argument")),
..
})) => (value, path),
NestedMeta::Meta(Meta::Path(_)) => {
- invalid_nested_attr(attr, &nested_attr)
+ invalid_nested_attr(attr, nested_attr)
.help("diagnostic slug must be the first argument")
.emit();
continue;
}
_ => {
- invalid_nested_attr(attr, &nested_attr).emit();
+ invalid_nested_attr(attr, nested_attr).emit();
continue;
}
};
#diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
});
}
- _ => invalid_nested_attr(attr, &nested_attr)
+ _ => invalid_nested_attr(attr, nested_attr)
.help("only `code` is a valid nested attributes following the slug")
.emit(),
}
Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug))
}
SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => {
- if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
+ if type_matches_path(info.ty, &["rustc_span", "Span"]) {
Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug))
- } else if type_is_unit(&info.ty) {
+ } else if type_is_unit(info.ty) {
Ok(self.add_subdiagnostic(&fn_ident, slug))
} else {
report_type_error(attr, "`Span` or `()`")?
let mut code = None;
for nested_attr in list.nested.iter() {
let NestedMeta::Meta(ref meta) = nested_attr else {
- throw_invalid_nested_attr!(attr, &nested_attr);
+ throw_invalid_nested_attr!(attr, nested_attr);
};
let span = meta.span().unwrap();
);
code.set_once((code_field, formatting_init), span);
}
- _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+ _ => throw_invalid_nested_attr!(attr, nested_attr, |diag| {
diag.help("`code` is the only valid nested attribute")
}),
}
path: &[&str],
ty_name: &str,
) -> Result<(), DiagnosticDeriveError> {
- if !type_matches_path(&info.ty, path) {
+ if !type_matches_path(info.ty, path) {
report_type_error(attr, ty_name)?;
}
attr: &Attribute,
info: &FieldInfo<'_>,
) -> Result<(), DiagnosticDeriveError> {
- if !type_matches_path(&info.ty, &["rustc_span", "Span"])
- && !type_matches_path(&info.ty, &["rustc_errors", "MultiSpan"])
+ if !type_matches_path(info.ty, &["rustc_span", "Span"])
+ && !type_matches_path(info.ty, &["rustc_errors", "MultiSpan"])
{
report_type_error(attr, "`Span` or `MultiSpan`")?;
}
let meta = match nested_attr {
NestedMeta::Meta(ref meta) => meta,
NestedMeta::Lit(_) => {
- invalid_nested_attr(attr, &nested_attr).emit();
+ invalid_nested_attr(attr, nested_attr).emit();
continue;
}
};
let string_value = match meta {
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => Some(value),
- Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+ Meta::Path(_) => throw_invalid_nested_attr!(attr, nested_attr, |diag| {
diag.help("a diagnostic slug must be the first argument to the attribute")
}),
_ => None,
| SubdiagnosticKind::MultipartSuggestion { ref mut applicability, .. },
) => {
let Some(value) = string_value else {
- invalid_nested_attr(attr, &nested_attr).emit();
+ invalid_nested_attr(attr, nested_attr).emit();
continue;
};
| SubdiagnosticKind::MultipartSuggestion { .. },
) => {
let Some(value) = string_value else {
- invalid_nested_attr(attr, &nested_attr).emit();
+ invalid_nested_attr(attr, nested_attr).emit();
continue;
};
// Invalid nested attribute
(_, SubdiagnosticKind::Suggestion { .. }) => {
- invalid_nested_attr(attr, &nested_attr)
+ invalid_nested_attr(attr, nested_attr)
.help(
"only `style`, `code` and `applicability` are valid nested attributes",
)
.emit();
}
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
- invalid_nested_attr(attr, &nested_attr)
+ invalid_nested_attr(attr, nested_attr)
.help("only `style` and `applicability` are valid nested attributes")
.emit()
}
_ => {
- invalid_nested_attr(attr, &nested_attr).emit();
+ invalid_nested_attr(attr, nested_attr).emit();
}
}
}
modifiers.eval_always.is_none(),
"Query {name} cannot be both `feedable` and `eval_always`."
);
- assert!(
- modifiers.no_hash.is_none(),
- "Query {name} cannot be both `feedable` and `no_hash`."
- );
feedable_queries.extend(quote! {
#(#doc_comments)*
[#attribute_stream] fn #name(#arg) #result,
use rustc_ast::{self as ast, *};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::svh::Svh;
-use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::sync::{Lrc, ReadGuard};
use rustc_expand::base::SyntaxExtension;
use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, LOCAL_CRATE};
use rustc_hir::definitions::Definitions;
pub struct CrateLoader<'a> {
// Immutable configuration.
sess: &'a Session,
- metadata_loader: Box<MetadataLoaderDyn>,
+ metadata_loader: &'a MetadataLoaderDyn,
+ definitions: ReadGuard<'a, Definitions>,
local_crate_name: Symbol,
// Mutable output.
- cstore: CStore,
- used_extern_options: FxHashSet<Symbol>,
+ cstore: &'a mut CStore,
+ used_extern_options: &'a mut FxHashSet<Symbol>,
}
pub enum LoadedMacro {
);
}
}
+
+ pub fn new(sess: &Session) -> CStore {
+ let mut stable_crate_ids = FxHashMap::default();
+ stable_crate_ids.insert(sess.local_stable_crate_id(), LOCAL_CRATE);
+ CStore {
+ // We add an empty entry for LOCAL_CRATE (which maps to zero) in
+ // order to make array indices in `metas` match with the
+ // corresponding `CrateNum`. This first entry will always remain
+ // `None`.
+ metas: IndexVec::from_elem_n(None, 1),
+ injected_panic_runtime: None,
+ allocator_kind: None,
+ alloc_error_handler_kind: None,
+ has_global_allocator: false,
+ has_alloc_error_handler: false,
+ stable_crate_ids,
+ unused_externs: Vec::new(),
+ }
+ }
}
impl<'a> CrateLoader<'a> {
pub fn new(
sess: &'a Session,
- metadata_loader: Box<MetadataLoaderDyn>,
- local_crate_name: &str,
+ metadata_loader: &'a MetadataLoaderDyn,
+ local_crate_name: Symbol,
+ cstore: &'a mut CStore,
+ definitions: ReadGuard<'a, Definitions>,
+ used_extern_options: &'a mut FxHashSet<Symbol>,
) -> Self {
- let mut stable_crate_ids = FxHashMap::default();
- stable_crate_ids.insert(sess.local_stable_crate_id(), LOCAL_CRATE);
-
CrateLoader {
sess,
metadata_loader,
- local_crate_name: Symbol::intern(local_crate_name),
- cstore: CStore {
- // We add an empty entry for LOCAL_CRATE (which maps to zero) in
- // order to make array indices in `metas` match with the
- // corresponding `CrateNum`. This first entry will always remain
- // `None`.
- metas: IndexVec::from_elem_n(None, 1),
- injected_panic_runtime: None,
- allocator_kind: None,
- alloc_error_handler_kind: None,
- has_global_allocator: false,
- has_alloc_error_handler: false,
- stable_crate_ids,
- unused_externs: Vec::new(),
- },
- used_extern_options: Default::default(),
+ local_crate_name,
+ cstore,
+ used_extern_options,
+ definitions,
}
}
-
pub fn cstore(&self) -> &CStore {
&self.cstore
}
- pub fn into_cstore(self) -> CStore {
- self.cstore
- }
-
fn existing_match(&self, name: Symbol, hash: Option<Svh>, kind: PathKind) -> Option<CrateNum> {
for (cnum, data) in self.cstore.iter_crate_data() {
if data.name() != name {
pub fn process_extern_crate(
&mut self,
item: &ast::Item,
- definitions: &Definitions,
def_id: LocalDefId,
) -> Option<CrateNum> {
match item.kind {
);
let name = match orig_name {
Some(orig_name) => {
- validate_crate_name(self.sess, orig_name.as_str(), Some(item.span));
+ validate_crate_name(self.sess, orig_name, Some(item.span));
orig_name
}
None => item.ident.name,
let cnum = self.resolve_crate(name, item.span, dep_kind)?;
- let path_len = definitions.def_path(def_id).data.len();
+ let path_len = self.definitions.def_path(def_id).data.len();
self.update_extern_crate(
cnum,
ExternCrate {
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
-use rustc_session::config::{CrateType, OutputFilenames, OutputType};
+use rustc_session::config::{CrateType, OutputType};
use rustc_session::output::filename_for_metadata;
use rustc_session::Session;
use tempfile::Builder as TempFileBuilder;
out_filename
}
-pub fn encode_and_write_metadata(
- tcx: TyCtxt<'_>,
- outputs: &OutputFilenames,
-) -> (EncodedMetadata, bool) {
+pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum MetadataKind {
None,
.unwrap_or(MetadataKind::None);
let crate_name = tcx.crate_name(LOCAL_CRATE);
- let out_filename = filename_for_metadata(tcx.sess, crate_name.as_str(), outputs);
+ let out_filename = filename_for_metadata(tcx.sess, crate_name, tcx.output_filenames(()));
// To avoid races with another rustc process scanning the output directory,
// we need to write the file somewhere else and atomically move it to its
// final destination, with an `fs::rename` call. In order for the rename to
fn as_any(&self) -> &dyn Any {
self
}
+ fn untracked_as_any(&mut self) -> &mut dyn Any {
+ self
+ }
fn crate_name(&self, cnum: CrateNum) -> Symbol {
self.get_crate_data(cnum).root.name
debug!("EncodeContext::encode_info_for_trait_item({:?})", def_id);
let tcx = self.tcx;
- let ast_item = tcx.hir().expect_trait_item(def_id.expect_local());
- self.tables.impl_defaultness.set(def_id.index, ast_item.defaultness);
+ let impl_defaultness = tcx.impl_defaultness(def_id.expect_local());
+ self.tables.impl_defaultness.set(def_id.index, impl_defaultness);
let trait_item = tcx.associated_item(def_id);
self.tables.assoc_container.set(def_id.index, trait_item.container);
match trait_item.kind {
ty::AssocKind::Const => {}
ty::AssocKind::Fn => {
- let hir::TraitItemKind::Fn(m_sig, m) = &ast_item.kind else { bug!() };
- match *m {
- hir::TraitFn::Required(ref names) => {
- record_array!(self.tables.fn_arg_names[def_id] <- *names)
- }
- hir::TraitFn::Provided(body) => {
- record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body))
- }
- };
- self.tables.asyncness.set(def_id.index, m_sig.header.asyncness);
+ record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id));
+ self.tables.asyncness.set(def_id.index, tcx.asyncness(def_id));
self.tables.constness.set(def_id.index, hir::Constness::NotConst);
}
ty::AssocKind::Type => {
use rustc_serialize::opaque::FileEncoder;
use rustc_serialize::Encoder as _;
use rustc_span::hygiene::MacroKind;
-use std::convert::TryInto;
use std::marker::PhantomData;
use std::num::NonZeroUsize;
+#![allow(rustc::usage_of_ty_tykind)]
+
/// This higher-order macro declares a list of types which can be allocated by `Arena`.
///
/// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]` where `T` is the type
[decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>,
[decode] borrowck_result:
rustc_middle::mir::BorrowCheckResult<'tcx>,
+ [] resolver: rustc_data_structures::steal::Steal<rustc_middle::ty::ResolverAstLowering>,
[decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult,
[decode] code_region: rustc_middle::mir::coverage::CodeRegion,
[] const_allocs: rustc_middle::mir::interpret::Allocation,
[] hir_id_set: rustc_hir::HirIdSet,
// Interned types
- [] tys: rustc_data_structures::intern::WithStableHash<rustc_middle::ty::TyS<'tcx>>,
- [] predicates: rustc_data_structures::intern::WithStableHash<rustc_middle::ty::PredicateS<'tcx>>,
+ [] tys: rustc_type_ir::WithCachedTypeInfo<rustc_middle::ty::TyKind<'tcx>>,
+ [] predicates: rustc_type_ir::WithCachedTypeInfo<rustc_middle::ty::PredicateKind<'tcx>>,
[] consts: rustc_middle::ty::ConstS<'tcx>,
// Note that this deliberately duplicates items in the `rustc_hir::arena`,
use rustc_middle::hir::nested_filter;
use rustc_span::def_id::StableCrateId;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
use rustc_target::spec::abi::Abi;
#[inline]
..
}) => {
// Ensure that the returned span has the item's SyntaxContext.
- fn_decl_span.find_ancestor_in_same_ctxt(*span).unwrap_or(*span)
+ fn_decl_span.find_ancestor_inside(*span).unwrap_or(*span)
}
_ => self.span_with_body(hir_id),
};
.filter_map(|(def_id, info)| {
let _ = info.as_owner()?;
let def_path_hash = definitions.def_path_hash(def_id);
- let span = resolutions.source_span.get(def_id).unwrap_or(&DUMMY_SP);
+ let span = tcx.source_span(def_id);
debug_assert_eq!(span.parent(), None);
Some((def_path_hash, span))
})
providers.hir_attrs = |tcx, id| {
tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs)
};
- providers.source_span =
- |tcx, def_id| tcx.resolutions(()).source_span.get(def_id).copied().unwrap_or(DUMMY_SP);
providers.def_span = |tcx, def_id| {
let def_id = def_id.expect_local();
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
/// The `#[target_feature(enable = "...")]` attribute and the enabled
/// features (only enabled features are supported right now).
pub target_features: Vec<Symbol>,
- /// The `#[linkage = "..."]` attribute and the value we found.
+ /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found.
pub linkage: Option<Linkage>,
+ /// The `#[linkage = "..."]` attribute on foreign items and the value we found.
+ pub import_linkage: Option<Linkage>,
/// The `#[link_section = "..."]` attribute, or what executable section this
/// should be placed in.
pub link_section: Option<Symbol>,
link_ordinal: None,
target_features: vec![],
linkage: None,
+ import_linkage: None,
link_section: None,
no_sanitize: SanitizerSet::empty(),
instruction_set: None,
use rustc_macros::HashStable;
use rustc_span::Symbol;
-use std::cmp::Ord;
use std::fmt::{self, Debug, Formatter};
rustc_index::newtype_index! {
/// Interned types generally have an `Outer` type and an `Inner` type, where
/// `Outer` is a newtype around `Interned<Inner>`, and all the operations are
/// done on `Outer`, because all occurrences are interned. E.g. `Ty` is an
-/// outer type and `TyS` is its inner type.
+/// outer type and `TyKind` is its inner type.
///
/// Here things are different because only const allocations are interned. This
/// means that both the inner type (`Allocation`) and the outer type
mod queries;
mod value;
-use std::convert::TryFrom;
use std::fmt;
use std::io;
use std::io::{Read, Write};
use rustc_macros::HashStable;
use rustc_target::abi::{HasDataLayout, Size};
-use std::convert::{TryFrom, TryInto};
use std::fmt;
////////////////////////////////////////////////////////////////////////////////
-use std::convert::{TryFrom, TryInto};
use std::fmt;
use either::{Either, Left, Right};
use either::Either;
use std::borrow::Cow;
-use std::convert::TryInto;
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::ops::{ControlFlow, Index, IndexMut};
use std::{iter, mem};
/// pass will be named after the type, and it will consist of a main
/// loop that goes over each available MIR and applies `run_pass`.
pub trait MirPass<'tcx> {
- fn name(&self) -> Cow<'_, str> {
+ fn name(&self) -> &str {
let name = std::any::type_name::<Self>();
- if let Some(tail) = name.rfind(':') {
- Cow::from(&name[tail + 1..])
- } else {
- Cow::from(name)
- }
+ if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name }
}
/// Returns `true` if this pass is enabled with the current combination of compiler flags.
}
}
-impl Display for MirPhase {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- match self {
- MirPhase::Built => write!(f, "built"),
- MirPhase::Analysis(p) => write!(f, "analysis-{}", p),
- MirPhase::Runtime(p) => write!(f, "runtime-{}", p),
- }
- }
-}
-
-impl Display for AnalysisPhase {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- match self {
- AnalysisPhase::Initial => write!(f, "initial"),
- AnalysisPhase::PostCleanup => write!(f, "post_cleanup"),
- }
- }
-}
-
-impl Display for RuntimePhase {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- match self {
- RuntimePhase::Initial => write!(f, "initial"),
- RuntimePhase::PostCleanup => write!(f, "post_cleanup"),
- RuntimePhase::Optimized => write!(f, "optimized"),
- }
- }
-}
-
/// Where a specific `mir::Body` comes from.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
let mut body = Body {
phase: MirPhase::Built,
- pass_count: 1,
+ pass_count: 0,
source,
basic_blocks: BasicBlocks::new(basic_blocks),
source_scopes,
pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
let mut body = Body {
phase: MirPhase::Built,
- pass_count: 1,
+ pass_count: 0,
source: MirSource::item(CRATE_DEF_ID.to_def_id()),
basic_blocks: BasicBlocks::new(basic_blocks),
source_scopes: IndexVec::new(),
Pointer, Provenance,
};
use rustc_middle::mir::visit::Visitor;
-use rustc_middle::mir::MirSource;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, TyCtxt};
use rustc_target::abi::Size;
#[inline]
pub fn dump_mir<'tcx, F>(
tcx: TyCtxt<'tcx>,
- pass_num: Option<&dyn Display>,
+ pass_num: bool,
pass_name: &str,
disambiguator: &dyn Display,
body: &Body<'tcx>,
fn dump_matched_mir_node<'tcx, F>(
tcx: TyCtxt<'tcx>,
- pass_num: Option<&dyn Display>,
+ pass_num: bool,
pass_name: &str,
disambiguator: &dyn Display,
body: &Body<'tcx>,
F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
{
let _: io::Result<()> = try {
- let mut file =
- create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body.source)?;
+ let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?;
// see notes on #41697 above
let def_path =
ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
if tcx.sess.opts.unstable_opts.dump_mir_graphviz {
let _: io::Result<()> = try {
- let mut file =
- create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body.source)?;
+ let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body)?;
write_mir_fn_graphviz(tcx, body, false, &mut file)?;
};
}
if let Some(spanview) = tcx.sess.opts.unstable_opts.dump_mir_spanview {
let _: io::Result<()> = try {
- let file_basename =
- dump_file_basename(tcx, pass_num, pass_name, disambiguator, body.source);
+ let file_basename = dump_file_basename(tcx, pass_num, pass_name, disambiguator, body);
let mut file = create_dump_file_with_basename(tcx, &file_basename, "html")?;
if body.source.def_id().is_local() {
write_mir_fn_spanview(tcx, body, spanview, &file_basename, &mut file)?;
/// where we should dump a MIR representation output files.
fn dump_file_basename<'tcx>(
tcx: TyCtxt<'tcx>,
- pass_num: Option<&dyn Display>,
+ pass_num: bool,
pass_name: &str,
disambiguator: &dyn Display,
- source: MirSource<'tcx>,
+ body: &Body<'tcx>,
) -> String {
+ let source = body.source;
let promotion_id = match source.promoted {
Some(id) => format!("-{:?}", id),
None => String::new(),
let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
String::new()
} else {
- match pass_num {
- None => ".-------".to_string(),
- Some(pass_num) => format!(".{}", pass_num),
+ if pass_num {
+ format!(".{:03}-{:03}", body.phase.phase_index(), body.pass_count)
+ } else {
+ ".-------".to_string()
}
};
pub fn create_dump_file<'tcx>(
tcx: TyCtxt<'tcx>,
extension: &str,
- pass_num: Option<&dyn Display>,
+ pass_num: bool,
pass_name: &str,
disambiguator: &dyn Display,
- source: MirSource<'tcx>,
+ body: &Body<'tcx>,
) -> io::Result<io::BufWriter<fs::File>> {
create_dump_file_with_basename(
tcx,
- &dump_file_basename(tcx, pass_num, pass_name, disambiguator, source),
+ &dump_file_basename(tcx, pass_num, pass_name, disambiguator, body),
extension,
)
}
use super::{BasicBlock, Constant, Field, Local, SwitchTargets, UserTypeProjection};
use crate::mir::coverage::{CodeRegion, CoverageKind};
+use crate::traits::Reveal;
use crate::ty::adjustment::PointerCast;
use crate::ty::subst::SubstsRef;
use crate::ty::{self, List, Ty};
Runtime(RuntimePhase),
}
+impl MirPhase {
+ pub fn name(&self) -> &'static str {
+ match *self {
+ MirPhase::Built => "built",
+ MirPhase::Analysis(AnalysisPhase::Initial) => "analysis",
+ MirPhase::Analysis(AnalysisPhase::PostCleanup) => "analysis-post-cleanup",
+ MirPhase::Runtime(RuntimePhase::Initial) => "runtime",
+ MirPhase::Runtime(RuntimePhase::PostCleanup) => "runtime-post-cleanup",
+ MirPhase::Runtime(RuntimePhase::Optimized) => "runtime-optimized",
+ }
+ }
+
+ pub fn reveal(&self) -> Reveal {
+ match *self {
+ MirPhase::Built | MirPhase::Analysis(_) => Reveal::UserFacing,
+ MirPhase::Runtime(_) => Reveal::All,
+ }
+ }
+}
+
/// See [`MirPhase::Analysis`].
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(HashStable)]
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, Hash, HashStable)]
#[rustc_pass_by_value]
pub enum RetagKind {
- /// The initial retag when entering a function.
+ /// The initial retag of arguments when entering a function.
FnEntry,
/// Retag preparing for a two-phase borrow.
TwoPhase,
SwitchInt {
/// The discriminant value being tested.
discr: Operand<'tcx>,
-
- /// The type of value being tested.
- /// This is always the same as the type of `discr`.
- /// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing.
- switch_ty: Ty<'tcx>,
-
targets: SwitchTargets,
},
-use crate::mir;
-use crate::mir::interpret::Scalar;
-use crate::ty::{self, Ty, TyCtxt};
use smallvec::{smallvec, SmallVec};
use super::{BasicBlock, InlineAsmOperand, Operand, SourceInfo, TerminatorKind};
}
impl<'tcx> TerminatorKind<'tcx> {
- pub fn if_(
- tcx: TyCtxt<'tcx>,
- cond: Operand<'tcx>,
- t: BasicBlock,
- f: BasicBlock,
- ) -> TerminatorKind<'tcx> {
- TerminatorKind::SwitchInt {
- discr: cond,
- switch_ty: tcx.types.bool,
- targets: SwitchTargets::static_if(0, f, t),
- }
+ pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
+ TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
}
pub fn successors(&self) -> Successors<'_> {
}
}
- pub fn as_switch(&self) -> Option<(&Operand<'tcx>, Ty<'tcx>, &SwitchTargets)> {
+ pub fn as_switch(&self) -> Option<(&Operand<'tcx>, &SwitchTargets)> {
match self {
- TerminatorKind::SwitchInt { discr, switch_ty, targets } => {
- Some((discr, *switch_ty, targets))
- }
+ TerminatorKind::SwitchInt { discr, targets } => Some((discr, targets)),
_ => None,
}
}
match *self {
Return | Resume | Abort | Unreachable | GeneratorDrop => vec![],
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 size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
- targets
- .values
- .iter()
- .map(|&u| {
- mir::ConstantKind::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty)
- .to_string()
- .into()
- })
- .chain(iter::once("otherwise".into()))
- .collect()
- }),
+ SwitchInt { ref targets, .. } => targets
+ .values
+ .iter()
+ .map(|&u| Cow::Owned(u.to_string()))
+ .chain(iter::once("otherwise".into()))
+ .collect(),
Call { target: Some(_), cleanup: Some(_), .. } => {
vec!["return".into(), "unwind".into()]
}
TerminatorKind::SwitchInt {
discr,
- switch_ty,
targets: _
} => {
self.visit_operand(discr, location);
- self.visit_ty($(& $mutability)? *switch_ty, TyContext::Location(location));
}
TerminatorKind::Drop {
}
query resolver_for_lowering(_: ()) -> &'tcx Steal<ty::ResolverAstLowering> {
- eval_always
+ feedable
no_hash
desc { "getting the resolver for lowering" }
}
/// This span is meant for dep-tracking rather than diagnostics. It should not be used outside
/// of rustc_middle::hir::source_map.
query source_span(key: LocalDefId) -> Span {
+ // Accesses untracked data
+ eval_always
desc { "getting the source span" }
}
desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) }
cache_on_disk_if { def_id.is_local() }
separate_provide_extern
+ feedable
}
/// Gets the span for the identifier of the definition.
use crate::ty::{self, Ty, TyCtxt};
use rustc_hir::def_id::DefId;
use rustc_span::source_map::Span;
-use std::iter::FromIterator;
pub mod type_op {
use crate::ty::fold::TypeFoldable;
use rustc_apfloat::Float;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_target::abi::Size;
-use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::num::NonZeroU8;
-use std::convert::TryInto;
-
use super::Const;
use crate::mir;
use crate::mir::interpret::{AllocId, ConstValue, Scalar};
//! Type context book-keeping.
+#![allow(rustc::usage_of_ty_tykind)]
+
use crate::arena::Arena;
use crate::dep_graph::{DepGraph, DepKindStruct};
use crate::hir::place::Place as HirPlace;
self, AdtDef, AdtDefData, AdtKind, Binder, BindingMode, BoundVar, CanonicalPolyFnSig,
ClosureSizeProfileData, Const, ConstS, DefIdTree, FloatTy, FloatVar, FloatVid,
GenericParamDefKind, InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy,
- PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicateS, ProjectionTy,
- Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, TyVid, TypeAndMut,
- UintTy, Visibility,
+ PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, ProjectionTy, Region,
+ RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVar, TyVid, TypeAndMut, UintTy,
+ Visibility,
};
use crate::ty::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef, UserSubsts};
use rustc_ast as ast;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::intern::{Interned, WithStableHash};
+use rustc_data_structures::intern::Interned;
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::steal::Steal;
-use rustc_data_structures::sync::{self, Lock, Lrc, ReadGuard, RwLock, WorkerLocal};
+use rustc_data_structures::sync::{self, Lock, Lrc, ReadGuard, WorkerLocal};
use rustc_data_structures::unord::UnordSet;
use rustc_data_structures::vec_map::VecMap;
use rustc_errors::{
use rustc_query_system::ich::StableHashingContext;
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
use rustc_session::config::{CrateType, OutputFilenames};
-use rustc_session::cstore::CrateStoreDyn;
+use rustc_session::cstore::{CrateStoreDyn, Untracked};
use rustc_session::lint::Lint;
use rustc_session::Limit;
use rustc_session::Session;
use rustc_target::abi::{Layout, LayoutS, TargetDataLayout, VariantIdx};
use rustc_target::spec::abi;
use rustc_type_ir::sty::TyKind::*;
+use rustc_type_ir::WithCachedTypeInfo;
use rustc_type_ir::{DynKind, InternAs, InternIteratorElement, Interner, TypeFlags};
use std::any::Any;
use std::ops::{Bound, Deref};
use std::sync::Arc;
-use super::{ImplPolarity, ResolverOutputs, RvalueScopes};
+use super::{ImplPolarity, RvalueScopes};
pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync {
/// Creates a new `OnDiskCache` instance from the serialized data in `data`.
// Specifically use a speedy hash algorithm for these hash sets, since
// they're accessed quite often.
- type_: InternedSet<'tcx, WithStableHash<TyS<'tcx>>>,
+ type_: InternedSet<'tcx, WithCachedTypeInfo<TyKind<'tcx>>>,
const_lists: InternedSet<'tcx, List<ty::Const<'tcx>>>,
substs: InternedSet<'tcx, InternalSubsts<'tcx>>,
canonical_var_infos: InternedSet<'tcx, List<CanonicalVarInfo<'tcx>>>,
region: InternedSet<'tcx, RegionKind<'tcx>>,
poly_existential_predicates: InternedSet<'tcx, List<PolyExistentialPredicate<'tcx>>>,
- predicate: InternedSet<'tcx, WithStableHash<PredicateS<'tcx>>>,
+ predicate: InternedSet<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
predicates: InternedSet<'tcx, List<Predicate<'tcx>>>,
projs: InternedSet<'tcx, List<ProjectionKind>>,
place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>,
/// Interns a type.
#[allow(rustc::usage_of_ty_tykind)]
#[inline(never)]
- fn intern_ty(
- &self,
- kind: TyKind<'tcx>,
- sess: &Session,
- definitions: &rustc_hir::definitions::Definitions,
- cstore: &CrateStoreDyn,
- source_span: &IndexVec<LocalDefId, Span>,
- ) -> Ty<'tcx> {
+ fn intern_ty(&self, kind: TyKind<'tcx>, sess: &Session, untracked: &Untracked) -> Ty<'tcx> {
Ty(Interned::new_unchecked(
self.type_
.intern(kind, |kind| {
let flags = super::flags::FlagComputation::for_kind(&kind);
- let stable_hash =
- self.stable_hash(&flags, sess, definitions, cstore, source_span, &kind);
+ let stable_hash = self.stable_hash(&flags, sess, untracked, &kind);
- let ty_struct = TyS {
- kind,
+ InternedInSet(self.arena.alloc(WithCachedTypeInfo {
+ internee: kind,
+ stable_hash,
flags: flags.flags,
outer_exclusive_binder: flags.outer_exclusive_binder,
- };
-
- InternedInSet(
- self.arena.alloc(WithStableHash { internee: ty_struct, stable_hash }),
- )
+ }))
})
.0,
))
&self,
flags: &ty::flags::FlagComputation,
sess: &'a Session,
- definitions: &'a rustc_hir::definitions::Definitions,
- cstore: &'a CrateStoreDyn,
- source_span: &'a IndexVec<LocalDefId, Span>,
+ untracked: &'a Untracked,
val: &T,
) -> Fingerprint {
// It's impossible to hash inference variables (and will ICE), so we don't need to try to cache them.
Fingerprint::ZERO
} else {
let mut hasher = StableHasher::new();
- let mut hcx = StableHashingContext::new(sess, definitions, cstore, source_span);
+ let mut hcx = StableHashingContext::new(sess, untracked);
val.hash_stable(&mut hcx, &mut hasher);
hasher.finish()
}
&self,
kind: Binder<'tcx, PredicateKind<'tcx>>,
sess: &Session,
- definitions: &rustc_hir::definitions::Definitions,
- cstore: &CrateStoreDyn,
- source_span: &IndexVec<LocalDefId, Span>,
+ untracked: &Untracked,
) -> Predicate<'tcx> {
Predicate(Interned::new_unchecked(
self.predicate
.intern(kind, |kind| {
let flags = super::flags::FlagComputation::for_predicate(kind);
- let stable_hash =
- self.stable_hash(&flags, sess, definitions, cstore, source_span, &kind);
+ let stable_hash = self.stable_hash(&flags, sess, untracked, &kind);
- let predicate_struct = PredicateS {
- kind,
+ InternedInSet(self.arena.alloc(WithCachedTypeInfo {
+ internee: kind,
+ stable_hash,
flags: flags.flags,
outer_exclusive_binder: flags.outer_exclusive_binder,
- };
-
- InternedInSet(
- self.arena
- .alloc(WithStableHash { internee: predicate_struct, stable_hash }),
- )
+ }))
})
.0,
))
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.field_indices }
}
+ pub fn field_index(&self, id: hir::HirId) -> usize {
+ self.field_indices().get(id).cloned().expect("no index for a field")
+ }
+
+ pub fn opt_field_index(&self, id: hir::HirId) -> Option<usize> {
+ self.field_indices().get(id).cloned()
+ }
+
pub fn user_provided_types(&self) -> LocalTableInContext<'_, CanonicalUserType<'tcx>> {
LocalTableInContext { hir_owner: self.hir_owner, data: &self.user_provided_types }
}
fn new(
interners: &CtxtInterners<'tcx>,
sess: &Session,
- definitions: &rustc_hir::definitions::Definitions,
- cstore: &CrateStoreDyn,
- source_span: &IndexVec<LocalDefId, Span>,
+ untracked: &Untracked,
) -> CommonTypes<'tcx> {
- let mk = |ty| interners.intern_ty(ty, sess, definitions, cstore, source_span);
+ let mk = |ty| interners.intern_ty(ty, sess, untracked);
CommonTypes {
unit: mk(Tuple(List::empty())),
/// This struct should only be created by `create_def`.
#[derive(Copy, Clone)]
-pub struct TyCtxtFeed<'tcx> {
+pub struct TyCtxtFeed<'tcx, KEY: Copy> {
pub tcx: TyCtxt<'tcx>,
// Do not allow direct access, as downstream code must not mutate this field.
- def_id: LocalDefId,
+ key: KEY,
+}
+
+impl<'tcx> TyCtxt<'tcx> {
+ pub fn feed_unit_query(self) -> TyCtxtFeed<'tcx, ()> {
+ TyCtxtFeed { tcx: self, key: () }
+ }
}
-impl<'tcx> TyCtxtFeed<'tcx> {
+impl<'tcx, KEY: Copy> TyCtxtFeed<'tcx, KEY> {
+ #[inline(always)]
+ pub fn key(&self) -> KEY {
+ self.key
+ }
+}
+
+impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> {
#[inline(always)]
pub fn def_id(&self) -> LocalDefId {
- self.def_id
+ self.key
}
}
/// Common consts, pre-interned for your convenience.
pub consts: CommonConsts<'tcx>,
- definitions: RwLock<Definitions>,
-
+ untracked: Untracked,
/// Output of the resolver.
pub(crate) untracked_resolutions: ty::ResolverGlobalCtxt,
- untracked_resolver_for_lowering: Steal<ty::ResolverAstLowering>,
/// The entire crate as AST. This field serves as the input for the hir_crate query,
/// which lowers it from AST to HIR. It must not be read or used by anything else.
pub untracked_crate: Steal<Lrc<ast::Crate>>,
lint_store: Lrc<dyn Any + sync::Send + sync::Sync>,
arena: &'tcx WorkerLocal<Arena<'tcx>>,
hir_arena: &'tcx WorkerLocal<hir::Arena<'tcx>>,
- resolver_outputs: ResolverOutputs,
+ untracked_resolutions: ty::ResolverGlobalCtxt,
+ untracked: Untracked,
krate: Lrc<ast::Crate>,
dep_graph: DepGraph,
on_disk_cache: Option<&'tcx dyn OnDiskCache<'tcx>>,
queries: &'tcx dyn query::QueryEngine<'tcx>,
query_kinds: &'tcx [DepKindStruct<'tcx>],
- crate_name: &str,
+ crate_name: Symbol,
output_filenames: OutputFilenames,
) -> GlobalCtxt<'tcx> {
- let ResolverOutputs {
- definitions,
- global_ctxt: untracked_resolutions,
- ast_lowering: untracked_resolver_for_lowering,
- } = resolver_outputs;
let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| {
s.emit_fatal(err);
});
let interners = CtxtInterners::new(arena);
- let common_types = CommonTypes::new(
- &interners,
- s,
- &definitions,
- &*untracked_resolutions.cstore,
- // This is only used to create a stable hashing context.
- &untracked_resolutions.source_span,
- );
+ let common_types = CommonTypes::new(&interners, s, &untracked);
let common_lifetimes = CommonLifetimes::new(&interners);
let common_consts = CommonConsts::new(&interners, &common_types);
hir_arena,
interners,
dep_graph,
- definitions: RwLock::new(definitions),
prof: s.prof.clone(),
types: common_types,
lifetimes: common_lifetimes,
consts: common_consts,
+ untracked,
untracked_resolutions,
- untracked_resolver_for_lowering: Steal::new(untracked_resolver_for_lowering),
untracked_crate: Steal::new(krate),
on_disk_cache,
queries,
pred_rcache: Default::default(),
selection_cache: Default::default(),
evaluation_cache: Default::default(),
- crate_name: Symbol::intern(crate_name),
+ crate_name,
data_layout,
alloc_map: Lock::new(interpret::AllocMap::new()),
output_filenames: Arc::new(output_filenames),
if let Some(id) = id.as_local() {
self.definitions_untracked().def_key(id)
} else {
- self.untracked_resolutions.cstore.def_key(id)
+ self.untracked.cstore.def_key(id)
}
}
if let Some(id) = id.as_local() {
self.definitions_untracked().def_path(id)
} else {
- self.untracked_resolutions.cstore.def_path(id)
+ self.untracked.cstore.def_path(id)
}
}
if let Some(def_id) = def_id.as_local() {
self.definitions_untracked().def_path_hash(def_id)
} else {
- self.untracked_resolutions.cstore.def_path_hash(def_id)
+ self.untracked.cstore.def_path_hash(def_id)
}
}
if crate_num == LOCAL_CRATE {
self.sess.local_stable_crate_id()
} else {
- self.untracked_resolutions.cstore.stable_crate_id(crate_num)
+ self.untracked.cstore.stable_crate_id(crate_num)
}
}
if stable_crate_id == self.sess.local_stable_crate_id() {
LOCAL_CRATE
} else {
- self.untracked_resolutions.cstore.stable_crate_id_to_crate_num(stable_crate_id)
+ self.untracked.cstore.stable_crate_id_to_crate_num(stable_crate_id)
}
}
// If this is a DefPathHash from the local crate, we can look up the
// DefId in the tcx's `Definitions`.
if stable_crate_id == self.sess.local_stable_crate_id() {
- self.definitions.read().local_def_path_hash_to_def_id(hash, err).to_def_id()
+ self.untracked.definitions.read().local_def_path_hash_to_def_id(hash, err).to_def_id()
} else {
// If this is a DefPathHash from an upstream crate, let the CrateStore map
// it to a DefId.
- let cstore = &*self.untracked_resolutions.cstore;
+ let cstore = &*self.untracked.cstore;
let cnum = cstore.stable_crate_id_to_crate_num(stable_crate_id);
cstore.def_path_hash_to_def_id(cnum, hash)
}
let (crate_name, stable_crate_id) = if def_id.is_local() {
(self.crate_name, self.sess.local_stable_crate_id())
} else {
- let cstore = &*self.untracked_resolutions.cstore;
+ let cstore = &*self.untracked.cstore;
(cstore.crate_name(def_id.krate), cstore.stable_crate_id(def_id.krate))
};
self.def_path(def_id).to_string_no_crate_verbose()
)
}
+}
+impl<'tcx> TyCtxtAt<'tcx> {
/// Create a new definition within the incr. comp. engine.
pub fn create_def(
self,
parent: LocalDefId,
data: hir::definitions::DefPathData,
- ) -> TyCtxtFeed<'tcx> {
+ ) -> TyCtxtFeed<'tcx, LocalDefId> {
// This function modifies `self.definitions` using a side-effect.
// We need to ensure that these side effects are re-run by the incr. comp. engine.
// Depending on the forever-red node will tell the graph that the calling query
// This is fine because:
// - those queries are `eval_always` so we won't miss their result changing;
// - this write will have happened before these queries are called.
- let def_id = self.definitions.write().create_def(parent, data);
+ let key = self.untracked.definitions.write().create_def(parent, data);
- TyCtxtFeed { tcx: self, def_id }
+ let feed = TyCtxtFeed { tcx: self.tcx, key };
+ feed.def_span(self.span);
+ feed
}
+}
+impl<'tcx> TyCtxt<'tcx> {
pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + 'tcx {
// Create a dependency to the red node to be sure we re-execute this when the amount of
// definitions change.
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
- let definitions = &self.definitions;
+ let definitions = &self.untracked.definitions;
std::iter::from_generator(|| {
let mut i = 0;
// Leak a read lock once we start iterating on definitions, to prevent adding new ones
// while iterating. If some query needs to add definitions, it should be `ensure`d above.
- let definitions = self.definitions.leak();
+ let definitions = self.untracked.definitions.leak();
definitions.def_path_table()
}
self.ensure().hir_crate(());
// Leak a read lock once we start iterating on definitions, to prevent adding new ones
// while iterating. If some query needs to add definitions, it should be `ensure`d above.
- let definitions = self.definitions.leak();
+ let definitions = self.untracked.definitions.leak();
definitions.def_path_hash_to_def_index_map()
}
/// Note that this is *untracked* and should only be used within the query
/// system if the result is otherwise tracked through queries
pub fn cstore_untracked(self) -> &'tcx CrateStoreDyn {
- &*self.untracked_resolutions.cstore
+ &*self.untracked.cstore
}
/// Note that this is *untracked* and should only be used within the query
/// system if the result is otherwise tracked through queries
#[inline]
pub fn definitions_untracked(self) -> ReadGuard<'tcx, Definitions> {
- self.definitions.read()
+ self.untracked.definitions.read()
}
/// Note that this is *untracked* and should only be used within the query
/// system if the result is otherwise tracked through queries
#[inline]
pub fn source_span_untracked(self, def_id: LocalDefId) -> Span {
- self.untracked_resolutions.source_span.get(def_id).copied().unwrap_or(DUMMY_SP)
+ self.untracked.source_span.get(def_id).copied().unwrap_or(DUMMY_SP)
}
#[inline(always)]
self,
f: impl FnOnce(StableHashingContext<'_>) -> R,
) -> R {
- let definitions = self.definitions_untracked();
- let hcx = StableHashingContext::new(
- self.sess,
- &*definitions,
- &*self.untracked_resolutions.cstore,
- &self.untracked_resolutions.source_span,
- );
- f(hcx)
+ f(StableHashingContext::new(self.sess, &self.untracked))
}
pub fn serialize_query_result_cache(self, encoder: FileEncoder) -> FileEncodeResult {
let shards = tcx.interners.type_.lock_shards();
let types = shards.iter().flat_map(|shard| shard.keys());
for &InternedInSet(t) in types {
- let variant = match t.kind {
+ let variant = match t.internee {
ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) |
ty::Float(..) | ty::Str | ty::Never => continue,
ty::Error(_) => /* unimportant */ continue,
}
#[allow(rustc::usage_of_ty_tykind)]
-impl<'tcx> Borrow<TyKind<'tcx>> for InternedInSet<'tcx, WithStableHash<TyS<'tcx>>> {
- fn borrow<'a>(&'a self) -> &'a TyKind<'tcx> {
- &self.0.kind
- }
-}
-
-impl<'tcx> PartialEq for InternedInSet<'tcx, WithStableHash<TyS<'tcx>>> {
- fn eq(&self, other: &InternedInSet<'tcx, WithStableHash<TyS<'tcx>>>) -> bool {
- // The `Borrow` trait requires that `x.borrow() == y.borrow()` equals
- // `x == y`.
- self.0.kind == other.0.kind
- }
-}
-
-impl<'tcx> Eq for InternedInSet<'tcx, WithStableHash<TyS<'tcx>>> {}
-
-impl<'tcx> Hash for InternedInSet<'tcx, WithStableHash<TyS<'tcx>>> {
- fn hash<H: Hasher>(&self, s: &mut H) {
- // The `Borrow` trait requires that `x.borrow().hash(s) == x.hash(s)`.
- self.0.kind.hash(s)
- }
-}
-
-impl<'tcx> Borrow<Binder<'tcx, PredicateKind<'tcx>>>
- for InternedInSet<'tcx, WithStableHash<PredicateS<'tcx>>>
-{
- fn borrow<'a>(&'a self) -> &'a Binder<'tcx, PredicateKind<'tcx>> {
- &self.0.kind
+impl<'tcx, T> Borrow<T> for InternedInSet<'tcx, WithCachedTypeInfo<T>> {
+ fn borrow<'a>(&'a self) -> &'a T {
+ &self.0.internee
}
}
-impl<'tcx> PartialEq for InternedInSet<'tcx, WithStableHash<PredicateS<'tcx>>> {
- fn eq(&self, other: &InternedInSet<'tcx, WithStableHash<PredicateS<'tcx>>>) -> bool {
+impl<'tcx, T: PartialEq> PartialEq for InternedInSet<'tcx, WithCachedTypeInfo<T>> {
+ fn eq(&self, other: &InternedInSet<'tcx, WithCachedTypeInfo<T>>) -> bool {
// The `Borrow` trait requires that `x.borrow() == y.borrow()` equals
// `x == y`.
- self.0.kind == other.0.kind
+ self.0.internee == other.0.internee
}
}
-impl<'tcx> Eq for InternedInSet<'tcx, WithStableHash<PredicateS<'tcx>>> {}
+impl<'tcx, T: Eq> Eq for InternedInSet<'tcx, WithCachedTypeInfo<T>> {}
-impl<'tcx> Hash for InternedInSet<'tcx, WithStableHash<PredicateS<'tcx>>> {
+impl<'tcx, T: Hash> Hash for InternedInSet<'tcx, WithCachedTypeInfo<T>> {
fn hash<H: Hasher>(&self, s: &mut H) {
// The `Borrow` trait requires that `x.borrow().hash(s) == x.hash(s)`.
- self.0.kind.hash(s)
+ self.0.internee.hash(s)
}
}
self.interners.intern_ty(
st,
self.sess,
- &self.definitions.read(),
- &*self.untracked_resolutions.cstore,
// This is only used to create a stable hashing context.
- &self.untracked_resolutions.source_span,
+ &self.untracked,
)
}
self.interners.intern_predicate(
binder,
self.sess,
- &self.definitions.read(),
- &*self.untracked_resolutions.cstore,
// This is only used to create a stable hashing context.
- &self.untracked_resolutions.source_span,
+ &self.untracked,
)
}
pub fn provide(providers: &mut ty::query::Providers) {
providers.resolutions = |tcx, ()| &tcx.untracked_resolutions;
- providers.resolver_for_lowering = |tcx, ()| &tcx.untracked_resolver_for_lowering;
providers.module_reexports =
|tcx, id| tcx.resolutions(()).reexport_map.get(&id).map(|v| &v[..]);
providers.crate_name = |tcx, id| {
// We want to check if the panic handler was defined in this crate
tcx.lang_items().panic_impl().map_or(false, |did| did.is_local())
};
+ providers.source_span =
+ |tcx, def_id| tcx.untracked.source_span.get(def_id).copied().unwrap_or(DUMMY_SP);
}
use crate::traits::{ObligationCause, ObligationCauseCode};
use crate::ty::diagnostics::suggest_constraining_type_param;
-use crate::ty::print::{FmtPrinter, Printer};
+use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, Printer};
use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
use hir::def::DefKind;
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
),
RegionsPlaceholderMismatch => write!(f, "one type is more general than the other"),
ArgumentSorts(values, _) | Sorts(values) => ty::tls::with(|tcx| {
- report_maybe_different(
- f,
- &values.expected.sort_string(tcx),
- &values.found.sort_string(tcx),
- )
+ let (mut expected, mut found) = with_forced_trimmed_paths!((
+ values.expected.sort_string(tcx),
+ values.found.sort_string(tcx),
+ ));
+ if expected == found {
+ expected = values.expected.sort_string(tcx);
+ found = values.found.sort_string(tcx);
+ }
+ report_maybe_different(f, &expected, &found)
}),
Traits(values) => ty::tls::with(|tcx| {
+ let (mut expected, mut found) = with_forced_trimmed_paths!((
+ tcx.def_path_str(values.expected),
+ tcx.def_path_str(values.found),
+ ));
+ if expected == found {
+ expected = tcx.def_path_str(values.expected);
+ found = tcx.def_path_str(values.found);
+ }
report_maybe_different(
f,
- &format!("trait `{}`", tcx.def_path_str(values.expected)),
- &format!("trait `{}`", tcx.def_path_str(values.found)),
+ &format!("trait `{expected}`"),
+ &format!("trait `{found}`"),
)
}),
IntMismatch(ref values) => {
}
pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option<PathBuf>) {
- let length_limit = 50;
- let type_limit = 4;
+ let width = self.sess.diagnostic_width();
+ let length_limit = width.saturating_sub(30);
+ let mut type_limit = 50;
let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS)
.pretty_print_type(ty)
.expect("could not write to `String`")
.into_buffer();
- if regular.len() <= length_limit {
+ if regular.len() <= width {
return (regular, None);
}
- let short = FmtPrinter::new_with_limit(
- self,
- hir::def::Namespace::TypeNS,
- rustc_session::Limit(type_limit),
- )
- .pretty_print_type(ty)
- .expect("could not write to `String`")
- .into_buffer();
+ let mut short;
+ loop {
+ // Look for the longest properly trimmed path that still fits in lenght_limit.
+ short = with_forced_trimmed_paths!(
+ FmtPrinter::new_with_limit(
+ self,
+ hir::def::Namespace::TypeNS,
+ rustc_session::Limit(type_limit),
+ )
+ .pretty_print_type(ty)
+ .expect("could not write to `String`")
+ .into_buffer()
+ );
+ if short.len() <= length_limit || type_limit == 0 {
+ break;
+ }
+ type_limit -= 1;
+ }
if regular == short {
return (regular, None);
}
_ => None,
}
}
+
+ pub fn to_error<'tcx>(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ preceding_substs: &[ty::GenericArg<'tcx>],
+ ) -> ty::GenericArg<'tcx> {
+ match &self.kind {
+ ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(),
+ ty::GenericParamDefKind::Type { .. } => tcx.ty_error().into(),
+ ty::GenericParamDefKind::Const { .. } => {
+ tcx.const_error(tcx.bound_type_of(self.def_id).subst(tcx, preceding_substs)).into()
+ }
+ }
+ }
}
#[derive(Default)]
}
}
+ pub fn params_to(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx [GenericParamDef] {
+ if let Some(index) = param_index.checked_sub(self.parent_count) {
+ &self.params[..index]
+ } else {
+ tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?"))
+ .params_to(param_index, tcx)
+ }
+ }
+
/// Returns the `GenericParamDef` associated with this `EarlyBoundRegion`.
pub fn region_param(
&'tcx self,
//!
//! ["The `ty` module: representing types"]: https://rustc-dev-guide.rust-lang.org/ty.html
+#![allow(rustc::usage_of_ty_tykind)]
+
pub use self::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable};
pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
pub use self::AssocItemContainer::*;
use rustc_attr as attr;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
-use rustc_data_structures::intern::{Interned, WithStableHash};
+use rustc_data_structures::intern::Interned;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::tagged_ptr::CopyTaggedPtr;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, LifetimeRes, Res};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap};
-use rustc_hir::definitions::Definitions;
use rustc_hir::Node;
use rustc_index::vec::IndexVec;
use rustc_macros::HashStable;
use rustc_query_system::ich::StableHashingContext;
use rustc_serialize::{Decodable, Encodable};
-use rustc_session::cstore::CrateStoreDyn;
+use rustc_session::cstore::Untracked;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{ExpnId, Span};
use rustc_target::abi::{Align, Integer, IntegerType, VariantIdx};
pub use rustc_target::abi::{ReprFlags, ReprOptions};
+use rustc_type_ir::WithCachedTypeInfo;
pub use subst::*;
pub use vtable::*;
pub use self::context::{
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GeneratorDiagnosticData,
- GeneratorInteriorTypeCause, GlobalCtxt, Lift, OnDiskCache, TyCtxt, TypeckResults, UserType,
- UserTypeAnnotationIndex,
+ GeneratorInteriorTypeCause, GlobalCtxt, Lift, OnDiskCache, TyCtxt, TyCtxtFeed, TypeckResults,
+ UserType, UserTypeAnnotationIndex,
};
pub use self::instance::{Instance, InstanceDef, ShortInstance};
pub use self::list::List;
pub type RegisteredTools = FxHashSet<Ident>;
pub struct ResolverOutputs {
- pub definitions: Definitions,
pub global_ctxt: ResolverGlobalCtxt,
pub ast_lowering: ResolverAstLowering,
+ pub untracked: Untracked,
}
#[derive(Debug)]
pub struct ResolverGlobalCtxt {
- pub cstore: Box<CrateStoreDyn>,
pub visibilities: FxHashMap<LocalDefId, Visibility>,
/// This field is used to decide whether we should make `PRIVATE_IN_PUBLIC` a hard error.
pub has_pub_restricted: bool,
/// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
pub expn_that_defined: FxHashMap<LocalDefId, ExpnId>,
- /// Reference span for definitions.
- pub source_span: IndexVec<LocalDefId, Span>,
pub effective_visibilities: EffectiveVisibilities,
pub extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
pub maybe_unused_trait_imports: FxIndexSet<LocalDefId>,
pub pos: usize,
}
-/// Represents a type.
-///
-/// IMPORTANT:
-/// - This is a very "dumb" struct (with no derives and no `impls`).
-/// - Values of this type are always interned and thus unique, and are stored
-/// as an `Interned<TyS>`.
-/// - `Ty` (which contains a reference to a `Interned<TyS>`) or `Interned<TyS>`
-/// should be used everywhere instead of `TyS`. In particular, `Ty` has most
-/// of the relevant methods.
-#[derive(PartialEq, Eq, PartialOrd, Ord)]
-#[allow(rustc::usage_of_ty_tykind)]
-pub(crate) struct TyS<'tcx> {
- /// This field shouldn't be used directly and may be removed in the future.
- /// Use `Ty::kind()` instead.
- kind: TyKind<'tcx>,
-
- /// This field provides fast access to information that is also contained
- /// in `kind`.
- ///
- /// This field shouldn't be used directly and may be removed in the future.
- /// Use `Ty::flags()` instead.
- flags: TypeFlags,
-
- /// This field provides fast access to information that is also contained
- /// in `kind`.
- ///
- /// This is a kind of confusing thing: it stores the smallest
- /// binder such that
- ///
- /// (a) the binder itself captures nothing but
- /// (b) all the late-bound things within the type are captured
- /// by some sub-binder.
- ///
- /// So, for a type without any late-bound things, like `u32`, this
- /// will be *innermost*, because that is the innermost binder that
- /// captures nothing. But for a type `&'D u32`, where `'D` is a
- /// late-bound region with De Bruijn index `D`, this would be `D + 1`
- /// -- the binder itself does not capture `D`, but `D` is captured
- /// by an inner binder.
- ///
- /// We call this concept an "exclusive" binder `D` because all
- /// De Bruijn indices within the type are contained within `0..D`
- /// (exclusive).
- outer_exclusive_binder: ty::DebruijnIndex,
-}
-
-/// Use this rather than `TyS`, whenever possible.
+/// Use this rather than `TyKind`, whenever possible.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
#[rustc_diagnostic_item = "Ty"]
#[rustc_pass_by_value]
-pub struct Ty<'tcx>(Interned<'tcx, WithStableHash<TyS<'tcx>>>);
+pub struct Ty<'tcx>(Interned<'tcx, WithCachedTypeInfo<TyKind<'tcx>>>);
impl<'tcx> TyCtxt<'tcx> {
/// A "bool" type used in rustc_mir_transform unit tests when we
/// have not spun up a TyCtxt.
- pub const BOOL_TY_FOR_UNIT_TESTING: Ty<'tcx> = Ty(Interned::new_unchecked(&WithStableHash {
- internee: TyS {
- kind: ty::Bool,
+ pub const BOOL_TY_FOR_UNIT_TESTING: Ty<'tcx> =
+ Ty(Interned::new_unchecked(&WithCachedTypeInfo {
+ internee: ty::Bool,
+ stable_hash: Fingerprint::ZERO,
flags: TypeFlags::empty(),
outer_exclusive_binder: DebruijnIndex::from_usize(0),
- },
- stable_hash: Fingerprint::ZERO,
- }));
-}
-
-impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TyS<'tcx> {
- #[inline]
- fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
- let TyS {
- kind,
-
- // The other fields just provide fast access to information that is
- // also contained in `kind`, so no need to hash them.
- flags: _,
-
- outer_exclusive_binder: _,
- } = self;
-
- kind.hash_stable(hcx, hasher)
- }
+ }));
}
impl ty::EarlyBoundRegion {
}
}
-/// Represents a predicate.
-///
-/// See comments on `TyS`, which apply here too (albeit for
-/// `PredicateS`/`Predicate` rather than `TyS`/`Ty`).
-#[derive(Debug)]
-pub(crate) struct PredicateS<'tcx> {
- kind: Binder<'tcx, PredicateKind<'tcx>>,
- flags: TypeFlags,
- /// See the comment for the corresponding field of [TyS].
- outer_exclusive_binder: ty::DebruijnIndex,
-}
-
-/// Use this rather than `PredicateS`, whenever possible.
+/// Use this rather than `PredicateKind`, whenever possible.
#[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable)]
#[rustc_pass_by_value]
-pub struct Predicate<'tcx>(Interned<'tcx, WithStableHash<PredicateS<'tcx>>>);
+pub struct Predicate<'tcx>(
+ Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
+);
impl<'tcx> Predicate<'tcx> {
/// Gets the inner `Binder<'tcx, PredicateKind<'tcx>>`.
#[inline]
pub fn kind(self) -> Binder<'tcx, PredicateKind<'tcx>> {
- self.0.kind
+ self.0.internee
}
#[inline(always)]
}
}
-impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for PredicateS<'tcx> {
- fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
- let PredicateS {
- ref kind,
-
- // The other fields just provide fast access to information that is
- // also contained in `kind`, so no need to hash them.
- flags: _,
- outer_exclusive_binder: _,
- } = self;
-
- kind.hash_stable(hcx, hasher);
- }
-}
-
impl rustc_errors::IntoDiagnosticArg for Predicate<'_> {
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
rustc_errors::DiagnosticArgValue::Str(std::borrow::Cow::Owned(self.to_string()))
unsafe {
match ptr & TAG_MASK {
TYPE_TAG => TermKind::Ty(Ty(Interned::new_unchecked(
- &*((ptr & !TAG_MASK) as *const WithStableHash<ty::TyS<'tcx>>),
+ &*((ptr & !TAG_MASK) as *const WithCachedTypeInfo<ty::TyKind<'tcx>>),
))),
CONST_TAG => TermKind::Const(ty::Const(Interned::new_unchecked(
&*((ptr & !TAG_MASK) as *const ty::ConstS<'tcx>),
TermKind::Ty(ty) => {
// Ensure we can use the tag bits.
assert_eq!(mem::align_of_val(&*ty.0.0) & TAG_MASK, 0);
- (TYPE_TAG, ty.0.0 as *const WithStableHash<ty::TyS<'tcx>> as usize)
+ (TYPE_TAG, ty.0.0 as *const WithCachedTypeInfo<ty::TyKind<'tcx>> as usize)
}
TermKind::Const(ct) => {
// Ensure we can use the tag bits.
}
}
-pub trait ToPredicate<'tcx, Predicate> {
- fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate;
+pub trait ToPredicate<'tcx, P = Predicate<'tcx>> {
+ fn to_predicate(self, tcx: TyCtxt<'tcx>) -> P;
}
impl<'tcx, T> ToPredicate<'tcx, T> for T {
}
}
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Binder<'tcx, PredicateKind<'tcx>> {
+impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, PredicateKind<'tcx>> {
#[inline(always)]
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
tcx.mk_predicate(self)
}
}
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Clause<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for Clause<'tcx> {
#[inline(always)]
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::Clause(self)))
}
}
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Binder<'tcx, TraitRef<'tcx>> {
+impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, TraitRef<'tcx>> {
#[inline(always)]
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
let pred: PolyTraitPredicate<'tcx> = self.to_predicate(tcx);
}
}
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyTraitPredicate<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for PolyTraitPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
self.map_bound(|p| PredicateKind::Clause(Clause::Trait(p))).to_predicate(tcx)
}
}
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyRegionOutlivesPredicate<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
self.map_bound(|p| PredicateKind::Clause(Clause::RegionOutlives(p))).to_predicate(tcx)
}
}
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyTypeOutlivesPredicate<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
self.map_bound(|p| PredicateKind::Clause(Clause::TypeOutlives(p))).to_predicate(tcx)
}
}
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyProjectionPredicate<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
self.map_bound(|p| PredicateKind::Clause(Clause::Projection(p))).to_predicate(tcx)
}
}
}
- pub fn field_index(self, hir_id: hir::HirId, typeck_results: &TypeckResults<'_>) -> usize {
- typeck_results.field_indices().get(hir_id).cloned().expect("no index for a field")
- }
-
pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option<usize> {
variant
.fields
use super::*;
use rustc_data_structures::static_assert_size;
// tidy-alphabetical-start
- static_assert_size!(PredicateS<'_>, 48);
- static_assert_size!(TyS<'_>, 40);
- static_assert_size!(WithStableHash<TyS<'_>>, 56);
+ static_assert_size!(PredicateKind<'_>, 32);
+ static_assert_size!(WithCachedTypeInfo<TyKind<'_>>, 56);
// tidy-alphabetical-end
}
use rustc_hir as hir;
use rustc_hir::def::{self, CtorKind, DefKind, Namespace};
use rustc_hir::def_id::{DefId, DefIdSet, CRATE_DEF_ID, LOCAL_CRATE};
-use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData};
+use rustc_hir::definitions::{DefKey, DefPathData, DefPathDataName, DisambiguatedDefPathData};
use rustc_hir::LangItem;
use rustc_session::config::TrimmedDefPaths;
use rustc_session::cstore::{ExternCrate, ExternCrateSource};
use std::cell::Cell;
use std::char;
use std::collections::BTreeMap;
-use std::convert::TryFrom;
use std::fmt::{self, Write as _};
use std::iter;
use std::ops::{ControlFlow, Deref, DerefMut};
static FORCE_IMPL_FILENAME_LINE: Cell<bool> = const { Cell::new(false) };
static SHOULD_PREFIX_WITH_CRATE: Cell<bool> = const { Cell::new(false) };
static NO_TRIMMED_PATH: Cell<bool> = const { Cell::new(false) };
+ static FORCE_TRIMMED_PATH: Cell<bool> = const { Cell::new(false) };
static NO_QUERIES: Cell<bool> = const { Cell::new(false) };
static NO_VISIBLE_PATH: Cell<bool> = const { Cell::new(false) };
}
/// of various rustc types, for example `std::vec::Vec` would be trimmed to `Vec`,
/// if no other `Vec` is found.
fn with_no_trimmed_paths(NoTrimmedGuard, NO_TRIMMED_PATH);
+ fn with_forced_trimmed_paths(ForceTrimmedGuard, FORCE_TRIMMED_PATH);
/// Prevent selection of visible paths. `Display` impl of DefId will prefer
/// visible (public) reexports of types as paths.
fn with_no_visible_paths(NoVisibleGuard, NO_VISIBLE_PATH);
self.try_print_visible_def_path_recur(def_id, &mut callers)
}
+ // Given a `DefId`, produce a short name. For types and traits, it prints *only* its name,
+ // For associated items on traits it prints out the trait's name and the associated item's name.
+ // For enum variants, if they have an unique name, then we only print the name, otherwise we
+ // print the enum name and the variant name. Otherwise, we do not print anything and let the
+ // caller use the `print_def_path` fallback.
+ fn force_print_trimmed_def_path(
+ mut self,
+ def_id: DefId,
+ ) -> Result<(Self::Path, bool), Self::Error> {
+ let key = self.tcx().def_key(def_id);
+ let visible_parent_map = self.tcx().visible_parent_map(());
+ let kind = self.tcx().def_kind(def_id);
+
+ let get_local_name = |this: &Self, name, def_id, key: DefKey| {
+ if let Some(visible_parent) = visible_parent_map.get(&def_id)
+ && let actual_parent = this.tcx().opt_parent(def_id)
+ && let DefPathData::TypeNs(_) = key.disambiguated_data.data
+ && Some(*visible_parent) != actual_parent
+ {
+ this
+ .tcx()
+ .module_children(visible_parent)
+ .iter()
+ .filter(|child| child.res.opt_def_id() == Some(def_id))
+ .find(|child| child.vis.is_public() && child.ident.name != kw::Underscore)
+ .map(|child| child.ident.name)
+ .unwrap_or(name)
+ } else {
+ name
+ }
+ };
+ if let DefKind::Variant = kind
+ && let Some(symbol) = self.tcx().trimmed_def_paths(()).get(&def_id)
+ {
+ // If `Assoc` is unique, we don't want to talk about `Trait::Assoc`.
+ self.write_str(get_local_name(&self, *symbol, def_id, key).as_str())?;
+ return Ok((self, true));
+ }
+ if let Some(symbol) = key.get_opt_name() {
+ if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = kind
+ && let Some(parent) = self.tcx().opt_parent(def_id)
+ && let parent_key = self.tcx().def_key(parent)
+ && let Some(symbol) = parent_key.get_opt_name()
+ {
+ // Trait
+ self.write_str(get_local_name(&self, symbol, parent, parent_key).as_str())?;
+ self.write_str("::")?;
+ } else if let DefKind::Variant = kind
+ && let Some(parent) = self.tcx().opt_parent(def_id)
+ && let parent_key = self.tcx().def_key(parent)
+ && let Some(symbol) = parent_key.get_opt_name()
+ {
+ // Enum
+
+ // For associated items and variants, we want the "full" path, namely, include
+ // the parent type in the path. For example, `Iterator::Item`.
+ self.write_str(get_local_name(&self, symbol, parent, parent_key).as_str())?;
+ self.write_str("::")?;
+ } else if let DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::Trait
+ | DefKind::TyAlias | DefKind::Fn | DefKind::Const | DefKind::Static(_) = kind
+ {
+ } else {
+ // If not covered above, like for example items out of `impl` blocks, fallback.
+ return Ok((self, false));
+ }
+ self.write_str(get_local_name(&self, symbol, def_id, key).as_str())?;
+ return Ok((self, true));
+ }
+ Ok((self, false))
+ }
+
/// Try to see if this path can be trimmed to a unique symbol name.
fn try_print_trimmed_def_path(
mut self,
def_id: DefId,
) -> Result<(Self::Path, bool), Self::Error> {
+ if FORCE_TRIMMED_PATH.with(|flag| flag.get()) {
+ let (s, trimmed) = self.force_print_trimmed_def_path(def_id)?;
+ if trimmed {
+ return Ok((s, true));
+ }
+ self = s;
+ }
if !self.tcx().sess.opts.unstable_opts.trim_diagnostic_paths
|| matches!(self.tcx().sess.opts.trimmed_def_paths, TrimmedDefPaths::Never)
|| NO_TRIMMED_PATH.with(|flag| flag.get())
};
}
+macro_rules! hash_result {
+ ([]) => {{
+ Some(dep_graph::hash_result)
+ }};
+ ([(no_hash) $($rest:tt)*]) => {{
+ None
+ }};
+ ([$other:tt $($modifiers:tt)*]) => {
+ hash_result!([$($modifiers)*])
+ };
+}
+
macro_rules! define_feedable {
($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
- impl<'tcx> TyCtxtFeed<'tcx> {
- $($(#[$attr])*
+ $(impl<'tcx, K: IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> {
+ $(#[$attr])*
#[inline(always)]
pub fn $name(self, value: $V) -> query_stored::$name<'tcx> {
- let key = self.def_id().into_query_param();
+ let key = self.key().into_query_param();
opt_remap_env_constness!([$($modifiers)*][key]);
let tcx = self.tcx;
match cached {
Ok(old) => {
- assert_eq!(
- value, old,
- "Trying to feed an already recorded value for query {} key={key:?}",
+ bug!(
+ "Trying to feed an already recorded value for query {} key={key:?}:\nold value: {old:?}\nnew value: {value:?}",
stringify!($name),
);
- return old;
}
Err(()) => (),
}
tcx,
key,
&value,
- dep_graph::hash_result,
+ hash_result!([$($modifiers)*]),
);
cache.complete(key, value, dep_node_index)
- })*
- }
+ }
+ })*
}
}
self.rebind(p.with_self_ty(tcx, self_ty)).to_predicate(tcx)
}
ExistentialPredicate::AutoTrait(did) => {
- let trait_ref = self.rebind(tcx.mk_trait_ref(did, [self_ty]));
- trait_ref.without_const().to_predicate(tcx)
+ let generics = tcx.generics_of(did);
+ let trait_ref = if generics.params.len() == 1 {
+ tcx.mk_trait_ref(did, [self_ty])
+ } else {
+ // If this is an ill-formed auto trait, then synthesize
+ // new error substs for the missing generics.
+ let err_substs =
+ ty::InternalSubsts::extend_with_error(tcx, did, &[self_ty.into()]);
+ tcx.mk_trait_ref(did, err_substs)
+ };
+ self.rebind(trait_ref).without_const().to_predicate(tcx)
}
}
}
impl<'tcx> Ty<'tcx> {
#[inline(always)]
pub fn kind(self) -> &'tcx TyKind<'tcx> {
- &self.0.0.kind
+ &self.0.0
}
#[inline(always)]
use crate::ty::visit::{TypeVisitable, TypeVisitor};
use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};
-use rustc_data_structures::intern::{Interned, WithStableHash};
+use rustc_data_structures::intern::Interned;
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
use rustc_serialize::{self, Decodable, Encodable};
+use rustc_type_ir::WithCachedTypeInfo;
use smallvec::SmallVec;
use core::intrinsics;
GenericArgKind::Type(ty) => {
// Ensure we can use the tag bits.
assert_eq!(mem::align_of_val(&*ty.0.0) & TAG_MASK, 0);
- (TYPE_TAG, ty.0.0 as *const WithStableHash<ty::TyS<'tcx>> as usize)
+ (TYPE_TAG, ty.0.0 as *const WithCachedTypeInfo<ty::TyKind<'tcx>> as usize)
}
GenericArgKind::Const(ct) => {
// Ensure we can use the tag bits.
&*((ptr & !TAG_MASK) as *const ty::RegionKind<'tcx>),
))),
TYPE_TAG => GenericArgKind::Type(Ty(Interned::new_unchecked(
- &*((ptr & !TAG_MASK) as *const WithStableHash<ty::TyS<'tcx>>),
+ &*((ptr & !TAG_MASK) as *const WithCachedTypeInfo<ty::TyKind<'tcx>>),
))),
CONST_TAG => GenericArgKind::Const(ty::Const(Interned::new_unchecked(
&*((ptr & !TAG_MASK) as *const ty::ConstS<'tcx>),
}
}
+ // Extend an `original_substs` list to the full number of substs expected by `def_id`,
+ // filling in the missing parameters with error ty/ct or 'static regions.
+ pub fn extend_with_error(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ original_substs: &[GenericArg<'tcx>],
+ ) -> SubstsRef<'tcx> {
+ ty::InternalSubsts::for_item(tcx, def_id, |def, substs| {
+ if let Some(subst) = original_substs.get(def.index as usize) {
+ *subst
+ } else {
+ def.to_error(tcx, substs)
+ }
+ })
+ }
+
#[inline]
pub fn types(&'tcx self) -> impl DoubleEndedIterator<Item = Ty<'tcx>> + 'tcx {
self.iter()
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
+use crate::mir;
use crate::ty::layout::IntegerExt;
use crate::ty::{
self, DefIdTree, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::GrowableBitSet;
+use rustc_index::vec::{Idx, IndexVec};
use rustc_macros::HashStable;
use rustc_span::{sym, DUMMY_SP};
use rustc_target::abi::{Integer, IntegerType, Size, TargetDataLayout};
pub fn bound_impl_subject(self, def_id: DefId) -> ty::EarlyBinder<ty::ImplSubject<'tcx>> {
ty::EarlyBinder(self.impl_subject(def_id))
}
+
+ /// Returns names of captured upvars for closures and generators.
+ ///
+ /// Here are some examples:
+ /// - `name__field1__field2` when the upvar is captured by value.
+ /// - `_ref__name__field` when the upvar is captured by reference.
+ ///
+ /// For generators this only contains upvars that are shared by all states.
+ pub fn closure_saved_names_of_captured_variables(
+ self,
+ def_id: DefId,
+ ) -> SmallVec<[String; 16]> {
+ let body = self.optimized_mir(def_id);
+
+ body.var_debug_info
+ .iter()
+ .filter_map(|var| {
+ let is_ref = match var.value {
+ mir::VarDebugInfoContents::Place(place)
+ if place.local == mir::Local::new(1) =>
+ {
+ // The projection is either `[.., Field, Deref]` or `[.., Field]`. It
+ // implies whether the variable is captured by value or by reference.
+ matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref)
+ }
+ _ => return None,
+ };
+ let prefix = if is_ref { "_ref__" } else { "" };
+ Some(prefix.to_owned() + var.name.as_str())
+ })
+ .collect()
+ }
+
+ // FIXME(eddyb) maybe precompute this? Right now it's computed once
+ // per generator monomorphization, but it doesn't depend on substs.
+ pub fn generator_layout_and_saved_local_names(
+ self,
+ def_id: DefId,
+ ) -> (
+ &'tcx ty::GeneratorLayout<'tcx>,
+ IndexVec<mir::GeneratorSavedLocal, Option<rustc_span::Symbol>>,
+ ) {
+ let tcx = self;
+ let body = tcx.optimized_mir(def_id);
+ let generator_layout = body.generator_layout().unwrap();
+ let mut generator_saved_local_names =
+ IndexVec::from_elem(None, &generator_layout.field_tys);
+
+ let state_arg = mir::Local::new(1);
+ for var in &body.var_debug_info {
+ let mir::VarDebugInfoContents::Place(place) = &var.value else { continue };
+ if place.local != state_arg {
+ continue;
+ }
+ match place.projection[..] {
+ [
+ // Deref of the `Pin<&mut Self>` state argument.
+ mir::ProjectionElem::Field(..),
+ mir::ProjectionElem::Deref,
+ // Field of a variant of the state.
+ mir::ProjectionElem::Downcast(_, variant),
+ mir::ProjectionElem::Field(field, _),
+ ] => {
+ let name = &mut generator_saved_local_names
+ [generator_layout.variant_fields[variant][field]];
+ if name.is_none() {
+ name.replace(var.name);
+ }
+ }
+ _ => {}
+ }
+ }
+ (generator_layout, generator_saved_local_names)
+ }
}
struct OpaqueTypeExpander<'tcx> {
-use std::convert::TryFrom;
use std::fmt;
use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar};
}
impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
- fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
+ fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo]) -> Self {
let err = tcx.ty_error();
- // FIXME(compiler-errors): It would be nice if we could get the
- // query key, so we could at least generate a fn signature that
- // has the right arity.
+
+ let arity = if let Some(frame) = stack.get(0)
+ && frame.query.name == "fn_sig"
+ && let Some(def_id) = frame.query.def_id
+ && let Some(node) = tcx.hir().get_if_local(def_id)
+ && let Some(sig) = node.fn_sig()
+ {
+ sig.decl.inputs.len() + sig.decl.implicit_self.has_implicit_self() as usize
+ } else {
+ tcx.sess.abort_if_errors();
+ unreachable!()
+ };
+
let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig(
- [].into_iter(),
+ std::iter::repeat(err).take(arity),
err,
false,
rustc_hir::Unsafety::Normal,
is_polymorphic: false,
tainted_by_errors: None,
injection_phase: None,
- pass_count: 1,
+ pass_count: 0,
};
body.local_decls.push(LocalDecl::new(return_ty, return_ty_span));
LogicalOp::And => (else_block, shortcircuit_block),
LogicalOp::Or => (shortcircuit_block, else_block),
};
- let term = TerminatorKind::if_(this.tcx, lhs, blocks.0, blocks.1);
+ let term = TerminatorKind::if_(lhs, blocks.0, blocks.1);
this.cfg.terminate(block, source_info, term);
this.cfg.push_assign_constant(
mod util;
use std::borrow::Borrow;
-use std::convert::TryFrom;
use std::mem;
impl<'a, 'tcx> Builder<'a, 'tcx> {
let then_block = this.cfg.start_new_block();
let else_block = this.cfg.start_new_block();
- let term = TerminatorKind::if_(this.tcx, operand, then_block, else_block);
+ let term = TerminatorKind::if_(operand, then_block, else_block);
let source_info = this.source_info(expr_span);
this.cfg.terminate(block, source_info, term);
self.source_info(match_start_span),
TerminatorKind::SwitchInt {
discr: Operand::Move(discr),
- switch_ty: discr_ty,
targets: switch_targets,
},
);
0 => (second_bb, first_bb),
v => span_bug!(test.span, "expected boolean value but got {:?}", v),
};
- TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb)
+ TerminatorKind::if_(Operand::Copy(place), true_bb, false_bb)
} else {
// The switch may be inexhaustive so we have a catch all block
debug_assert_eq!(options.len() + 1, target_blocks.len());
);
TerminatorKind::SwitchInt {
discr: Operand::Copy(place),
- switch_ty,
targets: switch_targets,
}
};
self.cfg.terminate(
block,
source_info,
- TerminatorKind::if_(self.tcx, Operand::Move(result), success_block, fail_block),
+ TerminatorKind::if_(Operand::Move(result), success_block, fail_block),
);
}
self.cfg.terminate(
eq_block,
source_info,
- TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block),
+ TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block),
);
}
use rustc_middle::ty;
use rustc_middle::ty::TypeVisitable;
use smallvec::SmallVec;
-use std::convert::TryInto;
impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn field_match_pairs<'pat>(
original_source_scope: SourceScope,
pattern_span: Span,
) {
- let tcx = self.tcx;
- let current_root = tcx.maybe_lint_level_root_bounded(arg_hir_id, self.hir_id);
- let parent_root = tcx.maybe_lint_level_root_bounded(
- self.source_scopes[original_source_scope]
- .local_data
- .as_ref()
- .assert_crate_local()
- .lint_root,
- self.hir_id,
- );
- if current_root != parent_root {
- self.source_scope =
- self.new_source_scope(pattern_span, LintLevel::Explicit(current_root), None);
- }
+ let parent_id = self.source_scopes[original_source_scope]
+ .local_data
+ .as_ref()
+ .assert_crate_local()
+ .lint_root;
+ self.maybe_new_source_scope(pattern_span, None, arg_hir_id, parent_id);
}
fn get_unit_temp(&mut self) -> Place<'tcx> {
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::HirId;
use rustc_index::vec::IndexVec;
use rustc_middle::middle::region;
use rustc_middle::mir::*;
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
{
let source_scope = self.source_scope;
- let tcx = self.tcx;
if let LintLevel::Explicit(current_hir_id) = lint_level {
- // Use `maybe_lint_level_root_bounded` with `root_lint_level` as a bound
- // to avoid adding Hir dependencies on our parents.
- // We estimate the true lint roots here to avoid creating a lot of source scopes.
-
- let parent_root = tcx.maybe_lint_level_root_bounded(
- self.source_scopes[source_scope].local_data.as_ref().assert_crate_local().lint_root,
- self.hir_id,
- );
- let current_root = tcx.maybe_lint_level_root_bounded(current_hir_id, self.hir_id);
-
- if parent_root != current_root {
- self.source_scope = self.new_source_scope(
- region_scope.1.span,
- LintLevel::Explicit(current_root),
- None,
- );
- }
+ let parent_id =
+ self.source_scopes[source_scope].local_data.as_ref().assert_crate_local().lint_root;
+ self.maybe_new_source_scope(region_scope.1.span, None, current_hir_id, parent_id);
}
self.push_scope(region_scope);
let mut block;
))
}
+ /// Possibly creates a new source scope if `current_root` and `parent_root`
+ /// are different, or if -Zmaximal-hir-to-mir-coverage is enabled.
+ pub(crate) fn maybe_new_source_scope(
+ &mut self,
+ span: Span,
+ safety: Option<Safety>,
+ current_id: HirId,
+ parent_id: HirId,
+ ) {
+ let (current_root, parent_root) =
+ if self.tcx.sess.opts.unstable_opts.maximal_hir_to_mir_coverage {
+ // Some consumers of rustc need to map MIR locations back to HIR nodes. Currently the
+ // the only part of rustc that tracks MIR -> HIR is the `SourceScopeLocalData::lint_root`
+ // field that tracks lint levels for MIR locations. Normally the number of source scopes
+ // is limited to the set of nodes with lint annotations. The -Zmaximal-hir-to-mir-coverage
+ // flag changes this behavior to maximize the number of source scopes, increasing the
+ // granularity of the MIR->HIR mapping.
+ (current_id, parent_id)
+ } else {
+ // Use `maybe_lint_level_root_bounded` with `self.hir_id` as a bound
+ // to avoid adding Hir dependencies on our parents.
+ // We estimate the true lint roots here to avoid creating a lot of source scopes.
+ (
+ self.tcx.maybe_lint_level_root_bounded(current_id, self.hir_id),
+ self.tcx.maybe_lint_level_root_bounded(parent_id, self.hir_id),
+ )
+ };
+
+ if current_root != parent_root {
+ let lint_level = LintLevel::Explicit(current_root);
+ self.source_scope = self.new_source_scope(span, lint_level, safety);
+ }
+ }
+
/// Creates a new source scope, nested in the current one.
pub(crate) fn new_source_scope(
&mut self,
hir::ExprKind::Field(ref source, ..) => ExprKind::Field {
lhs: self.mirror_expr(source),
variant_index: VariantIdx::new(0),
- name: Field::new(tcx.field_index(expr.hir_id, self.typeck_results)),
+ name: Field::new(self.typeck_results.field_index(expr.hir_id)),
},
hir::ExprKind::Cast(ref source, ref cast_ty) => {
// Check for a user-given type annotation on this `cast`
fields
.iter()
.map(|field| FieldExpr {
- name: Field::new(self.tcx.field_index(field.hir_id, self.typeck_results)),
+ name: Field::new(self.typeck_results.field_index(field.hir_id)),
expr: self.mirror_expr(field.expr),
})
.collect()
use std::cell::Cell;
use std::cmp::{self, max, min, Ordering};
use std::fmt;
-use std::iter::{once, IntoIterator};
+use std::iter::once;
use std::ops::RangeInclusive;
use smallvec::{smallvec, SmallVec};
let subpatterns = fields
.iter()
.map(|field| FieldPat {
- field: Field::new(self.tcx.field_index(field.hir_id, self.typeck_results)),
+ field: Field::new(self.typeck_results.field_index(field.hir_id)),
pattern: self.lower_pattern(&field.pat),
})
.collect();
source_info: self.source_info,
kind: TerminatorKind::SwitchInt {
discr: Operand::Move(discr),
- switch_ty: discr_ty,
targets: SwitchTargets::new(
values.iter().copied().zip(blocks.iter().copied()),
*blocks.last().unwrap(),
is_cleanup: unwind.is_cleanup(),
terminator: Some(Terminator {
source_info: self.source_info,
- kind: TerminatorKind::if_(tcx, move_(can_go), succ, drop_block),
+ kind: TerminatorKind::if_(move_(can_go), succ, drop_block),
}),
};
let loop_block = self.elaborator.patch().new_block(loop_block);
source_info: self.source_info,
kind: TerminatorKind::SwitchInt {
discr: move_(elem_size),
- switch_ty: tcx.types.usize,
targets: SwitchTargets::static_if(
0,
self.drop_loop_pair(ety, false, len),
DropStyle::Static => on_set,
DropStyle::Conditional | DropStyle::Open => {
let flag = self.elaborator.get_drop_flag(self.path).unwrap();
- let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset);
+ let term = TerminatorKind::if_(flag, on_set, on_unset);
self.new_block(unwind, term)
}
}
propagate(pred, &tmp);
}
- mir::TerminatorKind::SwitchInt { targets: _, ref discr, switch_ty: _ } => {
+ mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
body,
pred,
}
}
- SwitchInt { ref targets, ref discr, switch_ty: _ } => {
+ SwitchInt { ref targets, ref discr } => {
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
exit_state,
targets,
None if tcx.sess.opts.unstable_opts.dump_mir_dataflow
&& dump_enabled(tcx, A::NAME, def_id) =>
{
- create_dump_file(
- tcx,
- ".dot",
- None,
- A::NAME,
- &pass_name.unwrap_or("-----"),
- body.source,
- )?
+ create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
}
_ => return Ok(()),
//! ## `PartialOrd`
//!
//! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`]
-//! and [`MeetSemiLattice`] do not have [`PartialOrd`][std::cmp::PartialOrd] as a supertrait. This
+//! and [`MeetSemiLattice`] do not have [`PartialOrd`] as a supertrait. This
//! is because most standard library types use lexicographic ordering instead of set inclusion for
//! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a
//! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The
/// The dataflow state for an instance of [`ValueAnalysis`].
///
/// Every instance specifies a lattice that represents the possible values of a single tracked
-/// place. If we call this lattice `V` and set set of tracked places `P`, then a [`State`] is an
+/// place. If we call this lattice `V` and set of tracked places `P`, then a [`State`] is an
/// element of `{unreachable} ∪ (P -> V)`. This again forms a lattice, where the bottom element is
/// `unreachable` and the top element is the mapping `p ↦ ⊤`. Note that the mapping `p ↦ ⊥` is not
/// the bottom element (because joining an unreachable and any other reachable state yields a
pub struct AddRetag;
-/// Determines whether this place is "stable": Whether, if we evaluate it again
-/// after the assignment, we can be sure to obtain the same place value.
-/// (Concurrent accesses by other threads are no problem as these are anyway non-atomic
-/// copies. Data races are UB.)
-fn is_stable(place: PlaceRef<'_>) -> bool {
- // Which place this evaluates to can change with any memory write,
- // so cannot assume deref to be stable.
- !place.has_deref()
-}
-
/// Determine whether this type may contain a reference (or box), and thus needs retagging.
/// We will only recurse `depth` times into Tuples/ADTs to bound the cost of this.
fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> bool {
let basic_blocks = body.basic_blocks.as_mut();
let local_decls = &body.local_decls;
let needs_retag = |place: &Place<'tcx>| {
- // FIXME: Instead of giving up for unstable places, we should introduce
- // a temporary and retag on that.
- is_stable(place.as_ref())
+ !place.has_deref() // we're not eally interested in stores to "outside" locations, they are hard to keep track of anyway
&& may_contain_reference(place.ty(&*local_decls, tcx).ty, /*depth*/ 3, tcx)
&& !local_decls[place.local].is_deref_temp()
};
- let place_base_raw = |place: &Place<'tcx>| {
- // If this is a `Deref`, get the type of what we are deref'ing.
- if place.has_deref() {
- let ty = &local_decls[place.local].ty;
- ty.is_unsafe_ptr()
- } else {
- // Not a deref, and thus not raw.
- false
- }
- };
// PART 1
// Retag arguments at the beginning of the start block.
}
// PART 2
- // Retag return values of functions. Also escape-to-raw the argument of `drop`.
+ // Retag return values of functions.
// We collect the return destinations because we cannot mutate while iterating.
let returns = basic_blocks
.iter_mut()
}
// PART 3
- // Add retag after assignment.
+ // Add retag after assignments where data "enters" this function: the RHS is behind a deref and the LHS is not.
for block_data in basic_blocks {
// We want to insert statements as we iterate. To this end, we
// iterate backwards using indices.
for i in (0..block_data.statements.len()).rev() {
let (retag_kind, place) = match block_data.statements[i].kind {
- // Retag-as-raw after escaping to a raw pointer, if the referent
- // is not already a raw pointer.
- StatementKind::Assign(box (lplace, Rvalue::AddressOf(_, ref rplace)))
- if !place_base_raw(rplace) =>
- {
- (RetagKind::Raw, lplace)
- }
// Retag after assignments of reference type.
StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => {
- let kind = match rvalue {
- Rvalue::Ref(_, borrow_kind, _)
- if borrow_kind.allows_two_phase_borrow() =>
- {
- RetagKind::TwoPhase
- }
- _ => RetagKind::Default,
+ let add_retag = match rvalue {
+ // Ptr-creating operations already do their own internal retagging, no
+ // need to also add a retag statement.
+ Rvalue::Ref(..) | Rvalue::AddressOf(..) => false,
+ _ => true,
};
- (kind, *place)
+ if add_retag {
+ (RetagKind::Default, *place)
+ } else {
+ continue;
+ }
}
// Do nothing for the rest
_ => continue,
}
let target_bb_terminator = target_bb.terminator();
- let (discr, switch_ty, targets) = target_bb_terminator.kind.as_switch()?;
+ let (discr, targets) = target_bb_terminator.kind.as_switch()?;
if discr.place() == Some(*place) {
+ let switch_ty = place.ty(self.body.local_decls(), self.tcx).ty;
// We now know that the Switch matches on the const place, and it is statementless
// Now find which value in the Switch matches the const value.
let const_value =
let def_id = mir_source.def_id();
let span_viewables = span_viewables(tcx, mir_body, basic_coverage_blocks, &coverage_spans);
- let mut file = create_dump_file(tcx, "html", None, pass_name, &0, mir_source)
+ let mut file = create_dump_file(tcx, "html", false, pass_name, &0, mir_body)
.expect("Unexpected error creating MIR spanview HTML file");
let crate_name = tcx.crate_name(def_id.krate);
let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate();
.join("\n ")
));
}
- let mut file = create_dump_file(tcx, "dot", None, pass_name, &0, mir_source)
+ let mut file = create_dump_file(tcx, "dot", false, pass_name, &0, mir_body)
.expect("Unexpected error creating BasicCoverageBlock graphviz DOT file");
graphviz_writer
.write_graphviz(tcx, &mut file)
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::coverage::CoverageKind;
use rustc_middle::mir::*;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty;
use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP};
// All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`.
blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
dummy_place: Place<'tcx>,
next_local: usize,
- bool_ty: Ty<'tcx>,
}
impl<'tcx> MockBlocks<'tcx> {
blocks: IndexVec::new(),
dummy_place: Place { local: RETURN_PLACE, projection: ty::List::empty() },
next_local: 0,
- bool_ty: TyCtxt::BOOL_TY_FOR_UNIT_TESTING,
}
}
fn switchint(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
let switchint_kind = TerminatorKind::SwitchInt {
discr: Operand::Move(Place::from(self.new_temp())),
- switch_ty: self.bool_ty, // just a dummy value
targets: SwitchTargets::static_if(0, TEMP_BLOCK, TEMP_BLOCK),
};
self.add_block_from(some_from_block, switchint_kind)
round: usize,
) {
let mut reachable = None;
- dump_mir(tcx, None, "DestinationPropagation-dataflow", &round, body, |pass_where, w| {
+ dump_mir(tcx, false, "DestinationPropagation-dataflow", &round, body, |pass_where, w| {
let reachable = reachable.get_or_insert_with(|| traversal::reachable_as_bitset(body));
match pass_where {
//! This pass just dumps MIR at a specified point.
-use std::borrow::Cow;
use std::fs::File;
use std::io;
use rustc_middle::mir::write_mir_pretty;
use rustc_middle::mir::Body;
use rustc_middle::ty::TyCtxt;
-use rustc_session::config::{OutputFilenames, OutputType};
+use rustc_session::config::OutputType;
pub struct Marker(pub &'static str);
impl<'tcx> MirPass<'tcx> for Marker {
- fn name(&self) -> Cow<'_, str> {
- Cow::Borrowed(self.0)
+ fn name(&self) -> &str {
+ self.0
}
fn run_pass(&self, _tcx: TyCtxt<'tcx>, _body: &mut Body<'tcx>) {}
}
-pub fn emit_mir(tcx: TyCtxt<'_>, outputs: &OutputFilenames) -> io::Result<()> {
- let path = outputs.path(OutputType::Mir);
+pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> {
+ let path = tcx.output_filenames(()).path(OutputType::Mir);
let mut f = io::BufWriter::new(File::create(&path)?);
write_mir_pretty(tcx, None, &mut f)?;
Ok(())
let TerminatorKind::SwitchInt {
discr: parent_op,
- switch_ty: parent_ty,
targets: parent_targets
} = &bbs[parent].terminator().kind else {
unreachable!()
Operand::Copy(x) => Operand::Copy(*x),
Operand::Constant(x) => Operand::Constant(x.clone()),
};
+ let parent_ty = parent_op.ty(body.local_decls(), tcx);
let statements_before = bbs[parent].statements.len();
let parent_end = Location { block: parent, statement_index: statements_before };
// create temp to store inequality comparison between the two discriminants, `_t` in
// example above
let nequal = BinOp::Ne;
- let comp_res_type = nequal.ty(tcx, *parent_ty, opt_data.child_ty);
+ let comp_res_type = nequal.ty(tcx, parent_ty, opt_data.child_ty);
let comp_temp = patch.new_temp(comp_res_type, opt_data.child_source.span);
patch.add_statement(parent_end, StatementKind::StorageLive(comp_temp));
kind: TerminatorKind::SwitchInt {
// switch on the first discriminant, so we can mark the second one as dead
discr: parent_op,
- switch_ty: opt_data.child_ty,
targets: eq_targets,
},
}));
let false_case = eq_bb;
patch.patch_terminator(
parent,
- TerminatorKind::if_(
- tcx,
- Operand::Move(Place::from(comp_temp)),
- true_case,
- false_case,
- ),
+ TerminatorKind::if_(Operand::Move(Place::from(comp_temp)), true_case, false_case),
);
// generate StorageDead for the second_discriminant_temp not in use anymore
let bbs = &body.basic_blocks;
let TerminatorKind::SwitchInt {
targets,
- switch_ty: parent_ty,
- ..
+ discr: parent_discr,
} = &bbs[parent].terminator().kind else {
return None
};
+ let parent_ty = parent_discr.ty(body.local_decls(), tcx);
let parent_dest = {
let poss = targets.otherwise();
// If the fallthrough on the parent is trivially unreachable, we can let the
let (_, child) = targets.iter().next()?;
let child_terminator = &bbs[child].terminator();
let TerminatorKind::SwitchInt {
- switch_ty: child_ty,
targets: child_targets,
- ..
+ discr: child_discr,
} = &child_terminator.kind else {
return None
};
+ let child_ty = child_discr.ty(body.local_decls(), tcx);
if child_ty != parent_ty {
return None;
}
Some(OptimizationData {
destination,
child_place: *child_place,
- child_ty: *child_ty,
+ child_ty,
child_source: child_terminator.source_info,
})
}
let (assign, discr) = transform.get_discr(body);
let switch_targets =
SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block);
- let switch = TerminatorKind::SwitchInt {
- discr: Operand::Move(discr),
- switch_ty: transform.discr_ty,
- targets: switch_targets,
- };
+ let switch = TerminatorKind::SwitchInt { discr: Operand::Move(discr), targets: switch_targets };
let source_info = SourceInfo::outermost(body.span);
body.basic_blocks_mut().raw.insert(
tcx.mk_ptr(ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }),
source_info,
);
- if tcx.sess.opts.unstable_opts.mir_emit_retag {
- // Alias tracking must know we changed the type
- body.basic_blocks_mut()[START_BLOCK].statements.insert(
- 0,
- Statement {
- source_info,
- kind: StatementKind::Retag(RetagKind::Raw, Box::new(Place::from(SELF_ARG))),
- },
- )
- }
// Make sure we remove dead blocks to remove
// unrelated code from the resume part of the function
simplify::remove_dead_blocks(tcx, &mut body);
- dump_mir(tcx, None, "generator_drop", &0, &body, |_, _| Ok(()));
+ dump_mir(tcx, false, "generator_drop", &0, &body, |_, _| Ok(()));
body
}
// unrelated code from the drop part of the function
simplify::remove_dead_blocks(tcx, body);
- dump_mir(tcx, None, "generator_resume", &0, body, |_, _| Ok(()));
+ dump_mir(tcx, false, "generator_resume", &0, body, |_, _| Ok(()));
}
fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock {
// This is expanded to a drop ladder in `elaborate_generator_drops`.
let drop_clean = insert_clean_drop(body);
- dump_mir(tcx, None, "generator_pre-elab", &0, body, |_, _| Ok(()));
+ dump_mir(tcx, false, "generator_pre-elab", &0, body, |_, _| Ok(()));
// Expand `drop(generator_struct)` to a drop ladder which destroys upvars.
// If any upvars are moved out of, drop elaboration will handle upvar destruction.
// However we need to also elaborate the code generated by `insert_clean_drop`.
elaborate_generator_drops(tcx, body);
- dump_mir(tcx, None, "generator_post-transform", &0, body, |_, _| Ok(()));
+ dump_mir(tcx, false, "generator_post-transform", &0, body, |_, _| Ok(()));
// Create a copy of our MIR and use it to create the drop shim for the generator
let drop_shim = create_generator_drop_shim(tcx, &transform, gen_ty, body, drop_clean);
use rustc_middle::mir::*;
use rustc_middle::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
use rustc_session::config::OptLevel;
-use rustc_span::def_id::DefId;
use rustc_span::{hygiene::ExpnKind, ExpnData, LocalExpnId, Span};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi;
let param_env = tcx.param_env_reveal_all_normalized(def_id);
- let mut this = Inliner {
- tcx,
- param_env,
- codegen_fn_attrs: tcx.codegen_fn_attrs(def_id),
- history: Vec::new(),
- changed: false,
- };
+ let mut this =
+ Inliner { tcx, param_env, codegen_fn_attrs: tcx.codegen_fn_attrs(def_id), changed: false };
let blocks = BasicBlock::new(0)..body.basic_blocks.next_index();
this.process_blocks(body, blocks);
this.changed
param_env: ParamEnv<'tcx>,
/// Caller codegen attributes.
codegen_fn_attrs: &'tcx CodegenFnAttrs,
- /// Stack of inlined instances.
- /// We only check the `DefId` and not the substs because we want to
- /// avoid inlining cases of polymorphic recursion.
- /// The number of `DefId`s is finite, so checking history is enough
- /// to ensure that we do not loop endlessly while inlining.
- history: Vec<DefId>,
/// Indicates that the caller body has been modified.
changed: bool,
}
debug!("not-inlined {} [{}]", callsite.callee, reason);
continue;
}
- Ok(new_blocks) => {
+ Ok(_) => {
debug!("inlined {}", callsite.callee);
self.changed = true;
- self.history.push(callsite.callee.def_id());
- self.process_blocks(caller_body, new_blocks);
- self.history.pop();
+ // We could process the blocks returned by `try_inlining` here. However, that
+ // leads to exponential compile times due to the top-down nature of this kind
+ // of inlining.
}
}
}
return None;
}
- if self.history.contains(&callee.def_id()) {
- return None;
- }
-
let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, substs);
return Some(CallSite {
return Err("C variadic");
}
- if callee_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
- return Err("naked");
- }
-
if callee_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
return Err("cold");
}
continue;
}
- let (discr, val, switch_ty, first, second) = match bbs[bb_idx].terminator().kind {
+ let (discr, val, first, second) = match bbs[bb_idx].terminator().kind {
TerminatorKind::SwitchInt {
discr: ref discr @ (Operand::Copy(_) | Operand::Move(_)),
- switch_ty,
ref targets,
..
} if targets.iter().len() == 1 => {
if target == targets.otherwise() {
continue;
}
- (discr, value, switch_ty, target, targets.otherwise())
+ (discr, value, target, targets.otherwise())
}
// Only optimize switch int statements
_ => continue,
}
// Take ownership of items now that we know we can optimize.
let discr = discr.clone();
+ let discr_ty = discr.ty(&body.local_decls, tcx);
// Introduce a temporary for the discriminant value.
let source_info = bbs[bb_idx].terminator().source_info;
- let discr_local = body.local_decls.push(LocalDecl::new(switch_ty, source_info.span));
+ let discr_local = body.local_decls.push(LocalDecl::new(discr_ty, source_info.span));
// We already checked that first and second are different blocks,
// and bb_idx has a different terminator from both of them.
(*f).clone()
} else {
// Different value between blocks. Make value conditional on switch condition.
- let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
+ let size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size;
let const_cmp = Operand::const_from_scalar(
tcx,
- switch_ty,
+ discr_ty,
rustc_const_eval::interpret::Scalar::from_uint(val, size),
rustc_span::DUMMY_SP,
);
-use std::borrow::Cow;
-
use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
/// Just like `MirPass`, except it cannot mutate `Body`.
pub trait MirLint<'tcx> {
- fn name(&self) -> Cow<'_, str> {
+ fn name(&self) -> &str {
let name = std::any::type_name::<Self>();
- if let Some(tail) = name.rfind(':') {
- Cow::from(&name[tail + 1..])
- } else {
- Cow::from(name)
- }
+ if let Some((_, tail)) = name.rsplit_once(':') { tail } else { name }
}
fn is_enabled(&self, _sess: &Session) -> bool {
where
T: MirLint<'tcx>,
{
- fn name(&self) -> Cow<'_, str> {
+ fn name(&self) -> &str {
self.0.name()
}
where
T: MirPass<'tcx>,
{
- fn name(&self) -> Cow<'_, str> {
+ fn name(&self) -> &str {
self.1.name()
}
}
body.phase = new_phase;
+ body.pass_count = 0;
dump_mir_for_phase_change(tcx, body);
if validate || new_phase == MirPhase::Runtime(RuntimePhase::Optimized) {
- validate_body(tcx, body, format!("after phase change to {}", new_phase));
+ validate_body(tcx, body, format!("after phase change to {}", new_phase.name()));
}
body.pass_count = 1;
pass_name: &str,
is_after: bool,
) {
- let phase_index = body.phase.phase_index();
-
mir::dump_mir(
tcx,
- Some(&format_args!("{:03}-{:03}", phase_index, body.pass_count)),
+ true,
pass_name,
if is_after { &"after" } else { &"before" },
body,
}
pub fn dump_mir_for_phase_change<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
- let phase_index = body.phase.phase_index();
-
- mir::dump_mir(
- tcx,
- Some(&format_args!("{:03}-000", phase_index)),
- &format!("{}", body.phase),
- &"after",
- body,
- |_, _| Ok(()),
- )
+ assert_eq!(body.pass_count, 0);
+ mir::dump_mir(tcx, true, body.phase.name(), &"after", body, |_, _| Ok(()))
}
//! Removes assignments to ZST places.
use crate::MirPass;
-use rustc_middle::mir::tcx::PlaceTy;
-use rustc_middle::mir::{Body, LocalDecls, Place, StatementKind};
+use rustc_middle::mir::{Body, StatementKind};
use rustc_middle::ty::{self, Ty, TyCtxt};
pub struct RemoveZsts;
if !layout.is_zst() {
continue;
}
- if involves_a_union(place, local_decls, tcx) {
- continue;
- }
if tcx.consider_optimizing(|| {
format!(
"RemoveZsts - Place: {:?} SourceInfo: {:?}",
_ => false,
}
}
-
-/// Miri lazily allocates memory for locals on assignment,
-/// so we must preserve writes to unions and union fields,
-/// or it will ICE on reads of those fields.
-fn involves_a_union<'tcx>(
- place: Place<'tcx>,
- local_decls: &LocalDecls<'tcx>,
- tcx: TyCtxt<'tcx>,
-) -> bool {
- let mut place_ty = PlaceTy::from_ty(local_decls[place.local].ty);
- if place_ty.ty.is_union() {
- return true;
- }
- for elem in place.projection {
- place_ty = place_ty.projection_ty(tcx, elem);
- if place_ty.ty.is_union() {
- return true;
- }
- }
- return false;
-}
if ty.is_some() {
// The first argument (index 0), but add 1 for the return value.
let dropee_ptr = Place::from(Local::new(1 + 0));
- if tcx.sess.opts.unstable_opts.mir_emit_retag {
- // Function arguments should be retagged, and we make this one raw.
- body.basic_blocks_mut()[START_BLOCK].statements.insert(
- 0,
- Statement {
- source_info,
- kind: StatementKind::Retag(RetagKind::Raw, Box::new(dropee_ptr)),
- },
- );
- }
let patch = {
let param_env = tcx.param_env_reveal_all_normalized(def_id);
let mut elaborator =
statements.push(statement);
*kind = TerminatorKind::SwitchInt {
discr: Operand::Move(temp),
- switch_ty: discr_ty,
targets: SwitchTargets::new(cases.into_iter(), unreachable),
};
}
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use smallvec::SmallVec;
-use std::borrow::Cow;
-use std::convert::TryInto;
pub struct SimplifyCfg {
label: String,
}
impl<'tcx> MirPass<'tcx> for SimplifyCfg {
- fn name(&self) -> Cow<'_, str> {
- Cow::Borrowed(&self.label)
+ fn name(&self) -> &str {
+ &self.label
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
-use std::borrow::Cow;
-
/// A pass that replaces a branch with a goto when its condition is known.
pub struct SimplifyConstCondition {
label: String,
}
impl<'tcx> MirPass<'tcx> for SimplifyConstCondition {
- fn name(&self) -> Cow<'_, str> {
- Cow::Borrowed(&self.label)
+ fn name(&self) -> &str {
+ &self.label
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let terminator = block.terminator_mut();
terminator.kind = match terminator.kind {
TerminatorKind::SwitchInt {
- discr: Operand::Constant(ref c),
- switch_ty,
- ref targets,
- ..
+ discr: Operand::Constant(ref c), ref targets, ..
} => {
- let constant = c.literal.try_eval_bits(tcx, param_env, switch_ty);
+ let constant = c.literal.try_eval_bits(tcx, param_env, c.ty());
if let Some(constant) = constant {
let target = targets.target_for_value(constant);
TerminatorKind::Goto { target }
let targets = SwitchTargets::new(iter::once((new_value, bb_cond)), bb_otherwise);
let terminator = bb.terminator_mut();
- terminator.kind = TerminatorKind::SwitchInt {
- discr: Operand::Move(opt.to_switch_on),
- switch_ty: opt.branch_value_ty,
- targets,
- };
+ terminator.kind =
+ TerminatorKind::SwitchInt { discr: Operand::Move(opt.to_switch_on), targets };
}
for (idx, bb_idx) in storage_deads_to_remove {
let terminator = match terminator_kind {
// This will unconditionally run into an unreachable and is therefore unreachable as well.
TerminatorKind::Goto { target } if is_unreachable(*target) => TerminatorKind::Unreachable,
- TerminatorKind::SwitchInt { targets, discr, switch_ty } => {
+ TerminatorKind::SwitchInt { targets, discr } => {
let otherwise = targets.otherwise();
// If all targets are unreachable, we can be unreachable as well.
return None;
}
- TerminatorKind::SwitchInt {
- discr: discr.clone(),
- switch_ty: *switch_ty,
- targets: new_targets,
- }
+ TerminatorKind::SwitchInt { discr: discr.clone(), targets: new_targets }
} else {
// If the otherwise branch is reachable, we don't want to delete any unreachable branches.
return None;
Const, Ty, TyCtxt,
};
use rustc_span::symbol::sym;
-use std::convert::TryInto;
use std::ops::ControlFlow;
use crate::errors::UnusedGenericParams;
use rustc_ast::token::{self, Delimiter, Nonterminal};
use rustc_errors::{error_code, fluent, Diagnostic, IntoDiagnostic, PResult};
use rustc_span::{sym, BytePos, Span};
-use std::convert::TryInto;
// Public for rustfmt usage
#[derive(Debug)]
use rustc_session::parse::ParseSess;
use rustc_span::{sym, Span, DUMMY_SP};
-use std::convert::TryInto;
use std::ops::Range;
/// A wrapper type to ensure that the parser handles outer attributes correctly.
// want to keep their span info to improve diagnostics in these cases in a later stage.
(true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3`
(true, Some(AssocOp::Subtract)) | // `{ 42 } -5`
- (true, Some(AssocOp::Add)) // `{ 42 } + 42
- // If the next token is a keyword, then the tokens above *are* unambiguously incorrect:
- // `if x { a } else { b } && if y { c } else { d }`
- if !self.look_ahead(1, |t| t.is_used_keyword()) => {
- // These cases are ambiguous and can't be identified in the parser alone.
- let sp = self.sess.source_map().start_point(self.token.span);
- self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);
- false
- }
- (true, Some(AssocOp::LAnd)) |
- (true, Some(AssocOp::LOr)) |
- (true, Some(AssocOp::BitOr)) => {
- // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`. Separated from the
- // above due to #74233.
+ (true, Some(AssocOp::Add)) | // `{ 42 } + 42` (unary plus)
+ (true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
+ (true, Some(AssocOp::LOr)) | // `{ 42 } || 42` ("logical or" or closure)
+ (true, Some(AssocOp::BitOr)) // `{ 42 } | 42` or `{ 42 } |x| 42`
+ => {
// These cases are ambiguous and can't be identified in the parser alone.
//
// Bitwise AND is left out because guessing intent is hard. We can make
};
let capture_clause = self.parse_capture_clause()?;
- let fn_decl = self.parse_fn_block_decl()?;
+ let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?;
let decl_hi = self.prev_token.span;
let mut body = match fn_decl.output {
FnRetTy::Default(_) => {
fn_decl,
body,
fn_decl_span: lo.to(decl_hi),
+ fn_arg_span,
})),
);
}
/// Parses the `|arg, arg|` header of a closure.
- fn parse_fn_block_decl(&mut self) -> PResult<'a, P<FnDecl>> {
+ fn parse_fn_block_decl(&mut self) -> PResult<'a, (P<FnDecl>, Span)> {
+ let arg_start = self.token.span.lo();
+
let inputs = if self.eat(&token::OrOr) {
Vec::new()
} else {
self.expect_or()?;
args
};
+ let arg_span = self.prev_token.span.with_lo(arg_start);
let output =
self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes, RecoverReturnSign::Yes)?;
- Ok(P(FnDecl { inputs, output }))
+ Ok((P(FnDecl { inputs, output }), arg_span))
}
/// Parses a parameter in a closure header (e.g., `|arg, arg|`).
use rustc_span::source_map::{self, Span};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::DUMMY_SP;
-use std::convert::TryFrom;
use std::mem;
use thin_vec::ThinVec;
use tracing::debug;
}
match parse_item(self) {
Ok(None) => {
- let is_unnecessary_semicolon = !items.is_empty()
+ let mut is_unnecessary_semicolon = !items.is_empty()
// When the close delim is `)` in a case like the following, `token.kind` is expected to be `token::CloseDelim(Delimiter::Parenthesis)`,
- // but the actual `token.kind` is `token::CloseDelim(Delimiter::Bracket)`.
+ // but the actual `token.kind` is `token::CloseDelim(Delimiter::Brace)`.
// This is because the `token.kind` of the close delim is treated as the same as
// that of the open delim in `TokenTreesReader::parse_token_tree`, even if the delimiters of them are different.
// Therefore, `token.kind` should not be compared here.
.span_to_snippet(self.prev_token.span)
.map_or(false, |snippet| snippet == "}")
&& self.token.kind == token::Semi;
- let semicolon_span = self.token.span;
+ let mut semicolon_span = self.token.span;
+ if !is_unnecessary_semicolon {
+ // #105369, Detect spurious `;` before assoc fn body
+ is_unnecessary_semicolon = self.token == token::OpenDelim(Delimiter::Brace)
+ && self.prev_token.kind == token::Semi;
+ semicolon_span = self.prev_token.span;
+ }
// We have to bail or we'll potentially never make progress.
let non_item_span = self.token.span;
let is_let = self.token.is_keyword(kw::Let);
prefix.span = lo.to(self.prev_token.span);
}
- UseTreeKind::Simple(self.parse_rename()?, DUMMY_NODE_ID, DUMMY_NODE_ID)
+ UseTreeKind::Simple(self.parse_rename()?)
}
};
Ok((Some(vr), TrailingToken::MaybeComma))
},
- )
+ ).map_err(|mut err|{
+ err.help("enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`");
+ err
+ })
}
/// Parses `struct Foo { ... }`.
Err(e) => {
// Parsing failed, therefore it must be something more serious
// than just a missing separator.
+ for xx in &e.children {
+ // propagate the help message from sub error 'e' to main error 'expect_err;
+ expect_err.children.push(xx.clone());
+ }
expect_err.emit();
e.cancel();
Ok(Some(if self.token.is_keyword(kw::Let) {
self.parse_local_mk(lo, attrs, capture_semi, force_collect)?
- } else if self.is_kw_followed_by_ident(kw::Mut) {
- self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::MissingLet)?
- } else if self.is_kw_followed_by_ident(kw::Auto) {
+ } else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
+ self.recover_stmt_local_after_let(lo, attrs, InvalidVariableDeclarationSub::MissingLet)?
+ } else if self.is_kw_followed_by_ident(kw::Auto) && self.may_recover() {
self.bump(); // `auto`
- self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::UseLetNotAuto)?
- } else if self.is_kw_followed_by_ident(sym::var) {
+ self.recover_stmt_local_after_let(
+ lo,
+ attrs,
+ InvalidVariableDeclarationSub::UseLetNotAuto,
+ )?
+ } else if self.is_kw_followed_by_ident(sym::var) && self.may_recover() {
self.bump(); // `var`
- self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::UseLetNotVar)?
+ self.recover_stmt_local_after_let(
+ lo,
+ attrs,
+ InvalidVariableDeclarationSub::UseLetNotVar,
+ )?
} else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() {
// We have avoided contextual keywords like `union`, items with `crate` visibility,
// or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
}
}
- fn recover_stmt_local(
+ fn recover_stmt_local_after_let(
&mut self,
lo: Span,
attrs: AttrWrapper,
subdiagnostic: fn(Span) -> InvalidVariableDeclarationSub,
) -> PResult<'a, Stmt> {
- let stmt = self.recover_local_after_let(lo, attrs)?;
+ let stmt =
+ self.collect_tokens_trailing_token(attrs, ForceCollect::Yes, |this, attrs| {
+ let local = this.parse_local(attrs)?;
+ // FIXME - maybe capture semicolon in recovery?
+ Ok((
+ this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)),
+ TrailingToken::None,
+ ))
+ })?;
self.sess.emit_err(InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
Ok(stmt)
}
})
}
- fn recover_local_after_let(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
- self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
- let local = this.parse_local(attrs)?;
- // FIXME - maybe capture semicolon in recovery?
- Ok((
- this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)),
- TrailingToken::None,
- ))
- })
- }
-
/// Parses a local variable declaration.
fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
let lo = self.prev_token.span;
[dependencies]
rustc_lexer = { path = "../rustc_lexer" }
+rustc_data_structures = { path = "../rustc_data_structures" }
/// A piece is a portion of the format string which represents the next part
/// to emit. These are emitted as a stream by the `Parser` class.
-#[derive(Copy, Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq)]
pub enum Piece<'a> {
/// A literal string which should directly be emitted
String(&'a str),
/// This describes that formatting should process the next argument (as
/// specified inside) for emission.
- NextArgument(Argument<'a>),
+ NextArgument(Box<Argument<'a>>),
}
/// Representation of an argument specification.
} else {
self.suggest_positional_arg_instead_of_captured_arg(arg);
}
- Some(NextArgument(arg))
+ Some(NextArgument(Box::new(arg)))
}
}
'}' => {
(skips, true)
}
+// Assert a reasonable size for `Piece`
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(Piece<'_>, 16);
+
#[cfg(test)]
mod tests;
fn format_nothing() {
same(
"{}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(0),
position_span: InnerSpan { start: 2, end: 2 },
format: fmtdflt(),
- })],
+ }))],
);
}
#[test]
fn format_position() {
same(
"{3}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentIs(3),
position_span: InnerSpan { start: 2, end: 3 },
format: fmtdflt(),
- })],
+ }))],
);
}
#[test]
fn format_position_nothing_else() {
same(
"{3:}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentIs(3),
position_span: InnerSpan { start: 2, end: 3 },
format: fmtdflt(),
- })],
+ }))],
);
}
#[test]
fn format_named() {
same(
"{name}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentNamed("name"),
position_span: InnerSpan { start: 2, end: 6 },
format: fmtdflt(),
- })],
+ }))],
)
}
#[test]
fn format_type() {
same(
"{3:x}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentIs(3),
position_span: InnerSpan { start: 2, end: 3 },
format: FormatSpec {
ty: "x",
ty_span: None,
},
- })],
+ }))],
);
}
#[test]
fn format_align_fill() {
same(
"{3:>}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentIs(3),
position_span: InnerSpan { start: 2, end: 3 },
format: FormatSpec {
ty: "",
ty_span: None,
},
- })],
+ }))],
);
same(
"{3:0<}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentIs(3),
position_span: InnerSpan { start: 2, end: 3 },
format: FormatSpec {
ty: "",
ty_span: None,
},
- })],
+ }))],
);
same(
"{3:*<abcd}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentIs(3),
position_span: InnerSpan { start: 2, end: 3 },
format: FormatSpec {
ty: "abcd",
ty_span: Some(InnerSpan::new(6, 10)),
},
- })],
+ }))],
);
}
#[test]
fn format_counts() {
same(
"{:10x}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(0),
position_span: InnerSpan { start: 2, end: 2 },
format: FormatSpec {
ty: "x",
ty_span: None,
},
- })],
+ }))],
);
same(
"{:10$.10x}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(0),
position_span: InnerSpan { start: 2, end: 2 },
format: FormatSpec {
ty: "x",
ty_span: None,
},
- })],
+ }))],
);
same(
"{1:0$.10x}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentIs(1),
position_span: InnerSpan { start: 2, end: 3 },
format: FormatSpec {
ty: "x",
ty_span: None,
},
- })],
+ }))],
);
same(
"{:.*x}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(1),
position_span: InnerSpan { start: 2, end: 2 },
format: FormatSpec {
ty: "x",
ty_span: None,
},
- })],
+ }))],
);
same(
"{:.10$x}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(0),
position_span: InnerSpan { start: 2, end: 2 },
format: FormatSpec {
ty: "x",
ty_span: None,
},
- })],
+ }))],
);
same(
"{:a$.b$?}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(0),
position_span: InnerSpan { start: 2, end: 2 },
format: FormatSpec {
ty: "?",
ty_span: None,
},
- })],
+ }))],
);
same(
"{:.4}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(0),
position_span: InnerSpan { start: 2, end: 2 },
format: FormatSpec {
ty: "",
ty_span: None,
},
- })],
+ }))],
)
}
#[test]
fn format_flags() {
same(
"{:-}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(0),
position_span: InnerSpan { start: 2, end: 2 },
format: FormatSpec {
ty: "",
ty_span: None,
},
- })],
+ }))],
);
same(
"{:+#}",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(0),
position_span: InnerSpan { start: 2, end: 2 },
format: FormatSpec {
ty: "",
ty_span: None,
},
- })],
+ }))],
);
}
#[test]
"abcd {3:x} efg",
&[
String("abcd "),
- NextArgument(Argument {
+ NextArgument(Box::new(Argument {
position: ArgumentIs(3),
position_span: InnerSpan { start: 7, end: 8 },
format: FormatSpec {
ty: "x",
ty_span: None,
},
- }),
+ })),
String(" efg"),
],
);
fn format_whitespace() {
same(
"{ }",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(0),
position_span: InnerSpan { start: 2, end: 3 },
format: fmtdflt(),
- })],
+ }))],
);
same(
"{ }",
- &[NextArgument(Argument {
+ &[NextArgument(Box::new(Argument {
position: ArgumentImplicitlyIs(0),
position_span: InnerSpan { start: 2, end: 4 },
format: fmtdflt(),
- })],
+ }))],
);
}
fn handle_field_access(&mut self, lhs: &hir::Expr<'_>, hir_id: hir::HirId) {
match self.typeck_results().expr_ty_adjusted(lhs).kind() {
ty::Adt(def, _) => {
- let index = self.tcx.field_index(hir_id, self.typeck_results());
+ let index = self.typeck_results().field_index(hir_id);
self.insert_def_id(def.non_enum_variant().fields[index].did);
}
ty::Tuple(..) => {}
if let PatKind::Wild = pat.pat.kind {
continue;
}
- let index = self.tcx.field_index(pat.hir_id, self.typeck_results());
+ let index = self.typeck_results().field_index(pat.hir_id);
self.insert_def_id(variant.fields[index].did);
}
}
fn mark_as_used_if_union(&mut self, adt: ty::AdtDef<'tcx>, fields: &[hir::ExprField<'_>]) {
if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did().is_local() {
for field in fields {
- let index = self.tcx.field_index(field.hir_id, self.typeck_results());
+ let index = self.typeck_results().field_index(field.hir_id);
self.insert_def_id(adt.non_enum_variant().fields[index].did);
}
}
self.in_pat = false;
}
- fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+ fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
self.handle_res(path.res);
intravisit::walk_path(self, path);
}
hir_visit::walk_fn(self, fk, fd, b, id)
}
- fn visit_use(&mut self, p: &'v hir::Path<'v>, hir_id: hir::HirId) {
+ fn visit_use(&mut self, p: &'v hir::UsePath<'v>, hir_id: hir::HirId) {
// This is `visit_use`, but the type is `Path` so record it that way.
self.record("Path", Id::None, p);
hir_visit::walk_use(self, p, hir_id)
hir_visit::walk_lifetime(self, lifetime)
}
- fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
+ fn visit_path(&mut self, path: &hir::Path<'v>, _id: hir::HirId) {
self.record("Path", Id::None, path);
hir_visit::walk_path(self, path)
}
.or_insert_with(|| (ln, var, vec![id_and_sp]));
});
- let can_remove = matches!(&pat.kind, hir::PatKind::Struct(_, _, true));
+ let can_remove = match pat.kind {
+ hir::PatKind::Struct(_, fields, true) => {
+ // if all fields are shorthand, remove the struct field, otherwise, mark with _ as prefix
+ fields.iter().all(|f| f.is_shorthand)
+ }
+ _ => false,
+ };
for (_, (ln, var, hir_ids_and_spans)) in vars {
if self.used_on_entry(ln, var) {
intravisit::walk_item(self, item);
}
- fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) {
+ fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {
if let Some(def_id) = path.res.opt_def_id() {
let method_span = path.segments.last().map(|s| s.ident.span);
let item_is_allowed = self.tcx.check_stability_allow_unstable(
}
impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
- fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _id: hir::HirId) {
+ fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: hir::HirId) {
if let Some(def_id) = path.res.opt_def_id() {
if let Some(stab) = self.tcx.lookup_stability(def_id) {
self.fully_stable &= stab.level.is_stable();
}
impl<'tcx> Visitor<'tcx> for CaptureCollector<'_, 'tcx> {
- fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+ fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
if let Res::Local(var_id) = path.res {
self.visit_local_use(var_id, path.span);
}
// are checked for privacy (RFC 736). Rather than computing the set of
// unmentioned fields, just check them all.
for (vf_index, variant_field) in variant.fields.iter().enumerate() {
- let field = fields.iter().find(|f| {
- self.tcx.field_index(f.hir_id, self.typeck_results()) == vf_index
- });
+ let field = fields
+ .iter()
+ .find(|f| self.typeck_results().field_index(f.hir_id) == vf_index);
let (use_ctxt, span) = match field {
Some(field) => (field.ident.span, field.span),
None => (base.span, base.span),
} else {
for field in fields {
let use_ctxt = field.ident.span;
- let index = self.tcx.field_index(field.hir_id, self.typeck_results());
+ let index = self.typeck_results().field_index(field.hir_id);
self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false);
}
}
let variant = adt.variant_of_res(res);
for field in fields {
let use_ctxt = field.ident.span;
- let index = self.tcx.field_index(field.hir_id, self.typeck_results());
+ let index = self.typeck_results().field_index(field.hir_id);
self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false);
}
}
pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1);
}
-impl std::convert::From<DepNodeIndex> for QueryInvocationId {
+impl From<DepNodeIndex> for QueryInvocationId {
#[inline]
fn from(dep_node_index: DepNodeIndex) -> Self {
QueryInvocationId(dep_node_index.as_u32())
cx: Ctxt,
key: A,
result: &R,
- hash_result: fn(&mut StableHashingContext<'_>, &R) -> Fingerprint,
+ hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
) -> DepNodeIndex {
if let Some(data) = self.data.as_ref() {
// The caller query has more dependencies than the node we are creating. We may
// For sanity, we still check that the loaded stable hash and the new one match.
if let Some(dep_node_index) = self.dep_node_index_of_opt(&node) {
let _current_fingerprint =
- crate::query::incremental_verify_ich(cx, result, &node, Some(hash_result));
+ crate::query::incremental_verify_ich(cx, result, &node, hash_result);
#[cfg(debug_assertions)]
- data.current.record_edge(dep_node_index, node, _current_fingerprint);
+ if hash_result.is_some() {
+ data.current.record_edge(dep_node_index, node, _current_fingerprint);
+ }
return dep_node_index;
}
let mut edges = SmallVec::new();
K::read_deps(|task_deps| match task_deps {
TaskDepsRef::Allow(deps) => edges.extend(deps.lock().reads.iter().copied()),
- TaskDepsRef::Ignore | TaskDepsRef::Forbid => {
+ TaskDepsRef::Ignore => {} // During HIR lowering, we have no dependencies.
+ TaskDepsRef::Forbid => {
panic!("Cannot summarize when dependencies are not recorded.")
}
});
let hashing_timer = cx.profiler().incr_result_hashing();
- let current_fingerprint =
- cx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result));
+ let current_fingerprint = hash_result.map(|hash_result| {
+ cx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result))
+ });
let print_status = cfg!(debug_assertions) && cx.sess().opts.unstable_opts.dep_tasks;
&data.previous,
node,
edges,
- Some(current_fingerprint),
+ current_fingerprint,
print_status,
);
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder};
use rustc_serialize::{Decodable, Decoder, Encodable};
use smallvec::SmallVec;
-use std::convert::TryInto;
// The maximum value of `SerializedDepNodeIndex` leaves the upper two bits
// unused so that we can store multiple index types in `CompressedHybridIndex`,
use rustc_data_structures::sync::Lrc;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::definitions::{DefPathHash, Definitions};
-use rustc_index::vec::IndexVec;
-use rustc_session::cstore::CrateStore;
+use rustc_hir::definitions::DefPathHash;
+use rustc_session::cstore::Untracked;
use rustc_session::Session;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::Symbol;
/// things (e.g., each `DefId`/`DefPath` is only hashed once).
#[derive(Clone)]
pub struct StableHashingContext<'a> {
- definitions: &'a Definitions,
- cstore: &'a dyn CrateStore,
- source_span: &'a IndexVec<LocalDefId, Span>,
+ untracked: &'a Untracked,
// The value of `-Z incremental-ignore-spans`.
// This field should only be used by `unstable_opts_incremental_ignore_span`
incremental_ignore_spans: bool,
impl<'a> StableHashingContext<'a> {
#[inline]
- pub fn new(
- sess: &'a Session,
- definitions: &'a Definitions,
- cstore: &'a dyn CrateStore,
- source_span: &'a IndexVec<LocalDefId, Span>,
- ) -> Self {
+ pub fn new(sess: &'a Session, untracked: &'a Untracked) -> Self {
let hash_spans_initial = !sess.opts.unstable_opts.incremental_ignore_spans;
StableHashingContext {
body_resolver: BodyResolver::Forbidden,
- definitions,
- cstore,
- source_span,
+ untracked,
incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans,
caching_source_map: None,
raw_source_map: sess.source_map(),
if let Some(def_id) = def_id.as_local() {
self.local_def_path_hash(def_id)
} else {
- self.cstore.def_path_hash(def_id)
+ self.untracked.cstore.def_path_hash(def_id)
}
}
#[inline]
pub fn local_def_path_hash(&self, def_id: LocalDefId) -> DefPathHash {
- self.definitions.def_path_hash(def_id)
+ self.untracked.definitions.read().def_path_hash(def_id)
}
#[inline]
#[inline]
fn def_span(&self, def_id: LocalDefId) -> Span {
- *self.source_span.get(def_id).unwrap_or(&DUMMY_SP)
+ *self.untracked.source_span.get(def_id).unwrap_or(&DUMMY_SP)
}
#[inline]
use rustc_data_structures::sync::Lock;
use rustc_data_structures::sync::WorkerLocal;
use rustc_index::vec::{Idx, IndexVec};
-use std::default::Default;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
rustc_data_structures::{jobserver, OnDrop},
rustc_rayon_core as rayon_core,
rustc_span::DUMMY_SP,
- std::iter::{self, FromIterator},
- std::{mem, process},
+ std::iter,
+ std::process,
};
/// Represents a span and a query key.
jobserver::release_thread();
waiter.condvar.wait(&mut info);
// Release the lock before we potentially block in `acquire_thread`
- mem::drop(info);
+ drop(info);
jobserver::acquire_thread();
}
}
prefix.is_empty() || prefix.len() == 1 && prefix[0].ident.name == kw::PathRoot
};
match use_tree.kind {
- ast::UseTreeKind::Simple(rename, id1, id2) => {
+ ast::UseTreeKind::Simple(rename) => {
let mut ident = use_tree.ident();
let mut module_path = prefix;
let mut source = module_path.pop().unwrap();
let mut type_ns_only = false;
self.r.visibilities.insert(self.r.local_def_id(id), vis);
- if id1 != ast::DUMMY_NODE_ID {
- self.r.visibilities.insert(self.r.local_def_id(id1), vis);
- }
- if id2 != ast::DUMMY_NODE_ID {
- self.r.visibilities.insert(self.r.local_def_id(id2), vis);
- }
if nested {
// Correctly handle `self`
type_ns_only,
nested,
id,
- additional_ids: (id1, id2),
};
self.add_import(module_path, kind, use_tree.span, item, root_span, item.id, vis);
let new_span = prefix[prefix.len() - 1].ident.span;
let tree = ast::UseTree {
prefix: ast::Path::from_ident(Ident::new(kw::SelfLower, new_span)),
- kind: ast::UseTreeKind::Simple(
- Some(Ident::new(kw::Underscore, new_span)),
- ast::DUMMY_NODE_ID,
- ast::DUMMY_NODE_ID,
- ),
+ kind: ast::UseTreeKind::Simple(Some(Ident::new(kw::Underscore, new_span))),
span: use_tree.span,
};
self.build_reduced_graph_for_use_tree(
} else if orig_name == Some(kw::SelfLower) {
Some(self.r.graph_root)
} else {
- self.r.crate_loader.process_extern_crate(item, &self.r.definitions, local_def_id).map(
- |crate_id| {
- self.r.extern_crate_map.insert(local_def_id, crate_id);
- self.r.expect_module(crate_id.as_def_id())
- },
- )
+ let crate_id = self.r.crate_loader().process_extern_crate(item, local_def_id);
+ crate_id.map(|crate_id| {
+ self.r.extern_crate_map.insert(local_def_id, crate_id);
+ self.r.expect_module(crate_id.as_def_id())
+ })
}
.map(|module| {
let used = self.process_macro_use_imports(item, module);
fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) {
self.create_def(id, DefPathData::Use, use_tree.span);
- match use_tree.kind {
- UseTreeKind::Simple(_, id1, id2) => {
- self.create_def(id1, DefPathData::Use, use_tree.prefix.span);
- self.create_def(id2, DefPathData::Use, use_tree.prefix.span);
- }
- UseTreeKind::Glob => (),
- UseTreeKind::Nested(..) => {}
- }
visit::walk_use_tree(self, use_tree, id);
}
if !candidates.is_empty() {
show_candidates(
&self.session,
- &self.source_span,
+ &self.untracked.source_span,
&mut err,
span,
&candidates,
}
show_candidates(
&self.session,
- &self.source_span,
+ &self.untracked.source_span,
&mut err,
Some(span),
&import_suggestions,
// otherwise cause duplicate suggestions.
continue;
}
- if let Some(crate_id) = self.crate_loader.maybe_process_path_extern(ident.name) {
+ let crate_id = self.crate_loader().maybe_process_path_extern(ident.name);
+ if let Some(crate_id) = crate_id {
let crate_root = self.expect_module(crate_id.as_def_id());
suggestions.extend(self.lookup_import_candidates_from_module(
lookup_ident,
self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);
show_candidates(
&self.session,
- &self.source_span,
+ &self.untracked.source_span,
err,
None,
&import_suggestions,
(format!("use of undeclared type `{}`", ident), suggestion)
} else {
- let suggestion = if ident.name == sym::alloc {
- Some((
+ let mut suggestion = None;
+ if ident.name == sym::alloc {
+ suggestion = Some((
vec![],
String::from("add `extern crate alloc` to use the `alloc` crate"),
Applicability::MaybeIncorrect,
))
- } else {
+ }
+
+ suggestion = suggestion.or_else(|| {
self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map(
|sugg| {
(
)
},
)
- };
+ });
(format!("use of undeclared crate or module `{}`", ident), suggestion)
}
}
-use crate::{ImportKind, NameBinding, NameBindingKind, Resolver, ResolverTree};
+use crate::{NameBinding, NameBindingKind, Resolver, ResolverTree};
use rustc_ast::ast;
use rustc_ast::visit;
use rustc_ast::visit::Visitor;
for (binding, eff_vis) in visitor.import_effective_visibilities.iter() {
let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
if let Some(node_id) = import.id() {
- let mut update = |node_id| {
- r.effective_visibilities.update_eff_vis(
- r.local_def_id(node_id),
- eff_vis,
- ResolverTree(&r.definitions, &r.crate_loader),
- )
- };
- update(node_id);
- if let ImportKind::Single { additional_ids: (id1, id2), .. } = import.kind {
- // In theory all the single import IDs have individual visibilities and
- // effective visibilities, but in practice these IDs go straight to HIR
- // where all their few uses assume that their (effective) visibility
- // applies to the whole syntactic `use` item. So they all get the same
- // value which is the maximum of all bindings. Maybe HIR for imports
- // shouldn't use three IDs at all.
- if id1 != ast::DUMMY_NODE_ID {
- update(id1);
- }
- if id2 != ast::DUMMY_NODE_ID {
- update(id2);
- }
- }
+ r.effective_visibilities.update_eff_vis(
+ r.local_def_id(node_id),
+ eff_vis,
+ ResolverTree(&r.untracked),
+ )
}
}
use crate::Determinacy::{self, *};
use crate::Namespace::*;
use crate::{module_to_string, names_to_string, ImportSuggestion};
-use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
+use crate::{
+ AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingKey, ModuleKind, ResolutionError,
+ Resolver, Segment,
+};
use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
use crate::{NameBinding, NameBindingKind, PathResult};
/// If this is the import for `foo::bar::a`, we would have the ID of the `UseTree`
/// for `a` in this field.
id: NodeId,
- /// Additional `NodeId`s allocated to a `ast::UseTree` for automatically generated `use` statement
- /// (eg. implicit struct constructors)
- additional_ids: (NodeId, NodeId),
},
Glob {
is_prelude: bool,
ref type_ns_only,
ref nested,
ref id,
- ref additional_ids,
// Ignore the following to avoid an infinite loop while printing.
source_bindings: _,
target_bindings: _,
.field("type_ns_only", type_ns_only)
.field("nested", nested)
.field("id", id)
- .field("additional_ids", additional_ids)
.finish_non_exhaustive(),
Glob { ref is_prelude, ref max_vis, ref id } => f
.debug_struct("Glob")
if let Some(candidate) = &err.candidate {
import_candidates(
self.r.session,
- &self.r.source_span,
+ &self.r.untracked.source_span,
&mut diag,
Some(err.span),
&candidate,
match binding {
Ok(binding) => {
// Consistency checks, analogous to `finalize_macro_resolutions`.
- let initial_res = source_bindings[ns].get().map(|initial_binding| {
+ let initial_binding = source_bindings[ns].get().map(|initial_binding| {
all_ns_err = false;
if let Some(target_binding) = target_bindings[ns].get() {
if target.name == kw::Underscore
);
}
}
- initial_binding.res()
+ initial_binding
});
let res = binding.res();
- if let Ok(initial_res) = initial_res {
+ if let Ok(initial_binding) = initial_binding {
+ let initial_res = initial_binding.res();
if res != initial_res && this.ambiguity_errors.is_empty() {
- span_bug!(import.span, "inconsistent resolution for an import");
+ this.ambiguity_errors.push(AmbiguityError {
+ kind: AmbiguityKind::Import,
+ ident,
+ b1: initial_binding,
+ b2: binding,
+ misc1: AmbiguityErrorMisc::None,
+ misc2: AmbiguityErrorMisc::None,
+ });
}
} else if res != Res::Err
&& this.ambiguity_errors.is_empty()
// We have a single lifetime => success.
elision_lifetime = Elision::Param(res)
} else {
- // We have have multiple lifetimes => error.
+ // We have multiple lifetimes => error.
elision_lifetime = Elision::Err;
}
}
if let GenericParamKind::Lifetime = param.kind {
// Record lifetime res, so lowering knows there is something fishy.
self.record_lifetime_param(param.id, LifetimeRes::Error);
- continue;
}
+ continue;
}
Entry::Vacant(entry) => {
entry.insert(param.ident.span);
// Before we start looking for candidates, we have to get our hands
// on the type user is trying to perform invocation on; basically:
// we're transforming `HashMap::new` into just `HashMap`.
- let path = match path.split_last() {
+ let prefix_path = match path.split_last() {
Some((_, path)) if !path.is_empty() => path,
_ => return Some(parent_err),
};
let (mut err, candidates) =
- this.smart_resolve_report_errors(path, path_span, PathSource::Type, None);
+ this.smart_resolve_report_errors(prefix_path, path_span, PathSource::Type, None);
// There are two different error messages user might receive at
// this point:
if this.should_report_errs() {
if candidates.is_empty() {
- // When there is no suggested imports, we can just emit the error
- // and suggestions immediately. Note that we bypass the usually error
- // reporting routine (ie via `self.r.report_error`) because we need
- // to post-process the `ResolutionError` above.
- err.emit();
+ if path.len() == 2 && prefix_path.len() == 1 {
+ // Delay to check whether methond name is an associated function or not
+ // ```
+ // let foo = Foo {};
+ // foo::bar(); // possibly suggest to foo.bar();
+ //```
+ err.stash(
+ prefix_path[0].ident.span,
+ rustc_errors::StashKey::CallAssocMethod,
+ );
+ } else {
+ // When there is no suggested imports, we can just emit the error
+ // and suggestions immediately. Note that we bypass the usually error
+ // reporting routine (ie via `self.r.report_error`) because we need
+ // to post-process the `ResolutionError` above.
+ err.emit();
+ }
} else {
// If there are suggested imports, the error reporting is delayed
this.r.use_injections.push(UseError {
def_id,
instead: false,
suggestion: None,
- path: path.into(),
+ path: prefix_path.into(),
is_call: source.is_call(),
});
}
if !module.no_implicit_prelude {
let extern_prelude = self.r.extern_prelude.clone();
names.extend(extern_prelude.iter().flat_map(|(ident, _)| {
- self.r.crate_loader.maybe_process_path_extern(ident.name).and_then(
- |crate_id| {
+ self.r
+ .crate_loader()
+ .maybe_process_path_extern(ident.name)
+ .and_then(|crate_id| {
let crate_mod =
Res::Def(DefKind::Mod, crate_id.as_def_id());
} else {
None
}
- },
- )
+ })
}));
if let Some(prelude) = self.r.prelude {
use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::intern::Interned;
-use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::sync::{Lrc, RwLock};
use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed};
use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind};
use rustc_hir::def::Namespace::*;
use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools};
use rustc_middle::ty::{ResolverGlobalCtxt, ResolverOutputs};
use rustc_query_system::ich::StableHashingContext;
-use rustc_session::cstore::{CrateStore, MetadataLoaderDyn};
+use rustc_session::cstore::{CrateStore, MetadataLoaderDyn, Untracked};
use rustc_session::lint::LintBuffer;
use rustc_session::Session;
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency};
pub struct Resolver<'a> {
session: &'a Session,
- definitions: Definitions,
/// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
expn_that_defined: FxHashMap<LocalDefId, ExpnId>,
- /// Reference span for definitions.
- source_span: IndexVec<LocalDefId, Span>,
graph_root: Module<'a>,
arenas: &'a ResolverArenas<'a>,
dummy_binding: &'a NameBinding<'a>,
- crate_loader: CrateLoader<'a>,
+ local_crate_name: Symbol,
+ metadata_loader: Box<MetadataLoaderDyn>,
+ untracked: Untracked,
+ used_extern_options: FxHashSet<Symbol>,
macro_names: FxHashSet<Ident>,
builtin_macros: FxHashMap<Symbol, BuiltinMacroState>,
/// A small map keeping true kinds of built-in macros that appear to be fn-like on
/// A minimal subset of resolver that can implemenent `DefIdTree`, sometimes
/// required to satisfy borrow checker by avoiding borrowing the whole resolver.
#[derive(Clone, Copy)]
-struct ResolverTree<'a, 'b>(&'a Definitions, &'a CrateLoader<'b>);
+struct ResolverTree<'a>(&'a Untracked);
-impl DefIdTree for ResolverTree<'_, '_> {
+impl DefIdTree for ResolverTree<'_> {
#[inline]
fn opt_parent(self, id: DefId) -> Option<DefId> {
- let ResolverTree(definitions, crate_loader) = self;
+ let ResolverTree(Untracked { definitions, cstore, .. }) = self;
match id.as_local() {
- Some(id) => definitions.def_key(id).parent,
- None => crate_loader.cstore().def_key(id).parent,
+ Some(id) => definitions.read().def_key(id).parent,
+ None => cstore.as_any().downcast_ref::<CStore>().unwrap().def_key(id).parent,
}
.map(|index| DefId { index, ..id })
}
impl<'a, 'b> DefIdTree for &'a Resolver<'b> {
#[inline]
fn opt_parent(self, id: DefId) -> Option<DefId> {
- ResolverTree(&self.definitions, &self.crate_loader).opt_parent(id)
+ ResolverTree(&self.untracked).opt_parent(id)
}
}
"adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}",
node_id,
data,
- self.definitions.def_key(self.node_id_to_def_id[&node_id]),
+ self.untracked.definitions.read().def_key(self.node_id_to_def_id[&node_id]),
);
- let def_id = self.definitions.create_def(parent, data);
+ let def_id = self.untracked.definitions.write().create_def(parent, data);
// Create the definition.
if expn_id != ExpnId::root() {
// A relative span's parent must be an absolute span.
debug_assert_eq!(span.data_untracked().parent, None);
- let _id = self.source_span.push(span);
+ let _id = self.untracked.source_span.push(span);
debug_assert_eq!(_id, def_id);
// Some things for which we allocate `LocalDefId`s don't correspond to
pub fn new(
session: &'a Session,
krate: &Crate,
- crate_name: &str,
+ crate_name: Symbol,
metadata_loader: Box<MetadataLoaderDyn>,
arenas: &'a ResolverArenas<'a>,
) -> Resolver<'a> {
let mut resolver = Resolver {
session,
- definitions,
expn_that_defined: Default::default(),
- source_span,
// The outermost module has def ID 0; this is not reflected in the
// AST.
vis: ty::Visibility::Public,
}),
- crate_loader: CrateLoader::new(session, metadata_loader, crate_name),
+ metadata_loader,
+ local_crate_name: crate_name,
+ used_extern_options: Default::default(),
+ untracked: Untracked {
+ cstore: Box::new(CStore::new(session)),
+ source_span,
+ definitions: RwLock::new(definitions),
+ },
macro_names: FxHashSet::default(),
builtin_macros: Default::default(),
builtin_macro_kinds: Default::default(),
pub fn into_outputs(self) -> ResolverOutputs {
let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect();
- let definitions = self.definitions;
- let cstore = Box::new(self.crate_loader.into_cstore());
- let source_span = self.source_span;
let expn_that_defined = self.expn_that_defined;
let visibilities = self.visibilities;
let has_pub_restricted = self.has_pub_restricted;
let main_def = self.main_def;
let confused_type_with_std_module = self.confused_type_with_std_module;
let effective_visibilities = self.effective_visibilities;
+ let untracked = self.untracked;
let global_ctxt = ResolverGlobalCtxt {
- cstore,
- source_span,
expn_that_defined,
visibilities,
has_pub_restricted,
builtin_macro_kinds: self.builtin_macro_kinds,
lifetime_elision_allowed: self.lifetime_elision_allowed,
};
- ResolverOutputs { definitions, global_ctxt, ast_lowering }
+ ResolverOutputs { global_ctxt, ast_lowering, untracked }
}
pub fn clone_outputs(&self) -> ResolverOutputs {
let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect();
- let definitions = self.definitions.clone();
+ let definitions = self.untracked.definitions.clone();
let cstore = Box::new(self.cstore().clone());
+ let untracked =
+ Untracked { cstore, source_span: self.untracked.source_span.clone(), definitions };
let global_ctxt = ResolverGlobalCtxt {
- cstore,
- source_span: self.source_span.clone(),
expn_that_defined: self.expn_that_defined.clone(),
visibilities: self.visibilities.clone(),
has_pub_restricted: self.has_pub_restricted,
builtin_macro_kinds: self.builtin_macro_kinds.clone(),
lifetime_elision_allowed: self.lifetime_elision_allowed.clone(),
};
- ResolverOutputs { definitions, global_ctxt, ast_lowering }
+ ResolverOutputs { global_ctxt, ast_lowering, untracked }
}
fn create_stable_hashing_context(&self) -> StableHashingContext<'_> {
- StableHashingContext::new(
- self.session,
- &self.definitions,
- self.crate_loader.cstore(),
- &self.source_span,
+ StableHashingContext::new(self.session, &self.untracked)
+ }
+
+ pub fn crate_loader(&mut self) -> CrateLoader<'_> {
+ CrateLoader::new(
+ &self.session,
+ &*self.metadata_loader,
+ self.local_crate_name,
+ &mut *self.untracked.cstore.untracked_as_any().downcast_mut().unwrap(),
+ self.untracked.definitions.read(),
+ &mut self.used_extern_options,
)
}
pub fn cstore(&self) -> &CStore {
- self.crate_loader.cstore()
+ self.untracked.cstore.as_any().downcast_ref().unwrap()
}
fn dummy_ext(&self, macro_kind: MacroKind) -> Lrc<SyntaxExtension> {
self.session.time("resolve_main", || self.resolve_main());
self.session.time("resolve_check_unused", || self.check_unused(krate));
self.session.time("resolve_report_errors", || self.report_errors(krate));
- self.session.time("resolve_postprocess", || self.crate_loader.postprocess(krate));
+ self.session.time("resolve_postprocess", || self.crate_loader().postprocess(krate));
});
}
} else {
let crate_id = if finalize {
let Some(crate_id) =
- self.crate_loader.process_path_extern(ident.name, ident.span) else { return Some(self.dummy_binding); };
+ self.crate_loader().process_path_extern(ident.name, ident.span) else { return Some(self.dummy_binding); };
crate_id
} else {
- self.crate_loader.maybe_process_path_extern(ident.name)?
+ self.crate_loader().maybe_process_path_extern(ident.name)?
};
let crate_root = self.expect_module(crate_id.as_def_id());
let vis = ty::Visibility::<LocalDefId>::Public;
/// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
#[inline]
pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
- def_id.as_local().map(|def_id| self.source_span[def_id])
+ def_id.as_local().map(|def_id| self.untracked.source_span[def_id])
}
/// Retrieves the name of the given `DefId`.
#[inline]
pub fn opt_name(&self, def_id: DefId) -> Option<Symbol> {
let def_key = match def_id.as_local() {
- Some(def_id) => self.definitions.def_key(def_id),
+ Some(def_id) => self.untracked.definitions.read().def_key(def_id),
None => self.cstore().def_key(def_id),
};
def_key.get_opt_name()
}
fn get_proc_macro_quoted_span(&self, krate: CrateNum, id: usize) -> Span {
- self.crate_loader.cstore().get_proc_macro_quoted_span_untracked(krate, id, self.session)
+ self.cstore().get_proc_macro_quoted_span_untracked(krate, id, self.session)
}
fn declare_proc_macro(&mut self, id: NodeId) {
self.save_ctxt.lookup_def_id(ref_id)
}
- pub fn dump_crate_info(&mut self, name: &str) {
+ pub fn dump_crate_info(&mut self, name: Symbol) {
let source_file = self.tcx.sess.local_crate_source_file.as_ref();
let crate_root = source_file.map(|source_file| {
let source_file = Path::new(source_file);
let data = CratePreludeData {
crate_id: GlobalCrateId {
- name: name.into(),
+ name: name.to_string(),
disambiguator: (self.tcx.sess.local_stable_crate_id().to_u64(), 0),
},
crate_root: crate_root.unwrap_or_else(|| "<no source>".to_owned()),
self.dumper.crate_prelude(data);
}
- pub fn dump_compilation_options(&mut self, input: &Input, crate_name: &str) {
+ pub fn dump_compilation_options(&mut self, input: &Input, crate_name: Symbol) {
// Apply possible `remap-path-prefix` remapping to the input source file
// (and don't include remapping args anymore)
let (program, arguments) = {
}
}
- fn write_sub_paths(&mut self, path: &'tcx hir::Path<'tcx>) {
+ fn write_sub_paths<R>(&mut self, path: &'tcx hir::Path<'tcx, R>) {
self.write_segments(path.segments)
}
// As write_sub_paths, but does not process the last ident in the path (assuming it
// will be processed elsewhere). See note on write_sub_paths about global.
- fn write_sub_paths_truncated(&mut self, path: &'tcx hir::Path<'tcx>) {
+ fn write_sub_paths_truncated<R>(&mut self, path: &'tcx hir::Path<'tcx, R>) {
if let [segments @ .., _] = path.segments {
self.write_segments(segments)
}
use rustc_span::*;
use std::cell::Cell;
-use std::default::Default;
use std::env;
use std::fs::File;
use std::io::BufWriter;
}
/// Returns path to the compilation output (e.g., libfoo-12345678.rmeta)
- pub fn compilation_output(&self, crate_name: &str) -> PathBuf {
+ pub fn compilation_output(&self, crate_name: Symbol) -> PathBuf {
let sess = &self.tcx.sess;
// Save-analysis is emitted per whole session, not per each crate type
let crate_type = sess.crate_types()[0];
match self.tcx.hir().get(hir_id) {
Node::TraitRef(tr) => tr.path.res,
- Node::Item(&hir::Item { kind: hir::ItemKind::Use(path, _), .. }) => path.res,
+ Node::Item(&hir::Item { kind: hir::ItemKind::Use(path, _), .. }) => {
+ path.res.get(0).copied().unwrap_or(Res::Err)
+ }
Node::PathSegment(seg) => {
if seg.res != Res::Err {
seg.res
}
impl<'a> DumpHandler<'a> {
- pub fn new(odir: Option<&'a Path>, cratename: &str) -> DumpHandler<'a> {
- DumpHandler { odir, cratename: cratename.to_owned() }
+ pub fn new(odir: Option<&'a Path>, cratename: Symbol) -> DumpHandler<'a> {
+ DumpHandler { odir, cratename: cratename.to_string() }
}
fn output_file(&self, ctx: &SaveContext<'_>) -> (BufWriter<File>, PathBuf) {
pub fn process_crate<'l, 'tcx, H: SaveHandler>(
tcx: TyCtxt<'tcx>,
- cratename: &str,
+ cratename: Symbol,
input: &'l Input,
config: Option<Config>,
mut handler: H,
use crate::leb128::{self, largest_max_leb128_len};
use crate::serialize::{Decodable, Decoder, Encodable, Encoder};
-use std::convert::TryInto;
use std::fs::File;
use std::io::{self, Write};
use std::mem::MaybeUninit;
rustc_target = { path = "../rustc_target" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_index = { path = "../rustc_index" }
rustc_span = { path = "../rustc_span" }
rustc_fs_util = { path = "../rustc_fs_util" }
rustc_ast = { path = "../rustc_ast" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
smallvec = "1.8.1"
+termize = "0.1.1"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
let at_least = if at_least { 1 } else { 0 };
IncorrectCguReuseType {
span: error_span.0,
- cgu_user_name: &cgu_user_name,
+ cgu_user_name,
actual_reuse,
expected_reuse,
at_least,
Min,
}
-#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct FieldInfo {
pub name: Symbol,
pub offset: u64,
Union,
Enum,
Closure,
+ Generator,
}
#[derive(PartialEq, Eq, Hash, Debug)]
let struct_like = match kind {
DataTypeKind::Struct | DataTypeKind::Closure => true,
- DataTypeKind::Enum | DataTypeKind::Union => false,
+ DataTypeKind::Enum | DataTypeKind::Union | DataTypeKind::Generator => false,
};
for (i, variant_info) in variants.into_iter().enumerate() {
let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::stable_hasher::ToStableHashKey;
+use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
use rustc_target::abi::Align;
use rustc_target::spec::{PanicStrategy, SanitizerSet, SplitDebuginfo};
use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS};
use std::collections::{BTreeMap, BTreeSet};
use std::fmt;
use std::hash::Hash;
-use std::iter::{self, FromIterator};
+use std::iter;
use std::path::{Path, PathBuf};
use std::str::{self, FromStr};
DepInfo,
}
+// Safety: Trivial C-Style enums have a stable sort order across compilation sessions.
+unsafe impl StableOrd for OutputType {}
+
impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {
type KeyType = Self;
/// should be placed on disk.
pub fn output_path(&self, flavor: OutputType) -> PathBuf {
let extension = flavor.extension();
- self.with_directory_and_extension(&self.out_directory, &extension)
+ self.with_directory_and_extension(&self.out_directory, extension)
}
/// Gets the path where a compilation artifact of the given type for the
let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory);
- self.with_directory_and_extension(&temps_directory, &extension)
+ self.with_directory_and_extension(temps_directory, &extension)
}
pub fn with_extension(&self, extension: &str) -> PathBuf {
values_target_family
.extend(target.options.families.iter().map(|family| Symbol::intern(family)));
values_target_arch.insert(Symbol::intern(&target.arch));
- values_target_endian.insert(Symbol::intern(&target.options.endian.as_str()));
+ values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
values_target_env.insert(Symbol::intern(&target.options.env));
values_target_abi.insert(Symbol::intern(&target.options.abi));
values_target_vendor.insert(Symbol::intern(&target.options.vendor));
match matches.opt_str("target") {
Some(target) if target.ends_with(".json") => {
let path = Path::new(&target);
- TargetTriple::from_path(&path).unwrap_or_else(|_| {
+ TargetTriple::from_path(path).unwrap_or_else(|_| {
early_error(error_format, &format!("target file {path:?} does not exist"))
})
}
) -> (NativeLibKind, Option<bool>) {
let mut verbatim = None;
for modifier in modifiers.split(',') {
- let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
+ let (modifier, value) = match modifier.strip_prefix(['+', '-']) {
Some(m) => (m, modifier.starts_with('+')),
None => early_error(
error_format,
let mut search_paths = vec![];
for s in &matches.opt_strs("L") {
- search_paths.push(SearchPath::from_cli_opt(&s, error_format));
+ search_paths.push(SearchPath::from_cli_opt(s, error_format));
}
let libs = parse_libs(matches, error_format);
use crate::utils::NativeLibKind;
use crate::Session;
use rustc_ast as ast;
-use rustc_data_structures::sync::{self, MetadataRef};
-use rustc_hir::def_id::{CrateNum, DefId, StableCrateId, LOCAL_CRATE};
-use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
+use rustc_data_structures::sync::{self, MetadataRef, RwLock};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, StableCrateId, LOCAL_CRATE};
+use rustc_hir::definitions::{DefKey, DefPath, DefPathHash, Definitions};
+use rustc_index::vec::IndexVec;
use rustc_span::hygiene::{ExpnHash, ExpnId};
use rustc_span::symbol::Symbol;
use rustc_span::Span;
/// during resolve)
pub trait CrateStore: std::fmt::Debug {
fn as_any(&self) -> &dyn Any;
+ fn untracked_as_any(&mut self) -> &mut dyn Any;
// Foreign definitions.
// This information is safe to access, since it's hashed as part of the DefPathHash, which incr.
}
pub type CrateStoreDyn = dyn CrateStore + sync::Sync;
+
+#[derive(Debug)]
+pub struct Untracked {
+ pub cstore: Box<CrateStoreDyn>,
+ /// Reference span for definitions.
+ pub source_span: IndexVec<LocalDefId, Span>,
+ pub definitions: RwLock<Definitions>,
+}
#[derive(Diagnostic)]
#[diag(session_crate_name_does_not_match)]
-pub struct CrateNameDoesNotMatch<'a> {
+pub struct CrateNameDoesNotMatch {
#[primary_span]
pub span: Span,
- pub s: &'a str,
+ pub s: Symbol,
pub name: Symbol,
}
#[derive(Diagnostic)]
#[diag(session_invalid_character_in_create_name)]
-pub struct InvalidCharacterInCrateName<'a> {
+pub struct InvalidCharacterInCrateName {
#[primary_span]
pub span: Option<Span>,
pub character: char,
- pub crate_name: &'a str,
+ pub crate_name: Symbol,
}
#[derive(Subdiagnostic)]
LitError::InvalidIntSuffix => {
let suf = suffix.expect("suffix error with no suffix");
let suf = suf.as_str();
- if looks_like_width_suffix(&['i', 'u'], &suf) {
+ if looks_like_width_suffix(&['i', 'u'], suf) {
// If it looks like a width, try to be helpful.
sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() });
} else if let Some(fixed) = fix_base_capitalisation(suf) {
use smallvec::{smallvec, SmallVec};
use std::env;
use std::fs;
-use std::iter::FromIterator;
use std::path::{Path, PathBuf};
use crate::search_paths::{PathKind, SearchPath};
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
- pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
+ pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
pub const parse_cfguard: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
*slot |= match s {
"address" => SanitizerSet::ADDRESS,
"cfi" => SanitizerSet::CFI,
+ "kcfi" => SanitizerSet::KCFI,
"leak" => SanitizerSet::LEAK,
"memory" => SanitizerSet::MEMORY,
"memtag" => SanitizerSet::MEMTAG,
"list the symbols defined by a library crate (default: no)"),
macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
"show macro backtraces (default: no)"),
+ maximal_hir_to_mir_coverage: bool = (false, parse_bool, [TRACKED],
+ "save as much information as possible about the correspondence between MIR and HIR \
+ as source scopes (default: no)"),
merge_functions: Option<MergeFunctions> = (None, parse_merge_functions, [TRACKED],
"control the operation of the MergeFunctions LLVM pass, taking \
the same values as the target option of the same name"),
"run all passes except codegen; no output"),
no_generate_arange_section: bool = (false, parse_no_flag, [TRACKED],
"omit DWARF address ranges that give faster lookups"),
- no_interleave_lints: bool = (false, parse_no_flag, [UNTRACKED],
- "execute lints separately; allows benchmarking individual lints"),
no_leak_check: bool = (false, parse_no_flag, [UNTRACKED],
"disable the 'leak check' for subtyping; unsound, but useful for tests"),
no_link: bool = (false, parse_no_flag, [TRACKED],
use crate::Session;
use rustc_ast as ast;
use rustc_span::symbol::sym;
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
use std::path::{Path, PathBuf};
pub fn out_filename(
sess: &Session,
crate_type: CrateType,
outputs: &OutputFilenames,
- crate_name: &str,
+ crate_name: Symbol,
) -> PathBuf {
let default_filename = filename_for_input(sess, crate_type, crate_name, outputs);
let out_filename = outputs
}
}
-pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input) -> String {
- let validate = |s: String, span: Option<Span>| {
- validate_crate_name(sess, &s, span);
+pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input) -> Symbol {
+ let validate = |s: Symbol, span: Option<Span>| {
+ validate_crate_name(sess, s, span);
s
};
sess.find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s)));
if let Some(ref s) = sess.opts.crate_name {
+ let s = Symbol::intern(s);
if let Some((attr, name)) = attr_crate_name {
- if name.as_str() != s {
+ if name != s {
sess.emit_err(CrateNameDoesNotMatch { span: attr.span, s, name });
}
}
- return validate(s.clone(), None);
+ return validate(s, None);
}
if let Some((attr, s)) = attr_crate_name {
- return validate(s.to_string(), Some(attr.span));
+ return validate(s, Some(attr.span));
}
if let Input::File(ref path) = *input {
if let Some(s) = path.file_stem().and_then(|s| s.to_str()) {
if s.starts_with('-') {
sess.emit_err(CrateNameInvalid { s });
} else {
- return validate(s.replace('-', "_"), None);
+ return validate(Symbol::intern(&s.replace('-', "_")), None);
}
}
}
- "rust_out".to_string()
+ Symbol::intern("rust_out")
}
-pub fn validate_crate_name(sess: &Session, s: &str, sp: Option<Span>) {
+pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) {
let mut err_count = 0;
{
if s.is_empty() {
err_count += 1;
sess.emit_err(CrateNameEmpty { span: sp });
}
- for c in s.chars() {
+ for c in s.as_str().chars() {
if c.is_alphanumeric() {
continue;
}
pub fn filename_for_metadata(
sess: &Session,
- crate_name: &str,
+ crate_name: Symbol,
outputs: &OutputFilenames,
) -> PathBuf {
// If the command-line specified the path, use that directly.
pub fn filename_for_input(
sess: &Session,
crate_type: CrateType,
- crate_name: &str,
+ crate_name: Symbol,
outputs: &OutputFilenames,
) -> PathBuf {
let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
}
+ pub fn is_sanitizer_kcfi_enabled(&self) -> bool {
+ self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI)
+ }
+
/// Check whether this compile session and crate type use static crt.
pub fn crt_static(&self, crate_type: Option<CrateType>) -> bool {
if !self.target.crt_static_respected {
) -> Option<Symbol> {
attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str())
}
+
+ pub fn diagnostic_width(&self) -> usize {
+ let default_column_width = 140;
+ if let Some(width) = self.opts.diagnostic_width {
+ width
+ } else if self.opts.unstable_opts.ui_testing {
+ default_column_width
+ } else {
+ termize::dimensions().map_or(default_column_width, |(w, _)| w)
+ }
+ }
}
// JUSTIFICATION: defn of the suggested wrapper fns
}
}
+ // LLVM CFI and KCFI are mutually exclusive
+ if sess.is_sanitizer_cfi_enabled() && sess.is_sanitizer_kcfi_enabled() {
+ sess.emit_err(CannotMixAndMatchSanitizers {
+ first: "cfi".to_string(),
+ second: "kcfi".to_string(),
+ });
+ }
+
if sess.opts.unstable_opts.stack_protector != StackProtector::None {
if !sess.target.options.supports_stack_protector {
sess.emit_warning(StackProtectorNotSupportedForTarget {
// The slow path:
// This is either ASCII control character "DEL" or the beginning of
// a multibyte char. Just decode to `char`.
- let c = (&src[i..]).chars().next().unwrap();
+ let c = src[i..].chars().next().unwrap();
char_len = c.len_utf8();
let pos = BytePos::from_usize(i) + output_offset;
Some(new_file_and_idx)
} else {
let file = &self.line_cache[oldest].file;
- if !file_contains(&file, span_data.hi) {
+ if !file_contains(file, span_data.hi) {
return None;
}
-use crate::HashStableContext;
+use crate::{HashStableContext, Symbol};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use rustc_data_structures::AtomicRef;
/// Computes the stable ID for a crate with the given name and
/// `-Cmetadata` arguments.
- pub fn new(crate_name: &str, is_exe: bool, mut metadata: Vec<String>) -> StableCrateId {
+ pub fn new(crate_name: Symbol, is_exe: bool, mut metadata: Vec<String>) -> StableCrateId {
let mut hasher = StableHasher::new();
- crate_name.hash(&mut hasher);
+ // We must hash the string text of the crate name, not the id, as the id is not stable
+ // across builds.
+ crate_name.as_str().hash(&mut hasher);
// We don't want the stable crate ID to depend on the order of
// -C metadata arguments, so sort them:
impl PartialOrd for DefId {
#[inline]
fn partial_cmp(&self, other: &DefId) -> Option<std::cmp::Ordering> {
- Some(Ord::cmp(self, other))
+ Some(self.cmp(other))
}
}
}
pub fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
- with_session_globals(|session_globals| f(&mut *session_globals.hygiene_data.borrow_mut()))
+ with_session_globals(|session_globals| f(&mut session_globals.hygiene_data.borrow_mut()))
}
#[inline]
pub fn remapped_path_if_available(&self) -> &Path {
match self {
RealFileName::LocalPath(p)
- | RealFileName::Remapped { local_path: _, virtual_name: p } => &p,
+ | RealFileName::Remapped { local_path: _, virtual_name: p } => p,
}
}
pub fn is_dummy(self) -> bool {
self.lo.0 == 0 && self.hi.0 == 0
}
+ #[inline]
+ pub fn is_visible(self, sm: &SourceMap) -> bool {
+ !self.is_dummy() && sm.is_span_accessible(self.span())
+ }
/// Returns `true` if `self` fully encloses `other`.
pub fn contains(self, other: Self) -> bool {
self.lo <= other.lo && other.hi <= self.hi
self.data_untracked().is_dummy()
}
+ #[inline]
+ pub fn is_visible(self, sm: &SourceMap) -> bool {
+ self.data_untracked().is_visible(sm)
+ }
+
/// Returns `true` if this span comes from any kind of macro, desugaring or inlining.
#[inline]
pub fn from_expansion(self) -> bool {
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::StableHasher;
use rustc_data_structures::sync::{AtomicU32, Lrc, MappedReadGuard, ReadGuard, RwLock};
+use std::cmp;
use std::hash::Hash;
use std::path::{Path, PathBuf};
use std::sync::atomic::Ordering;
-use std::{clone::Clone, cmp};
-use std::{convert::TryFrom, unreachable};
use std::fs;
use std::io;
// If an interner exists, return it. Otherwise, prepare a fresh one.
#[inline]
fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T {
- crate::with_session_globals(|session_globals| f(&mut *session_globals.span_interner.lock()))
+ crate::with_session_globals(|session_globals| f(&mut session_globals.span_interner.lock()))
}
use rustc_macros::HashStable_Generic;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
-use std::cmp::{Ord, PartialEq, PartialOrd};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::str;
Capture,
Center,
Clone,
+ Context,
Continue,
Copy,
Count,
Relaxed,
Release,
Result,
- ResumeTy,
Return,
Right,
Rust,
generic_associated_types_extended,
generic_const_exprs,
generic_param_attrs,
- get_context,
global_allocator,
global_asm,
globs,
item_like_imports,
iter,
iter_repeat,
+ kcfi,
keyword,
kind,
kreg,
ty,
type_alias_enum_variants,
type_alias_impl_trait,
+ type_ascribe,
type_ascription,
type_changing_struct_update,
type_id,
}
}
-/// This is the most general way to print identifiers.
+/// The most general type to print identifiers.
+///
/// AST pretty-printer is used as a fallback for turning AST structures into token streams for
/// proc macros. Additionally, proc macros may stringify their input and expect it survive the
/// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
impl<D: Decoder> Decodable<D> for Symbol {
#[inline]
default fn decode(d: &mut D) -> Symbol {
- Symbol::intern(&d.read_str())
+ Symbol::intern(d.read_str())
}
}
/// For example `sym::rustfmt` or `sym::u8`.
pub mod sym {
use super::Symbol;
- use std::convert::TryInto;
#[doc(inline)]
pub use super::sym_generated::*;
tracing = "0.1"
punycode = "0.4.0"
rustc-demangle = "0.1.21"
+twox-hash = "1.6.3"
rustc_span = { path = "../rustc_span" }
rustc_middle = { path = "../rustc_middle" }
use rustc_middle::ty::{FnSig, Ty, TyCtxt};
use rustc_target::abi::call::FnAbi;
+use std::hash::Hasher;
+use twox_hash::XxHash64;
mod typeid_itanium_cxx_abi;
use typeid_itanium_cxx_abi::TypeIdOptions;
pub fn typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> String {
typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS)
}
+
+/// Returns an LLVM KCFI type metadata identifier for the specified FnAbi.
+pub fn kcfi_typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> u32 {
+ // An LLVM KCFI type metadata identifier is a 32-bit constant produced by taking the lower half
+ // of the xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
+ let mut hash: XxHash64 = Default::default();
+ hash.write(
+ typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, TypeIdOptions::NO_OPTIONS).as_bytes(),
+ );
+ hash.finish() as u32
+}
+
+/// Returns an LLVM KCFI type metadata identifier for the specified FnSig.
+pub fn kcfi_typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> u32 {
+ // An LLVM KCFI type metadata identifier is a 32-bit constant produced by taking the lower half
+ // of the xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
+ let mut hash: XxHash64 = Default::default();
+ hash.write(
+ typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS).as_bytes(),
+ );
+ hash.finish() as u32
+}
where
C: HasDataLayout,
{
- data = arg_scalar(cx, &scalar1, offset, data);
+ data = arg_scalar(cx, scalar1, offset, data);
match (scalar1.primitive(), scalar2.primitive()) {
(abi::F32, _) => offset += Reg::f32().size,
(_, abi::F64) => offset += Reg::f64().size,
if (offset.bytes() % 4) != 0 && scalar2.primitive().is_float() {
offset += Size::from_bytes(4 - (offset.bytes() % 4));
}
- data = arg_scalar(cx, &scalar2, offset, data);
+ data = arg_scalar(cx, scalar2, offset, data);
return data;
}
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
-use std::iter::FromIterator;
use std::path::{Path, PathBuf};
#[macro_use]
-use super::apple_base::{macos_link_env_remove, macos_llvm_target, opts, Arch};
+use super::apple_base::{macos_llvm_target, opts, Arch};
use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
pub fn target() -> Target {
// FIXME: The leak sanitizer currently fails the tests, see #88132.
base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD;
- base.link_env_remove.to_mut().extend(macos_link_env_remove());
-
Target {
// Clang automatically chooses a more specific target based on
// MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
//
// For example, `-C target-cpu=cortex-a53`.
-use super::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions};
+use super::{
+ Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, Target, TargetOptions,
+};
pub fn target() -> Target {
let opts = TargetOptions {
linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
linker: Some("rust-lld".into()),
features: "+strict-align,+neon,+fp-armv8".into(),
+ supported_sanitizers: SanitizerSet::KCFI,
relocation_model: RelocModel::Static,
disable_redzone: true,
max_atomic_width: Some(128),
use crate::spec::{
- aarch64_apple_ios_sim, aarch64_apple_watchos_sim, x86_64_apple_ios, x86_64_apple_tvos,
- x86_64_apple_watchos_sim,
+ aarch64_apple_darwin, aarch64_apple_ios_sim, aarch64_apple_watchos_sim, i686_apple_darwin,
+ x86_64_apple_darwin, x86_64_apple_ios, x86_64_apple_tvos, x86_64_apple_watchos_sim,
};
#[test]
assert_eq!(target.abi, "sim")
}
}
+
+#[test]
+fn macos_link_environment_unmodified() {
+ let all_macos_targets = [
+ aarch64_apple_darwin::target(),
+ i686_apple_darwin::target(),
+ x86_64_apple_darwin::target(),
+ ];
+
+ for target in all_macos_targets {
+ // macOS targets should only remove information for cross-compiling, but never
+ // for the host.
+ assert_eq!(target.link_env_remove, crate::spec::cvs!["IPHONEOS_DEPLOYMENT_TARGET"]);
+ }
+}
Arm64_sim => "apple-a12",
}
}
-
- fn link_env_remove(self) -> StaticCow<[StaticCow<str>]> {
- match self {
- Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim
- | Arm64_sim => {
- cvs!["MACOSX_DEPLOYMENT_TARGET"]
- }
- X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"],
- }
- }
}
fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs {
abi: abi.into(),
os: os.into(),
cpu: arch.target_cpu().into(),
- link_env_remove: arch.link_env_remove(),
+ link_env_remove: link_env_remove(arch, os),
vendor: "apple".into(),
linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
// macOS has -dead_strip, which doesn't rely on function_sections
format!("{}-apple-macosx{}.{}.0", arch.target_name(), major, minor)
}
-pub fn macos_link_env_remove() -> Vec<StaticCow<str>> {
- let mut env_remove = Vec::with_capacity(2);
- // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
- // may occur when we're linking a custom build script while targeting iOS for example.
- if let Ok(sdkroot) = env::var("SDKROOT") {
- if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("iPhoneSimulator.platform") {
- env_remove.push("SDKROOT".into())
+fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]> {
+ // Apple platforms only officially support macOS as a host for any compilation.
+ //
+ // If building for macOS, we go ahead and remove any erroneous environment state
+ // that's only applicable to cross-OS compilation. Always leave anything for the
+ // host OS alone though.
+ if os == "macos" {
+ let mut env_remove = Vec::with_capacity(2);
+ // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
+ // may occur when we're linking a custom build script while targeting iOS for example.
+ if let Ok(sdkroot) = env::var("SDKROOT") {
+ if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("iPhoneSimulator.platform")
+ {
+ env_remove.push("SDKROOT".into())
+ }
+ }
+ // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
+ // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
+ // although this is apparently ignored when using the linker at "/usr/bin/ld".
+ env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
+ env_remove.into()
+ } else {
+ // Otherwise if cross-compiling for a different OS/SDK, remove any part
+ // of the linking environment that's wrong and reversed.
+ match arch {
+ Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim
+ | Arm64_sim => {
+ cvs!["MACOSX_DEPLOYMENT_TARGET"]
+ }
+ X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"],
}
}
- // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
- // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
- // although this is apparently ignored when using the linker at "/usr/bin/ld".
- env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
- env_remove
}
fn ios_deployment_target() -> (u32, u32) {
-use super::apple_base::{macos_link_env_remove, macos_llvm_target, opts, Arch};
+use super::apple_base::{macos_llvm_target, opts, Arch};
use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions};
pub fn target() -> Target {
let mut base = opts("macos", arch);
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-m32"]);
- base.link_env_remove.to_mut().extend(macos_link_env_remove());
base.stack_probes = StackProbeType::X86;
base.frame_pointer = FramePointer::Always;
use serde_json::Value;
use std::borrow::Cow;
use std::collections::BTreeMap;
-use std::convert::TryFrom;
use std::hash::{Hash, Hasher};
-use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::str::FromStr;
bitflags::bitflags! {
#[derive(Default, Encodable, Decodable)]
- pub struct SanitizerSet: u8 {
+ pub struct SanitizerSet: u16 {
const ADDRESS = 1 << 0;
const LEAK = 1 << 1;
const MEMORY = 1 << 2;
const CFI = 1 << 5;
const MEMTAG = 1 << 6;
const SHADOWCALLSTACK = 1 << 7;
+ const KCFI = 1 << 8;
}
}
Some(match self {
SanitizerSet::ADDRESS => "address",
SanitizerSet::CFI => "cfi",
+ SanitizerSet::KCFI => "kcfi",
SanitizerSet::LEAK => "leak",
SanitizerSet::MEMORY => "memory",
SanitizerSet::MEMTAG => "memtag",
[
SanitizerSet::ADDRESS,
SanitizerSet::CFI,
+ SanitizerSet::KCFI,
SanitizerSet::LEAK,
SanitizerSet::MEMORY,
SanitizerSet::MEMTAG,
base.$key_name |= match s.as_str() {
Some("address") => SanitizerSet::ADDRESS,
Some("cfi") => SanitizerSet::CFI,
+ Some("kcfi") => SanitizerSet::KCFI,
Some("leak") => SanitizerSet::LEAK,
Some("memory") => SanitizerSet::MEMORY,
Some("memtag") => SanitizerSet::MEMTAG,
// Additionally look in the sysroot under `lib/rustlib/<triple>/target.json`
// as a fallback.
- let rustlib_path = crate::target_rustlib_path(&sysroot, &target_triple);
+ let rustlib_path = crate::target_rustlib_path(sysroot, target_triple);
let p = PathBuf::from_iter([
Path::new(sysroot),
Path::new(&rustlib_path),
// For now this target just never has an entry symbol no matter the output
// type, so unconditionally pass this.
"--no-entry",
- // Rust really needs a way for users to specify exports and imports in
- // the source code. --export-dynamic isn't the right tool for this job,
- // however it does have the side effect of automatically exporting a lot
- // of symbols, which approximates what people want when compiling for
- // wasm32-unknown-unknown expect, so use it for now.
- "--export-dynamic",
],
);
options.add_pre_link_args(
// otherwise
"--target=wasm32-unknown-unknown",
"-Wl,--no-entry",
- "-Wl,--export-dynamic",
],
);
// `args::args()` makes the WASI API calls itself.
options.main_needs_argc_argv = false;
+ // And, WASI mangles the name of "main" to distinguish between different
+ // signatures.
+ options.entry_name = "__main_void".into();
+
Target {
llvm_target: "wasm32-wasi".into(),
pointer_width: 32,
-use super::apple_base::{macos_link_env_remove, macos_llvm_target, opts, Arch};
+use super::apple_base::{macos_llvm_target, opts, Arch};
use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet};
use crate::spec::{StackProbeType, Target, TargetOptions};
base.max_atomic_width = Some(128); // core2 supports cmpxchg16b
base.frame_pointer = FramePointer::Always;
base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-m64"]);
- base.link_env_remove.to_mut().extend(macos_link_env_remove());
base.stack_probes = StackProbeType::X86;
base.supported_sanitizers =
SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::THREAD;
// features.
use super::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy};
-use super::{RelroLevel, StackProbeType, Target, TargetOptions};
+use super::{RelroLevel, SanitizerSet, StackProbeType, Target, TargetOptions};
pub fn target() -> Target {
let opts = TargetOptions {
features:
"-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
.into(),
+ supported_sanitizers: SanitizerSet::KCFI,
disable_redzone: true,
panic_strategy: PanicStrategy::Abort,
code_model: Some(CodeModel::Kernel),
+++ /dev/null
-// This file contains various trait resolution methods used by codegen.
-// They all assume regions can be erased and monomorphic types. It
-// seems likely that they should eventually be merged into more
-// general routines.
-
-use crate::infer::{DefiningAnchor, TyCtxtInferExt};
-use crate::traits::error_reporting::TypeErrCtxtExt;
-use crate::traits::{
- ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, TraitEngineExt,
- Unimplemented,
-};
-use rustc_infer::traits::FulfillmentErrorCode;
-use rustc_middle::traits::CodegenObligationError;
-use rustc_middle::ty::{self, TyCtxt};
-
-/// Attempts to resolve an obligation to an `ImplSource`. The result is
-/// a shallow `ImplSource` resolution, meaning that we do not
-/// (necessarily) resolve all nested obligations on the impl. Note
-/// that type check should guarantee to us that all nested
-/// obligations *could be* resolved if we wanted to.
-///
-/// This also expects that `trait_ref` is fully normalized.
-pub fn codegen_select_candidate<'tcx>(
- tcx: TyCtxt<'tcx>,
- (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
-) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
- // We expect the input to be fully normalized.
- debug_assert_eq!(trait_ref, tcx.normalize_erasing_regions(param_env, trait_ref));
-
- // Do the initial selection for the obligation. This yields the
- // shallow result we are looking for -- that is, what specific impl.
- let infcx = tcx
- .infer_ctxt()
- .ignoring_regions()
- .with_opaque_type_inference(DefiningAnchor::Bubble)
- .build();
- //~^ HACK `Bubble` is required for
- // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs
- let mut selcx = SelectionContext::new(&infcx);
-
- let obligation_cause = ObligationCause::dummy();
- let obligation = Obligation::new(tcx, obligation_cause, param_env, trait_ref);
-
- let selection = match selcx.select(&obligation) {
- Ok(Some(selection)) => selection,
- Ok(None) => return Err(CodegenObligationError::Ambiguity),
- Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented),
- Err(e) => {
- bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
- }
- };
-
- debug!(?selection);
-
- // Currently, we use a fulfillment context to completely resolve
- // all nested obligations. This is because they can inform the
- // inference of the impl's type parameters.
- let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx);
- let impl_source = selection.map(|predicate| {
- fulfill_cx.register_predicate_obligation(&infcx, predicate);
- });
-
- // In principle, we only need to do this so long as `impl_source`
- // contains unbound type parameters. It could be a slight
- // optimization to stop iterating early.
- let errors = fulfill_cx.select_all_or_error(&infcx);
- if !errors.is_empty() {
- // `rustc_monomorphize::collector` assumes there are no type errors.
- // Cycle errors are the only post-monomorphization errors possible; emit them now so
- // `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization.
- for err in errors {
- if let FulfillmentErrorCode::CodeCycle(cycle) = err.code {
- infcx.err_ctxt().report_overflow_error_cycle(&cycle);
- }
- }
- return Err(CodegenObligationError::FulfillmentError);
- }
-
- let impl_source = infcx.resolve_vars_if_possible(impl_source);
- let impl_source = infcx.tcx.erase_regions(impl_source);
-
- // Opaque types may have gotten their hidden types constrained, but we can ignore them safely
- // as they will get constrained elsewhere, too.
- // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass
- let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
-
- Ok(&*tcx.arena.alloc(impl_source))
-}
use crate::traits::util::impl_subject_and_oblig;
use crate::traits::SkipLeakCheck;
use crate::traits::{
- self, Normalized, Obligation, ObligationCause, ObligationCtxt, PredicateObligation,
- PredicateObligations, SelectionContext,
+ self, Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations,
+ SelectionContext,
};
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::Diagnostic;
use std::iter;
use std::ops::ControlFlow;
+use super::NormalizeExt;
+
/// Whether we do the orphan check relative to this crate or
/// to some remote crate.
#[derive(Copy, Clone, Debug)]
predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates,
};
- let Normalized { value: mut header, obligations } =
- traits::normalize(selcx, param_env, ObligationCause::dummy(), header);
+ let InferOk { value: mut header, obligations } =
+ selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(header);
header.predicates.extend(obligations.into_iter().map(|o| o.predicate));
header
#[instrument(skip(infcx), level = "debug")]
pub fn is_const_evaluatable<'tcx>(
infcx: &InferCtxt<'tcx>,
- ct: ty::Const<'tcx>,
+ unexpanded_ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
span: Span,
) -> Result<(), NotConstEvaluatable> {
let tcx = infcx.tcx;
- let uv = match ct.kind() {
- ty::ConstKind::Unevaluated(uv) => uv,
- // FIXME(generic_const_exprs): this seems wrong but I couldn't find a way to get this to trigger
- ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"),
+ match tcx.expand_abstract_consts(unexpanded_ct).kind() {
+ ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) => (),
ty::ConstKind::Param(_)
| ty::ConstKind::Bound(_, _)
| ty::ConstKind::Placeholder(_)
};
if tcx.features().generic_const_exprs {
- let ct = tcx.expand_abstract_consts(ct);
+ let ct = tcx.expand_abstract_consts(unexpanded_ct);
let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
tcx.def_kind(uv.def.did) == DefKind::AnonConst
}
}
- let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
- match concrete {
- Err(ErrorHandled::TooGeneric) => Err(NotConstEvaluatable::Error(
- infcx
- .tcx
- .sess
- .delay_span_bug(span, "Missing value for constant, but no error reported?"),
- )),
- Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
- Ok(_) => Ok(()),
+ match unexpanded_ct.kind() {
+ ty::ConstKind::Expr(_) => {
+ // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, but
+ // currently it is not possible to evaluate `ConstKind::Expr` so we are unable to tell if it
+ // is evaluatable or not. For now we just ICE until this is implemented.
+ Err(NotConstEvaluatable::Error(tcx.sess.delay_span_bug(
+ span,
+ "evaluating `ConstKind::Expr` is not currently supported",
+ )))
+ }
+ ty::ConstKind::Unevaluated(uv) => {
+ let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
+ match concrete {
+ Err(ErrorHandled::TooGeneric) => {
+ Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug(
+ span,
+ "Missing value for constant, but no error reported?",
+ )))
+ }
+ Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
+ Ok(_) => Ok(()),
+ }
+ }
+ _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
}
} else {
+ let uv = match unexpanded_ct.kind() {
+ ty::ConstKind::Unevaluated(uv) => uv,
+ ty::ConstKind::Expr(_) => {
+ bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled")
+ }
+ _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
+ };
+
// FIXME: We should only try to evaluate a given constant here if it is fully concrete
// as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
//
&& satisfied_from_param_env(
tcx,
infcx,
- tcx.expand_abstract_consts(ct),
+ tcx.expand_abstract_consts(unexpanded_ct),
param_env,
) =>
{
impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> {
type BreakTy = ();
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ debug!("is_const_evaluatable: candidate={:?}", c);
if let Ok(()) = self.infcx.commit_if_ok(|_| {
let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty())
let result = b_ct.visit_with(&mut v);
if let ControlFlow::Break(()) = result {
- debug!("is_const_evaluatable: abstract_const ~~> ok");
+ debug!("is_const_evaluatable: yes");
return true;
}
}
}
}
+ debug!("is_const_evaluatable: no");
false
}
self.register_infer_ok_obligations(infer_ok)
}
+ /// Makes `expected <: actual`.
+ pub fn eq_exp<T>(
+ &self,
+ cause: &ObligationCause<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ a_is_expected: bool,
+ a: T,
+ b: T,
+ ) -> Result<(), TypeError<'tcx>>
+ where
+ T: ToTrace<'tcx>,
+ {
+ self.infcx
+ .at(cause, param_env)
+ .eq_exp(a_is_expected, a, b)
+ .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
+ }
+
pub fn eq<T: ToTrace<'tcx>>(
&self,
cause: &ObligationCause<'tcx>,
};
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use crate::infer::{self, InferCtxt, TyCtxtInferExt};
+use crate::infer::{self, InferCtxt};
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::query::normalize::QueryNormalizeExt as _;
use crate::traits::specialize::to_pretty_impl_header;
/// returns a span and `ArgKind` information that describes the
/// arguments it expects. This can be supplied to
/// `report_arg_count_mismatch`.
- fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec<ArgKind>)>;
+ fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)>;
/// Reports an error when the number of arguments needed by a
/// trait match doesn't match the number that the expression
expected_args: Vec<ArgKind>,
found_args: Vec<ArgKind>,
is_closure: bool,
+ closure_pipe_span: Option<Span>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
/// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
}
pub trait TypeErrCtxtExt<'tcx> {
+ fn report_overflow_error<T>(
+ &self,
+ predicate: &T,
+ span: Span,
+ suggest_increasing_limit: bool,
+ mutate: impl FnOnce(&mut Diagnostic),
+ ) -> !
+ where
+ T: fmt::Display
+ + TypeFoldable<'tcx>
+ + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
+ <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;
+
fn report_fulfillment_errors(
&self,
errors: &[FulfillmentError<'tcx>],
body_id: Option<hir::BodyId>,
) -> ErrorGuaranteed;
- fn report_overflow_error<T>(
+ fn report_overflow_obligation<T>(
&self,
obligation: &Obligation<'tcx, T>,
suggest_increasing_limit: bool,
) -> !
where
- T: fmt::Display
- + TypeFoldable<'tcx>
- + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
- <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;
+ T: ToPredicate<'tcx> + Clone;
fn suggest_new_overflow_limit(&self, err: &mut Diagnostic);
- fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !;
+ fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !;
/// The `root_obligation` parameter should be the `root_obligation` field
/// from a `FulfillmentError`. If no `FulfillmentError` is available,
/// returns a span and `ArgKind` information that describes the
/// arguments it expects. This can be supplied to
/// `report_arg_count_mismatch`.
- fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec<ArgKind>)> {
+ fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
let sm = self.tcx.sess.source_map();
let hir = self.tcx.hir();
Some(match node {
Node::Expr(&hir::Expr {
- kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }),
+ kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
..
}) => (
fn_decl_span,
+ fn_arg_span,
hir.body(body)
.params
.iter()
kind: hir::TraitItemKind::Fn(ref sig, _), ..
}) => (
sig.span,
+ None,
sig.decl
.inputs
.iter()
),
Node::Ctor(ref variant_data) => {
let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
- (span, vec![ArgKind::empty(); variant_data.fields().len()])
+ (span, None, vec![ArgKind::empty(); variant_data.fields().len()])
}
_ => panic!("non-FnLike node found: {:?}", node),
})
expected_args: Vec<ArgKind>,
found_args: Vec<ArgKind>,
is_closure: bool,
+ closure_arg_span: Option<Span>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let kind = if is_closure { "closure" } else { "function" };
if let Some(found_span) = found_span {
err.span_label(found_span, format!("takes {}", found_str));
- // move |_| { ... }
- // ^^^^^^^^-- def_span
- //
- // move |_| { ... }
- // ^^^^^-- prefix
- let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span);
- // move |_| { ... }
- // ^^^-- pipe_span
- let pipe_span =
- if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span };
-
// Suggest to take and ignore the arguments with expected_args_length `_`s if
// found arguments is empty (assume the user just wants to ignore args in this case).
// For example, if `expected_args_length` is 2, suggest `|_, _|`.
if found_args.is_empty() && is_closure {
let underscores = vec!["_"; expected_args.len()].join(", ");
err.span_suggestion_verbose(
- pipe_span,
+ closure_arg_span.unwrap_or(found_span),
&format!(
"consider changing the closure to take and ignore the expected argument{}",
pluralize!(expected_args.len())
/// occurrences in any case.
fn report_overflow_error<T>(
&self,
- obligation: &Obligation<'tcx, T>,
+ predicate: &T,
+ span: Span,
suggest_increasing_limit: bool,
+ mutate: impl FnOnce(&mut Diagnostic),
) -> !
where
T: fmt::Display
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
{
- let predicate = self.resolve_vars_if_possible(obligation.predicate.clone());
+ let predicate = self.resolve_vars_if_possible(predicate.clone());
let mut pred_str = predicate.to_string();
+
if pred_str.len() > 50 {
// We don't need to save the type to a file, we will be talking about this type already
// in a separate note when we explain the obligation, so it will be available that way.
}
let mut err = struct_span_err!(
self.tcx.sess,
- obligation.cause.span,
+ span,
E0275,
"overflow evaluating the requirement `{}`",
pred_str,
self.suggest_new_overflow_limit(&mut err);
}
- self.note_obligation_cause_code(
- &mut err,
- &obligation.predicate,
- obligation.param_env,
- obligation.cause.code(),
- &mut vec![],
- &mut Default::default(),
- );
+ mutate(&mut err);
err.emit();
self.tcx.sess.abort_if_errors();
bug!();
}
+ /// Reports that an overflow has occurred and halts compilation. We
+ /// halt compilation unconditionally because it is important that
+ /// overflows never be masked -- they basically represent computations
+ /// whose result could not be truly determined and thus we can't say
+ /// if the program type checks or not -- and they are unusual
+ /// occurrences in any case.
+ fn report_overflow_obligation<T>(
+ &self,
+ obligation: &Obligation<'tcx, T>,
+ suggest_increasing_limit: bool,
+ ) -> !
+ where
+ T: ToPredicate<'tcx> + Clone,
+ {
+ let predicate = obligation.predicate.clone().to_predicate(self.tcx);
+ let predicate = self.resolve_vars_if_possible(predicate);
+ self.report_overflow_error(
+ &predicate,
+ obligation.cause.span,
+ suggest_increasing_limit,
+ |err| {
+ self.note_obligation_cause_code(
+ err,
+ &predicate,
+ obligation.param_env,
+ obligation.cause.code(),
+ &mut vec![],
+ &mut Default::default(),
+ );
+ },
+ );
+ }
+
fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) {
let suggested_limit = match self.tcx.recursion_limit() {
Limit(0) => Limit(2),
}
/// Reports that a cycle was detected which led to overflow and halts
- /// compilation. This is equivalent to `report_overflow_error` except
+ /// compilation. This is equivalent to `report_overflow_obligation` except
/// that we can give a more helpful error message (and, in particular,
/// we do not suggest increasing the overflow limit, which is not
/// going to help).
- fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
+ fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
let cycle = self.resolve_vars_if_possible(cycle.to_owned());
assert!(!cycle.is_empty());
// The 'deepest' obligation is most likely to have a useful
// cause 'backtrace'
- self.report_overflow_error(cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), false);
+ self.report_overflow_obligation(
+ cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(),
+ false,
+ );
}
fn report_selection_error(
// can get a better error message by performing HIR-based well-formedness checking.
if let ObligationCauseCode::WellFormed(Some(wf_loc)) =
root_obligation.cause.code().peel_derives()
+ && !obligation.predicate.has_non_region_infer()
{
if let Some(cause) = self
.tcx
obligation.cause.code(),
)
} else {
- let (closure_span, found) = found_did
+ let (closure_span, closure_arg_span, found) = found_did
.and_then(|did| {
let node = self.tcx.hir().get_if_local(did)?;
- let (found_span, found) = self.get_fn_like_arguments(node)?;
- Some((Some(found_span), found))
+ let (found_span, closure_arg_span, found) =
+ self.get_fn_like_arguments(node)?;
+ Some((Some(found_span), closure_arg_span, found))
})
- .unwrap_or((found_span, found));
+ .unwrap_or((found_span, None, found));
self.report_arg_count_mismatch(
span,
expected,
found,
found_trait_ty.is_closure(),
+ closure_arg_span,
)
}
}
diag.emit();
}
FulfillmentErrorCode::CodeCycle(ref cycle) => {
- self.report_overflow_error_cycle(cycle);
+ self.report_overflow_obligation_cycle(cycle);
}
}
}
}
self.probe(|_| {
- let mut err = error.err;
- let mut values = None;
+ let ocx = ObligationCtxt::new_in_snapshot(self);
// try to find the mismatched types to report the error with.
//
// this can fail if the problem was higher-ranked, in which
// cause I have no idea for a good error message.
let bound_predicate = predicate.kind();
- if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) =
+ let (values, err) = if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) =
bound_predicate.skip_binder()
{
- let mut selcx = SelectionContext::new(self);
let data = self.replace_bound_vars_with_fresh_vars(
obligation.cause.span,
infer::LateBoundRegionConversionTime::HigherRankedType,
bound_predicate.rebind(data),
);
- let mut obligations = vec![];
- // FIXME(normalization): Change this to use `At::normalize`
- let normalized_ty = super::normalize_projection_type(
- &mut selcx,
+ let normalized_ty = ocx.normalize(
+ &obligation.cause,
obligation.param_env,
- data.projection_ty,
- obligation.cause.clone(),
- 0,
- &mut obligations,
+ self.tcx
+ .mk_projection(data.projection_ty.item_def_id, data.projection_ty.substs),
);
debug!(?obligation.cause, ?obligation.param_env);
| ObligationCauseCode::ObjectCastObligation(..)
| ObligationCauseCode::OpaqueType
);
- if let Err(new_err) = self.at(&obligation.cause, obligation.param_env).eq_exp(
+ let expected_ty = data.term.ty().unwrap_or_else(|| self.tcx.ty_error());
+
+ // constrain inference variables a bit more to nested obligations from normalize so
+ // we can have more helpful errors.
+ ocx.select_where_possible();
+
+ if let Err(new_err) = ocx.eq_exp(
+ &obligation.cause,
+ obligation.param_env,
is_normalized_ty_expected,
normalized_ty,
- data.term,
+ expected_ty,
) {
- values = Some((data, is_normalized_ty_expected, normalized_ty, data.term));
- err = new_err;
+ (Some((data, is_normalized_ty_expected, normalized_ty, expected_ty)), new_err)
+ } else {
+ (None, error.err)
}
- }
+ } else {
+ (None, error.err)
+ };
let msg = values
.and_then(|(predicate, _, normalized_ty, expected_ty)| {
- self.maybe_detailed_projection_msg(predicate, normalized_ty, expected_ty)
+ self.maybe_detailed_projection_msg(
+ predicate,
+ normalized_ty.into(),
+ expected_ty.into(),
+ )
})
.unwrap_or_else(|| format!("type mismatch resolving `{}`", predicate));
let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}");
&mut diag,
&obligation.cause,
secondary_span,
- values.map(|(_, is_normalized_ty_expected, normalized_ty, term)| {
+ values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| {
infer::ValuePairs::Terms(ExpectedFound::new(
is_normalized_ty_expected,
- normalized_ty,
- term,
+ normalized_ty.into(),
+ expected_ty.into(),
))
}),
err,
&self,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> Vec<ImplCandidate<'tcx>> {
- self.tcx
+ let mut candidates: Vec<_> = self
+ .tcx
.all_impls(trait_pred.def_id())
.filter_map(|def_id| {
if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative
self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false)
.map(|similarity| ImplCandidate { trait_ref: imp, similarity })
})
- .collect()
+ .collect();
+ if candidates.iter().any(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })) {
+ // If any of the candidates is a perfect match, we don't want to show all of them.
+ // This is particularly relevant for the case of numeric types (as they all have the
+ // same cathegory).
+ candidates.retain(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. }));
+ }
+ candidates
}
fn report_similar_impl_candidates(
return report(normalized_impl_candidates, err);
}
- let normalize = |candidate| {
- let infcx = self.tcx.infer_ctxt().build();
- infcx
- .at(&ObligationCause::dummy(), ty::ParamEnv::empty())
- .query_normalize(candidate)
- .map_or(candidate, |normalized| normalized.value)
- };
-
// Sort impl candidates so that ordering is consistent for UI tests.
// because the ordering of `impl_candidates` may not be deterministic:
// https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507
let mut normalized_impl_candidates_and_similarities = impl_candidates
.into_iter()
.map(|ImplCandidate { trait_ref, similarity }| {
- let normalized = normalize(trait_ref);
+ // FIXME(compiler-errors): This should be using `NormalizeExt::normalize`
+ let normalized = self
+ .at(&ObligationCause::dummy(), ty::ParamEnv::empty())
+ .query_normalize(trait_ref)
+ .map_or(trait_ref, |normalized| normalized.value);
(similarity, normalized)
})
.collect::<Vec<_>>();
use super::InferCtxtPrivExt;
use crate::infer::InferCtxtExt as _;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
#[derive(Debug)]
pub enum GeneratorInteriorOrUpvar {
obligated_types: &mut Vec<Ty<'tcx>>,
seen_requirements: &mut FxHashSet<DefId>,
) where
- T: fmt::Display + ToPredicate<'tcx, T>;
+ T: fmt::Display + ToPredicate<'tcx>;
/// Suggest to await before try: future? => future.await?
fn suggest_await_before_try(
obligated_types: &mut Vec<Ty<'tcx>>,
seen_requirements: &mut FxHashSet<DefId>,
) where
- T: fmt::Display,
+ T: fmt::Display + ToPredicate<'tcx>,
{
let tcx = self.tcx;
match *cause_code {
ObligationCauseCode::BindingObligation(item_def_id, span)
| ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..) => {
let item_name = tcx.def_path_str(item_def_id);
+ let short_item_name = with_forced_trimmed_paths!(tcx.def_path_str(item_def_id));
let mut multispan = MultiSpan::from(span);
+ let sm = tcx.sess.source_map();
if let Some(ident) = tcx.opt_item_ident(item_def_id) {
- let sm = tcx.sess.source_map();
let same_line =
match (sm.lookup_line(ident.span.hi()), sm.lookup_line(span.lo())) {
(Ok(l), Ok(r)) => l.line == r.line,
_ => true,
};
- if !ident.span.is_dummy() && !ident.span.overlaps(span) && !same_line {
+ if ident.span.is_visible(sm) && !ident.span.overlaps(span) && !same_line {
multispan.push_span_label(ident.span, "required by a bound in this");
}
}
- let descr = format!("required by a bound in `{}`", item_name);
- if !span.is_dummy() {
- let msg = format!("required by this bound in `{}`", item_name);
+ let descr = format!("required by a bound in `{item_name}`");
+ if span.is_visible(sm) {
+ let msg = format!("required by this bound in `{short_item_name}`");
multispan.push_span_label(span, msg);
err.span_note(multispan, &descr);
} else {
pub mod auto_trait;
mod chalk_fulfill;
-pub mod codegen;
mod coherence;
pub mod const_evaluatable;
mod engine;
mod specialize;
mod structural_match;
mod util;
+mod vtable;
pub mod wf;
-use crate::errors::DumpVTableEntries;
use crate::infer::outlives::env::OutlivesEnvironment;
use crate::infer::{InferCtxt, TyCtxtInferExt};
use crate::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{
- self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, VtblEntry,
-};
+use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeSuperVisitable};
use rustc_middle::ty::{InternalSubsts, SubstsRef};
-use rustc_span::{sym, Span};
-use smallvec::SmallVec;
+use rustc_span::Span;
use std::fmt::Debug;
use std::ops::ControlFlow;
pub use self::object_safety::is_vtable_safe_method;
pub use self::object_safety::MethodViolationCode;
pub use self::object_safety::ObjectSafetyViolation;
-pub(crate) use self::project::{normalize, normalize_to};
pub use self::project::{normalize_projection_type, NormalizeExt};
pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
fn pred_known_to_hold_modulo_regions<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- pred: impl ToPredicate<'tcx, ty::Predicate<'tcx>> + TypeVisitable<'tcx>,
+ pred: impl ToPredicate<'tcx> + TypeVisitable<'tcx>,
span: Span,
) -> bool {
let has_non_region_infer = pred.has_non_region_infer();
false
}
-#[derive(Clone, Debug)]
-enum VtblSegment<'tcx> {
- MetadataDSA,
- TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool },
-}
-
-/// Prepare the segments for a vtable
-fn prepare_vtable_segments<'tcx, T>(
- tcx: TyCtxt<'tcx>,
- trait_ref: ty::PolyTraitRef<'tcx>,
- mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
-) -> Option<T> {
- // The following constraints holds for the final arrangement.
- // 1. The whole virtual table of the first direct super trait is included as the
- // the prefix. If this trait doesn't have any super traits, then this step
- // consists of the dsa metadata.
- // 2. Then comes the proper pointer metadata(vptr) and all own methods for all
- // other super traits except those already included as part of the first
- // direct super trait virtual table.
- // 3. finally, the own methods of this trait.
-
- // This has the advantage that trait upcasting to the first direct super trait on each level
- // is zero cost, and to another trait includes only replacing the pointer with one level indirection,
- // while not using too much extra memory.
-
- // For a single inheritance relationship like this,
- // D --> C --> B --> A
- // The resulting vtable will consists of these segments:
- // DSA, A, B, C, D
-
- // For a multiple inheritance relationship like this,
- // D --> C --> A
- // \-> B
- // The resulting vtable will consists of these segments:
- // DSA, A, B, B-vptr, C, D
-
- // For a diamond inheritance relationship like this,
- // D --> B --> A
- // \-> C -/
- // The resulting vtable will consists of these segments:
- // DSA, A, B, C, C-vptr, D
-
- // For a more complex inheritance relationship like this:
- // O --> G --> C --> A
- // \ \ \-> B
- // | |-> F --> D
- // | \-> E
- // |-> N --> J --> H
- // \ \-> I
- // |-> M --> K
- // \-> L
- // The resulting vtable will consists of these segments:
- // DSA, A, B, B-vptr, C, D, D-vptr, E, E-vptr, F, F-vptr, G,
- // H, H-vptr, I, I-vptr, J, J-vptr, K, K-vptr, L, L-vptr, M, M-vptr,
- // N, N-vptr, O
-
- // emit dsa segment first.
- if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::MetadataDSA) {
- return Some(v);
- }
-
- let mut emit_vptr_on_new_entry = false;
- let mut visited = util::PredicateSet::new(tcx);
- let predicate = trait_ref.without_const().to_predicate(tcx);
- let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> =
- smallvec![(trait_ref, emit_vptr_on_new_entry, None)];
- visited.insert(predicate);
-
- // the main traversal loop:
- // basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes
- // that each node is emitted after all its descendents have been emitted.
- // so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set.
- // this is done on the fly.
- // Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it
- // stops after it finds a node that has a next-sibling node.
- // This next-sibling node will used as the starting point of next slice.
-
- // Example:
- // For a diamond inheritance relationship like this,
- // D#1 --> B#0 --> A#0
- // \-> C#1 -/
-
- // Starting point 0 stack [D]
- // Loop run #0: Stack after diving in is [D B A], A is "childless"
- // after this point, all newly visited nodes won't have a vtable that equals to a prefix of this one.
- // Loop run #0: Emitting the slice [B A] (in reverse order), B has a next-sibling node, so this slice stops here.
- // Loop run #0: Stack after exiting out is [D C], C is the next starting point.
- // Loop run #1: Stack after diving in is [D C], C is "childless", since its child A is skipped(already emitted).
- // Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node.
- // Loop run #1: Stack after exiting out is []. Now the function exits.
-
- loop {
- // dive deeper into the stack, recording the path
- 'diving_in: loop {
- if let Some((inner_most_trait_ref, _, _)) = stack.last() {
- let inner_most_trait_ref = *inner_most_trait_ref;
- let mut direct_super_traits_iter = tcx
- .super_predicates_of(inner_most_trait_ref.def_id())
- .predicates
- .into_iter()
- .filter_map(move |(pred, _)| {
- pred.subst_supertrait(tcx, &inner_most_trait_ref).to_opt_poly_trait_pred()
- });
-
- 'diving_in_skip_visited_traits: loop {
- if let Some(next_super_trait) = direct_super_traits_iter.next() {
- if visited.insert(next_super_trait.to_predicate(tcx)) {
- // We're throwing away potential constness of super traits here.
- // FIXME: handle ~const super traits
- let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref);
- stack.push((
- next_super_trait,
- emit_vptr_on_new_entry,
- Some(direct_super_traits_iter),
- ));
- break 'diving_in_skip_visited_traits;
- } else {
- continue 'diving_in_skip_visited_traits;
- }
- } else {
- break 'diving_in;
- }
- }
- }
- }
-
- // Other than the left-most path, vptr should be emitted for each trait.
- emit_vptr_on_new_entry = true;
-
- // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
- 'exiting_out: loop {
- if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() {
- if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::TraitOwnEntries {
- trait_ref: *inner_most_trait_ref,
- emit_vptr: *emit_vptr,
- }) {
- return Some(v);
- }
-
- 'exiting_out_skip_visited_traits: loop {
- if let Some(siblings) = siblings_opt {
- if let Some(next_inner_most_trait_ref) = siblings.next() {
- if visited.insert(next_inner_most_trait_ref.to_predicate(tcx)) {
- // We're throwing away potential constness of super traits here.
- // FIXME: handle ~const super traits
- let next_inner_most_trait_ref =
- next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
- *inner_most_trait_ref = next_inner_most_trait_ref;
- *emit_vptr = emit_vptr_on_new_entry;
- break 'exiting_out;
- } else {
- continue 'exiting_out_skip_visited_traits;
- }
- }
- }
- stack.pop();
- continue 'exiting_out;
- }
- }
- // all done
- return None;
- }
- }
-}
-
-fn dump_vtable_entries<'tcx>(
- tcx: TyCtxt<'tcx>,
- sp: Span,
- trait_ref: ty::PolyTraitRef<'tcx>,
- entries: &[VtblEntry<'tcx>],
-) {
- tcx.sess.emit_err(DumpVTableEntries {
- span: sp,
- trait_ref,
- entries: format!("{:#?}", entries),
- });
-}
-
-fn own_existential_vtable_entries<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> &'tcx [DefId] {
- let trait_methods = tcx
- .associated_items(trait_def_id)
- .in_definition_order()
- .filter(|item| item.kind == ty::AssocKind::Fn);
- // Now list each method's DefId (for within its trait).
- let own_entries = trait_methods.filter_map(move |trait_method| {
- debug!("own_existential_vtable_entry: trait_method={:?}", trait_method);
- let def_id = trait_method.def_id;
-
- // Some methods cannot be called on an object; skip those.
- if !is_vtable_safe_method(tcx, trait_def_id, &trait_method) {
- debug!("own_existential_vtable_entry: not vtable safe");
- return None;
- }
-
- Some(def_id)
- });
-
- tcx.arena.alloc_from_iter(own_entries.into_iter())
-}
-
-/// Given a trait `trait_ref`, iterates the vtable entries
-/// that come from `trait_ref`, including its supertraits.
-fn vtable_entries<'tcx>(
- tcx: TyCtxt<'tcx>,
- trait_ref: ty::PolyTraitRef<'tcx>,
-) -> &'tcx [VtblEntry<'tcx>] {
- debug!("vtable_entries({:?})", trait_ref);
-
- let mut entries = vec![];
-
- let vtable_segment_callback = |segment| -> ControlFlow<()> {
- match segment {
- VtblSegment::MetadataDSA => {
- entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
- }
- VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
- let existential_trait_ref = trait_ref
- .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
-
- // Lookup the shape of vtable for the trait.
- let own_existential_entries =
- tcx.own_existential_vtable_entries(existential_trait_ref.def_id());
-
- let own_entries = own_existential_entries.iter().copied().map(|def_id| {
- debug!("vtable_entries: trait_method={:?}", def_id);
-
- // The method may have some early-bound lifetimes; add regions for those.
- let substs = trait_ref.map_bound(|trait_ref| {
- InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind {
- GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
- GenericParamDefKind::Type { .. }
- | GenericParamDefKind::Const { .. } => {
- trait_ref.substs[param.index as usize]
- }
- })
- });
-
- // The trait type may have higher-ranked lifetimes in it;
- // erase them if they appear, so that we get the type
- // at some particular call site.
- let substs = tcx
- .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), substs);
-
- // It's possible that the method relies on where-clauses that
- // do not hold for this particular set of type parameters.
- // Note that this method could then never be called, so we
- // do not want to try and codegen it, in that case (see #23435).
- let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
- if impossible_predicates(tcx, predicates.predicates) {
- debug!("vtable_entries: predicates do not hold");
- return VtblEntry::Vacant;
- }
-
- let instance = ty::Instance::resolve_for_vtable(
- tcx,
- ty::ParamEnv::reveal_all(),
- def_id,
- substs,
- )
- .expect("resolution failed during building vtable representation");
- VtblEntry::Method(instance)
- });
-
- entries.extend(own_entries);
-
- if emit_vptr {
- entries.push(VtblEntry::TraitVPtr(trait_ref));
- }
- }
- }
-
- ControlFlow::Continue(())
- };
-
- let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback);
-
- if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) {
- let sp = tcx.def_span(trait_ref.def_id());
- dump_vtable_entries(tcx, sp, trait_ref, &entries);
- }
-
- tcx.arena.alloc_from_iter(entries.into_iter())
-}
-
-/// Find slot base for trait methods within vtable entries of another trait
-fn vtable_trait_first_method_offset<'tcx>(
- tcx: TyCtxt<'tcx>,
- key: (
- ty::PolyTraitRef<'tcx>, // trait_to_be_found
- ty::PolyTraitRef<'tcx>, // trait_owning_vtable
- ),
-) -> usize {
- let (trait_to_be_found, trait_owning_vtable) = key;
-
- // #90177
- let trait_to_be_found_erased = tcx.erase_regions(trait_to_be_found);
-
- let vtable_segment_callback = {
- let mut vtable_base = 0;
-
- move |segment| {
- match segment {
- VtblSegment::MetadataDSA => {
- vtable_base += TyCtxt::COMMON_VTABLE_ENTRIES.len();
- }
- VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
- if tcx.erase_regions(trait_ref) == trait_to_be_found_erased {
- return ControlFlow::Break(vtable_base);
- }
- vtable_base += util::count_own_vtable_entries(tcx, trait_ref);
- if emit_vptr {
- vtable_base += 1;
- }
- }
- }
- ControlFlow::Continue(())
- }
- };
-
- if let Some(vtable_base) =
- prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback)
- {
- vtable_base
- } else {
- bug!("Failed to find info for expected trait in vtable");
- }
-}
-
-/// Find slot offset for trait vptr within vtable entries of another trait
-pub fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
- tcx: TyCtxt<'tcx>,
- key: (
- Ty<'tcx>, // trait object type whose trait owning vtable
- Ty<'tcx>, // trait object for supertrait
- ),
-) -> Option<usize> {
- let (source, target) = key;
- assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer());
- assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer());
-
- // this has been typecked-before, so diagnostics is not really needed.
- let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None);
-
- let trait_ref = tcx.mk_trait_ref(unsize_trait_did, [source, target]);
-
- match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), ty::Binder::dummy(trait_ref))) {
- Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => {
- implsrc_traitcasting.vtable_vptr_slot
- }
- otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"),
- }
-}
-
pub fn provide(providers: &mut ty::query::Providers) {
object_safety::provide(providers);
- structural_match::provide(providers);
+ vtable::provide(providers);
*providers = ty::query::Providers {
specialization_graph_of: specialize::specialization_graph_provider,
specializes: specialize::specializes,
- codegen_select_candidate: codegen::codegen_select_candidate,
- own_existential_vtable_entries,
- vtable_entries,
- vtable_trait_upcasting_coercion_new_vptr_slot,
subst_and_check_impossible_predicates,
is_impossible_method,
..*providers
pub(super) struct InProgress;
pub trait NormalizeExt<'tcx> {
+ /// Normalize a value using the `AssocTypeNormalizer`.
+ ///
+ /// This normalization should be used when the type contains inference variables or the
+ /// projection may be fallible.
fn normalize<T: TypeFoldable<'tcx>>(&self, t: T) -> InferOk<'tcx, T>;
}
fn normalize<T: TypeFoldable<'tcx>>(&self, value: T) -> InferOk<'tcx, T> {
let mut selcx = SelectionContext::new(self.infcx);
let Normalized { value, obligations } =
- normalize(&mut selcx, self.param_env, self.cause.clone(), value);
+ normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
InferOk { value, obligations }
}
}
}
}
-/// Normalizes any associated type projections in `value`, replacing
-/// them with a fully resolved type where possible. The return value
-/// combines the normalized result and any additional obligations that
-/// were incurred as result.
-pub(crate) fn normalize<'a, 'b, 'tcx, T>(
- selcx: &'a mut SelectionContext<'b, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- cause: ObligationCause<'tcx>,
- value: T,
-) -> Normalized<'tcx, T>
-where
- T: TypeFoldable<'tcx>,
-{
- let mut obligations = Vec::new();
- let value = normalize_to(selcx, param_env, cause, value, &mut obligations);
- Normalized { value, obligations }
-}
-
-pub(crate) fn normalize_to<'a, 'b, 'tcx, T>(
- selcx: &'a mut SelectionContext<'b, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- cause: ObligationCause<'tcx>,
- value: T,
- obligations: &mut Vec<PredicateObligation<'tcx>>,
-) -> T
-where
- T: TypeFoldable<'tcx>,
-{
- normalize_with_depth_to(selcx, param_env, cause, 0, value, obligations)
-}
-
/// As `normalize`, but with a custom depth.
pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
Reveal::All => {
let recursion_limit = self.tcx().recursion_limit();
if !recursion_limit.value_within_limit(self.depth) {
- let obligation = Obligation::with_depth(
- self.tcx(),
- self.cause.clone(),
- recursion_limit.0,
- self.param_env,
- ty,
+ self.selcx.infcx.err_ctxt().report_overflow_error(
+ &ty,
+ self.cause.span,
+ true,
+ |_| {},
);
- self.selcx.infcx.err_ctxt().report_overflow_error(&obligation, true);
}
let substs = substs.fold_with(self);
},
));
- let ty = super::normalize_to(
+ let ty = normalize_with_depth_to(
selcx,
obligation.param_env,
cause.clone(),
+ obligation.recursion_depth + 1,
tcx.bound_trait_impl_trait_tys(impl_fn_def_id)
.map_bound(|tys| {
tys.map_or_else(|_| tcx.ty_error(), |tys| tys[&obligation.predicate.item_def_id])
nested: &mut Vec<PredicateObligation<'tcx>>,
) {
let tcx = selcx.tcx();
- for predicate in tcx
+ let own = tcx
.predicates_of(obligation.predicate.item_def_id)
- .instantiate_own(tcx, obligation.predicate.substs)
- .predicates
- {
+ .instantiate_own(tcx, obligation.predicate.substs);
+ for (predicate, span) in std::iter::zip(own.predicates, own.spans) {
let normalized = normalize_with_depth_to(
selcx,
obligation.param_env,
predicate,
nested,
);
+
+ let nested_cause = if matches!(
+ obligation.cause.code(),
+ super::CompareImplItemObligation { .. }
+ | super::CheckAssociatedTypeBounds { .. }
+ | super::AscribeUserTypeProvePredicate(..)
+ ) {
+ obligation.cause.clone()
+ } else if span.is_dummy() {
+ ObligationCause::new(
+ obligation.cause.span,
+ obligation.cause.body_id,
+ super::ItemObligation(obligation.predicate.item_def_id),
+ )
+ } else {
+ ObligationCause::new(
+ obligation.cause.span,
+ obligation.cause.body_id,
+ super::BindingObligation(obligation.predicate.item_def_id, span),
+ )
+ };
nested.push(Obligation::with_depth(
tcx,
- obligation.cause.clone(),
+ nested_cause,
obligation.recursion_depth + 1,
obligation.param_env,
normalized,
use crate::infer::{InferCtxt, InferOk};
use crate::traits::error_reporting::TypeErrCtxtExt;
use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
-use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
+use crate::traits::{ObligationCause, PredicateObligation, Reveal};
use rustc_data_structures::sso::SsoHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::traits::Normalized;
pub use rustc_middle::traits::query::NormalizationResult;
pub trait QueryNormalizeExt<'tcx> {
+ /// Normalize a value using the `QueryNormalizer`.
+ ///
+ /// This normalization should *only* be used when the projection does not
+ /// have possible ambiguity or may not be well-formed.
+ ///
+ /// After codegen, when lifetimes do not matter, it is preferable to instead
+ /// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure.
fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
where
T: TypeFoldable<'tcx>;
let substs = substs.try_fold_with(self)?;
let recursion_limit = self.tcx().recursion_limit();
if !recursion_limit.value_within_limit(self.anon_depth) {
- let obligation = Obligation::with_depth(
- self.tcx(),
- self.cause.clone(),
- recursion_limit.0,
- self.param_env,
- ty,
+ self.infcx.err_ctxt().report_overflow_error(
+ &ty,
+ self.cause.span,
+ true,
+ |_| {},
);
- self.infcx.err_ctxt().report_overflow_error(&obligation, true);
}
let generic_ty = self.tcx().bound_type_of(def_id);
// type/region parameters.
let self_ty = obligation.self_ty().skip_binder();
match self_ty.kind() {
- ty::Generator(..) => {
+ // async constructs get lowered to a special kind of generator that
+ // should *not* `impl Generator`.
+ ty::Generator(did, ..) if !self.tcx().generator_is_async(*did) => {
debug!(?self_ty, ?obligation, "assemble_generator_candidates",);
candidates.vec.push(GeneratorCandidate);
) {
let self_ty = obligation.self_ty().skip_binder();
if let ty::Generator(did, ..) = self_ty.kind() {
+ // async constructs get lowered to a special kind of generator that
+ // should directly `impl Future`.
if self.tcx().generator_is_async(*did) {
debug!(?self_ty, ?obligation, "assemble_future_candidates",);
use crate::traits::project::{normalize_with_depth, normalize_with_depth_to};
use crate::traits::util::{self, closure_trait_ref_and_return_type, predicate_for_trait_def};
+use crate::traits::vtable::{
+ count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset,
+ VtblSegment,
+};
use crate::traits::{
BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
ImplSourceGeneratorData, ImplSourceObjectData, ImplSourceTraitAliasData,
ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, ObjectCastObligation,
Obligation, ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection,
- SelectionError, TraitNotObjectSafe, TraitObligation, Unimplemented, VtblSegment,
+ SelectionError, TraitNotObjectSafe, TraitObligation, Unimplemented,
};
use super::BuiltinImplConditions;
debug!(?nested, "object nested obligations");
- let vtable_base = super::super::vtable_trait_first_method_offset(
+ let vtable_base = vtable_trait_first_method_offset(
tcx,
(unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)),
);
vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
}
VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
- vptr_offset += util::count_own_vtable_entries(tcx, trait_ref);
+ vptr_offset += count_own_vtable_entries(tcx, trait_ref);
if trait_ref == upcast_trait_ref {
if emit_vptr {
return ControlFlow::Break(Some(vptr_offset));
};
let vtable_vptr_slot =
- super::super::prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback)
- .unwrap();
+ prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap();
Ok(ImplSourceTraitUpcastingData { upcast_trait_ref, vtable_vptr_slot, nested })
}
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::fold::BottomUpFolder;
-use rustc_middle::ty::print::{FmtPrinter, Print};
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::SubstsRef;
use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
if !candidate_set.ambiguous && no_candidates_apply {
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
- let self_ty = trait_ref.self_ty();
- let (trait_desc, self_desc) = with_no_trimmed_paths!({
- let trait_desc = trait_ref.print_only_trait_path().to_string();
- let self_desc = if self_ty.has_concrete_skeleton() {
- Some(self_ty.to_string())
+ if !trait_ref.references_error() {
+ let self_ty = trait_ref.self_ty();
+ let (trait_desc, self_desc) = with_no_trimmed_paths!({
+ let trait_desc = trait_ref.print_only_trait_path().to_string();
+ let self_desc = if self_ty.has_concrete_skeleton() {
+ Some(self_ty.to_string())
+ } else {
+ None
+ };
+ (trait_desc, self_desc)
+ });
+ let cause = if let Conflict::Upstream = conflict {
+ IntercrateAmbiguityCause::UpstreamCrateUpdate {
+ trait_desc,
+ self_desc,
+ }
} else {
- None
+ IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
};
- (trait_desc, self_desc)
- });
- let cause = if let Conflict::Upstream = conflict {
- IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
- } else {
- IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
- };
- debug!(?cause, "evaluate_stack: pushing cause");
- self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause);
+ debug!(?cause, "evaluate_stack: pushing cause");
+ self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause);
+ }
}
}
}
error_obligation: &Obligation<'tcx, T>,
) -> Result<(), OverflowError>
where
- T: fmt::Display
- + TypeFoldable<'tcx>
- + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
- <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
+ T: ToPredicate<'tcx> + Clone,
{
if !self.infcx.tcx.recursion_limit().value_within_limit(depth) {
match self.query_mode {
if let Some(e) = self.infcx.tainted_by_errors() {
return Err(OverflowError::Error(e));
}
- self.infcx.err_ctxt().report_overflow_error(error_obligation, true);
+ self.infcx.err_ctxt().report_overflow_obligation(error_obligation, true);
}
TraitQueryMode::Canonical => {
return Err(OverflowError::Canonical);
error_obligation: &Obligation<'tcx, V>,
) -> Result<(), OverflowError>
where
- V: fmt::Display
- + TypeFoldable<'tcx>
- + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
- <V as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
+ V: ToPredicate<'tcx> + Clone,
{
self.check_recursion_depth(obligation.recursion_depth, error_obligation)
}
-use crate::infer::{InferCtxt, TyCtxtInferExt};
-use crate::traits::{ObligationCause, ObligationCtxt};
-
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
-use rustc_hir::lang_items::LangItem;
-use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_span::Span;
use std::ops::ControlFlow;
.break_value()
}
-/// This method returns true if and only if `adt_ty` itself has been marked as
-/// eligible for structural-match: namely, if it implements both
-/// `StructuralPartialEq` and `StructuralEq` (which are respectively injected by
-/// `#[derive(PartialEq)]` and `#[derive(Eq)]`).
-///
-/// Note that this does *not* recursively check if the substructure of `adt_ty`
-/// implements the traits.
-fn type_marked_structural<'tcx>(
- infcx: &InferCtxt<'tcx>,
- adt_ty: Ty<'tcx>,
- cause: ObligationCause<'tcx>,
-) -> bool {
- let ocx = ObligationCtxt::new(infcx);
- // require `#[derive(PartialEq)]`
- let structural_peq_def_id =
- infcx.tcx.require_lang_item(LangItem::StructuralPeq, Some(cause.span));
- ocx.register_bound(cause.clone(), ty::ParamEnv::empty(), adt_ty, structural_peq_def_id);
- // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around
- // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.)
- let structural_teq_def_id =
- infcx.tcx.require_lang_item(LangItem::StructuralTeq, Some(cause.span));
- ocx.register_bound(cause, ty::ParamEnv::empty(), adt_ty, structural_teq_def_id);
-
- // We deliberately skip *reporting* fulfillment errors (via
- // `report_fulfillment_errors`), for two reasons:
- //
- // 1. The error messages would mention `std::marker::StructuralPartialEq`
- // (a trait which is solely meant as an implementation detail
- // for now), and
- //
- // 2. We are sometimes doing future-incompatibility lints for
- // now, so we do not want unconditional errors here.
- ocx.select_all_or_error().is_empty()
-}
-
/// This implements the traversal over the structure of a given type to try to
/// find instances of ADTs (specifically structs or enums) that do not implement
/// the structural-match traits (`StructuralPartialEq` and `StructuralEq`).
})
}
}
-
-pub fn provide(providers: &mut Providers) {
- providers.has_structural_eq_impls = |tcx, ty| {
- let infcx = tcx.infer_ctxt().build();
- let cause = ObligationCause::dummy();
- type_marked_structural(&infcx, ty, cause)
- };
-}
supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect()
}
-/// Given a trait `trait_ref`, returns the number of vtable entries
-/// that come from `trait_ref`, excluding its supertraits. Used in
-/// computing the vtable base for an upcast trait of a trait object.
-pub fn count_own_vtable_entries<'tcx>(
- tcx: TyCtxt<'tcx>,
- trait_ref: ty::PolyTraitRef<'tcx>,
-) -> usize {
- tcx.own_existential_vtable_entries(trait_ref.def_id()).len()
-}
-
/// Given an upcast trait object described by `object`, returns the
/// index of the method `method_def_id` (which should be part of
/// `object.upcast_trait_ref`) within the vtable for `object`.
--- /dev/null
+use crate::errors::DumpVTableEntries;
+use crate::traits::{impossible_predicates, is_vtable_safe_method};
+use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItem;
+use rustc_infer::traits::util::PredicateSet;
+use rustc_infer::traits::ImplSource;
+use rustc_middle::ty::visit::TypeVisitable;
+use rustc_middle::ty::InternalSubsts;
+use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry};
+use rustc_span::{sym, Span};
+use smallvec::SmallVec;
+
+use std::fmt::Debug;
+use std::ops::ControlFlow;
+
+#[derive(Clone, Debug)]
+pub(super) enum VtblSegment<'tcx> {
+ MetadataDSA,
+ TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool },
+}
+
+/// Prepare the segments for a vtable
+pub(super) fn prepare_vtable_segments<'tcx, T>(
+ tcx: TyCtxt<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
+) -> Option<T> {
+ // The following constraints holds for the final arrangement.
+ // 1. The whole virtual table of the first direct super trait is included as the
+ // the prefix. If this trait doesn't have any super traits, then this step
+ // consists of the dsa metadata.
+ // 2. Then comes the proper pointer metadata(vptr) and all own methods for all
+ // other super traits except those already included as part of the first
+ // direct super trait virtual table.
+ // 3. finally, the own methods of this trait.
+
+ // This has the advantage that trait upcasting to the first direct super trait on each level
+ // is zero cost, and to another trait includes only replacing the pointer with one level indirection,
+ // while not using too much extra memory.
+
+ // For a single inheritance relationship like this,
+ // D --> C --> B --> A
+ // The resulting vtable will consists of these segments:
+ // DSA, A, B, C, D
+
+ // For a multiple inheritance relationship like this,
+ // D --> C --> A
+ // \-> B
+ // The resulting vtable will consists of these segments:
+ // DSA, A, B, B-vptr, C, D
+
+ // For a diamond inheritance relationship like this,
+ // D --> B --> A
+ // \-> C -/
+ // The resulting vtable will consists of these segments:
+ // DSA, A, B, C, C-vptr, D
+
+ // For a more complex inheritance relationship like this:
+ // O --> G --> C --> A
+ // \ \ \-> B
+ // | |-> F --> D
+ // | \-> E
+ // |-> N --> J --> H
+ // \ \-> I
+ // |-> M --> K
+ // \-> L
+ // The resulting vtable will consists of these segments:
+ // DSA, A, B, B-vptr, C, D, D-vptr, E, E-vptr, F, F-vptr, G,
+ // H, H-vptr, I, I-vptr, J, J-vptr, K, K-vptr, L, L-vptr, M, M-vptr,
+ // N, N-vptr, O
+
+ // emit dsa segment first.
+ if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::MetadataDSA) {
+ return Some(v);
+ }
+
+ let mut emit_vptr_on_new_entry = false;
+ let mut visited = PredicateSet::new(tcx);
+ let predicate = trait_ref.without_const().to_predicate(tcx);
+ let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> =
+ smallvec![(trait_ref, emit_vptr_on_new_entry, None)];
+ visited.insert(predicate);
+
+ // the main traversal loop:
+ // basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes
+ // that each node is emitted after all its descendents have been emitted.
+ // so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set.
+ // this is done on the fly.
+ // Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it
+ // stops after it finds a node that has a next-sibling node.
+ // This next-sibling node will used as the starting point of next slice.
+
+ // Example:
+ // For a diamond inheritance relationship like this,
+ // D#1 --> B#0 --> A#0
+ // \-> C#1 -/
+
+ // Starting point 0 stack [D]
+ // Loop run #0: Stack after diving in is [D B A], A is "childless"
+ // after this point, all newly visited nodes won't have a vtable that equals to a prefix of this one.
+ // Loop run #0: Emitting the slice [B A] (in reverse order), B has a next-sibling node, so this slice stops here.
+ // Loop run #0: Stack after exiting out is [D C], C is the next starting point.
+ // Loop run #1: Stack after diving in is [D C], C is "childless", since its child A is skipped(already emitted).
+ // Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node.
+ // Loop run #1: Stack after exiting out is []. Now the function exits.
+
+ loop {
+ // dive deeper into the stack, recording the path
+ 'diving_in: loop {
+ if let Some((inner_most_trait_ref, _, _)) = stack.last() {
+ let inner_most_trait_ref = *inner_most_trait_ref;
+ let mut direct_super_traits_iter = tcx
+ .super_predicates_of(inner_most_trait_ref.def_id())
+ .predicates
+ .into_iter()
+ .filter_map(move |(pred, _)| {
+ pred.subst_supertrait(tcx, &inner_most_trait_ref).to_opt_poly_trait_pred()
+ });
+
+ 'diving_in_skip_visited_traits: loop {
+ if let Some(next_super_trait) = direct_super_traits_iter.next() {
+ if visited.insert(next_super_trait.to_predicate(tcx)) {
+ // We're throwing away potential constness of super traits here.
+ // FIXME: handle ~const super traits
+ let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref);
+ stack.push((
+ next_super_trait,
+ emit_vptr_on_new_entry,
+ Some(direct_super_traits_iter),
+ ));
+ break 'diving_in_skip_visited_traits;
+ } else {
+ continue 'diving_in_skip_visited_traits;
+ }
+ } else {
+ break 'diving_in;
+ }
+ }
+ }
+ }
+
+ // Other than the left-most path, vptr should be emitted for each trait.
+ emit_vptr_on_new_entry = true;
+
+ // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
+ 'exiting_out: loop {
+ if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() {
+ if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::TraitOwnEntries {
+ trait_ref: *inner_most_trait_ref,
+ emit_vptr: *emit_vptr,
+ }) {
+ return Some(v);
+ }
+
+ 'exiting_out_skip_visited_traits: loop {
+ if let Some(siblings) = siblings_opt {
+ if let Some(next_inner_most_trait_ref) = siblings.next() {
+ if visited.insert(next_inner_most_trait_ref.to_predicate(tcx)) {
+ // We're throwing away potential constness of super traits here.
+ // FIXME: handle ~const super traits
+ let next_inner_most_trait_ref =
+ next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
+ *inner_most_trait_ref = next_inner_most_trait_ref;
+ *emit_vptr = emit_vptr_on_new_entry;
+ break 'exiting_out;
+ } else {
+ continue 'exiting_out_skip_visited_traits;
+ }
+ }
+ }
+ stack.pop();
+ continue 'exiting_out;
+ }
+ }
+ // all done
+ return None;
+ }
+ }
+}
+
+fn dump_vtable_entries<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ sp: Span,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ entries: &[VtblEntry<'tcx>],
+) {
+ tcx.sess.emit_err(DumpVTableEntries {
+ span: sp,
+ trait_ref,
+ entries: format!("{:#?}", entries),
+ });
+}
+
+fn own_existential_vtable_entries<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> &'tcx [DefId] {
+ let trait_methods = tcx
+ .associated_items(trait_def_id)
+ .in_definition_order()
+ .filter(|item| item.kind == ty::AssocKind::Fn);
+ // Now list each method's DefId (for within its trait).
+ let own_entries = trait_methods.filter_map(move |trait_method| {
+ debug!("own_existential_vtable_entry: trait_method={:?}", trait_method);
+ let def_id = trait_method.def_id;
+
+ // Some methods cannot be called on an object; skip those.
+ if !is_vtable_safe_method(tcx, trait_def_id, &trait_method) {
+ debug!("own_existential_vtable_entry: not vtable safe");
+ return None;
+ }
+
+ Some(def_id)
+ });
+
+ tcx.arena.alloc_from_iter(own_entries.into_iter())
+}
+
+/// Given a trait `trait_ref`, iterates the vtable entries
+/// that come from `trait_ref`, including its supertraits.
+fn vtable_entries<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+) -> &'tcx [VtblEntry<'tcx>] {
+ debug!("vtable_entries({:?})", trait_ref);
+
+ let mut entries = vec![];
+
+ let vtable_segment_callback = |segment| -> ControlFlow<()> {
+ match segment {
+ VtblSegment::MetadataDSA => {
+ entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
+ }
+ VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+ let existential_trait_ref = trait_ref
+ .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
+
+ // Lookup the shape of vtable for the trait.
+ let own_existential_entries =
+ tcx.own_existential_vtable_entries(existential_trait_ref.def_id());
+
+ let own_entries = own_existential_entries.iter().copied().map(|def_id| {
+ debug!("vtable_entries: trait_method={:?}", def_id);
+
+ // The method may have some early-bound lifetimes; add regions for those.
+ let substs = trait_ref.map_bound(|trait_ref| {
+ InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind {
+ GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
+ GenericParamDefKind::Type { .. }
+ | GenericParamDefKind::Const { .. } => {
+ trait_ref.substs[param.index as usize]
+ }
+ })
+ });
+
+ // The trait type may have higher-ranked lifetimes in it;
+ // erase them if they appear, so that we get the type
+ // at some particular call site.
+ let substs = tcx
+ .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), substs);
+
+ // It's possible that the method relies on where-clauses that
+ // do not hold for this particular set of type parameters.
+ // Note that this method could then never be called, so we
+ // do not want to try and codegen it, in that case (see #23435).
+ let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
+ if impossible_predicates(tcx, predicates.predicates) {
+ debug!("vtable_entries: predicates do not hold");
+ return VtblEntry::Vacant;
+ }
+
+ let instance = ty::Instance::resolve_for_vtable(
+ tcx,
+ ty::ParamEnv::reveal_all(),
+ def_id,
+ substs,
+ )
+ .expect("resolution failed during building vtable representation");
+ VtblEntry::Method(instance)
+ });
+
+ entries.extend(own_entries);
+
+ if emit_vptr {
+ entries.push(VtblEntry::TraitVPtr(trait_ref));
+ }
+ }
+ }
+
+ ControlFlow::Continue(())
+ };
+
+ let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback);
+
+ if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) {
+ let sp = tcx.def_span(trait_ref.def_id());
+ dump_vtable_entries(tcx, sp, trait_ref, &entries);
+ }
+
+ tcx.arena.alloc_from_iter(entries.into_iter())
+}
+
+/// Find slot base for trait methods within vtable entries of another trait
+pub(super) fn vtable_trait_first_method_offset<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ key: (
+ ty::PolyTraitRef<'tcx>, // trait_to_be_found
+ ty::PolyTraitRef<'tcx>, // trait_owning_vtable
+ ),
+) -> usize {
+ let (trait_to_be_found, trait_owning_vtable) = key;
+
+ // #90177
+ let trait_to_be_found_erased = tcx.erase_regions(trait_to_be_found);
+
+ let vtable_segment_callback = {
+ let mut vtable_base = 0;
+
+ move |segment| {
+ match segment {
+ VtblSegment::MetadataDSA => {
+ vtable_base += TyCtxt::COMMON_VTABLE_ENTRIES.len();
+ }
+ VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+ if tcx.erase_regions(trait_ref) == trait_to_be_found_erased {
+ return ControlFlow::Break(vtable_base);
+ }
+ vtable_base += count_own_vtable_entries(tcx, trait_ref);
+ if emit_vptr {
+ vtable_base += 1;
+ }
+ }
+ }
+ ControlFlow::Continue(())
+ }
+ };
+
+ if let Some(vtable_base) =
+ prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback)
+ {
+ vtable_base
+ } else {
+ bug!("Failed to find info for expected trait in vtable");
+ }
+}
+
+/// Find slot offset for trait vptr within vtable entries of another trait
+pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ key: (
+ Ty<'tcx>, // trait object type whose trait owning vtable
+ Ty<'tcx>, // trait object for supertrait
+ ),
+) -> Option<usize> {
+ let (source, target) = key;
+ assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer());
+ assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer());
+
+ // this has been typecked-before, so diagnostics is not really needed.
+ let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None);
+
+ let trait_ref = tcx.mk_trait_ref(unsize_trait_did, [source, target]);
+
+ match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), ty::Binder::dummy(trait_ref))) {
+ Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => {
+ implsrc_traitcasting.vtable_vptr_slot
+ }
+ otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"),
+ }
+}
+
+/// Given a trait `trait_ref`, returns the number of vtable entries
+/// that come from `trait_ref`, excluding its supertraits. Used in
+/// computing the vtable base for an upcast trait of a trait object.
+pub(crate) fn count_own_vtable_entries<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+) -> usize {
+ tcx.own_existential_vtable_entries(trait_ref.def_id()).len()
+}
+
+pub(super) fn provide(providers: &mut ty::query::Providers) {
+ *providers = ty::query::Providers {
+ own_existential_vtable_entries,
+ vtable_entries,
+ vtable_trait_upcasting_coercion_new_vptr_slot,
+ ..*providers
+ };
+}
ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())),
));
}
- // FIXME(generic_const_exprs): This seems wrong but I could not find a way to get this to trigger
ty::ConstKind::Expr(_) => {
- bug!("checking wfness of `ConstKind::Expr` is unsupported")
+ // FIXME(generic_const_exprs): this doesnt verify that given `Expr(N + 1)` the
+ // trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
+ // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
+ // which means that the `DefId` would have been typeck'd elsewhere. However in
+ // the future we may allow directly lowering to `ConstKind::Expr` in which case
+ // we would not be proving bounds we should.
+
+ let predicate =
+ ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct));
+ let cause = self.cause(traits::WellFormed(None));
+ self.out.push(traits::Obligation::with_depth(
+ self.tcx(),
+ cause,
+ self.recursion_depth,
+ self.param_env,
+ predicate,
+ ));
}
ty::ConstKind::Error(_)
--- /dev/null
+// This file contains various trait resolution methods used by codegen.
+// They all assume regions can be erased and monomorphic types. It
+// seems likely that they should eventually be merged into more
+// general routines.
+
+use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
+use rustc_infer::traits::FulfillmentErrorCode;
+use rustc_middle::traits::CodegenObligationError;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::traits::{
+ ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, TraitEngineExt,
+ Unimplemented,
+};
+
+/// Attempts to resolve an obligation to an `ImplSource`. The result is
+/// a shallow `ImplSource` resolution, meaning that we do not
+/// (necessarily) resolve all nested obligations on the impl. Note
+/// that type check should guarantee to us that all nested
+/// obligations *could be* resolved if we wanted to.
+///
+/// This also expects that `trait_ref` is fully normalized.
+pub fn codegen_select_candidate<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
+) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
+ // We expect the input to be fully normalized.
+ debug_assert_eq!(trait_ref, tcx.normalize_erasing_regions(param_env, trait_ref));
+
+ // Do the initial selection for the obligation. This yields the
+ // shallow result we are looking for -- that is, what specific impl.
+ let infcx = tcx
+ .infer_ctxt()
+ .ignoring_regions()
+ .with_opaque_type_inference(DefiningAnchor::Bubble)
+ .build();
+ //~^ HACK `Bubble` is required for
+ // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs
+ let mut selcx = SelectionContext::new(&infcx);
+
+ let obligation_cause = ObligationCause::dummy();
+ let obligation = Obligation::new(tcx, obligation_cause, param_env, trait_ref);
+
+ let selection = match selcx.select(&obligation) {
+ Ok(Some(selection)) => selection,
+ Ok(None) => return Err(CodegenObligationError::Ambiguity),
+ Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented),
+ Err(e) => {
+ bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
+ }
+ };
+
+ debug!(?selection);
+
+ // Currently, we use a fulfillment context to completely resolve
+ // all nested obligations. This is because they can inform the
+ // inference of the impl's type parameters.
+ let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx);
+ let impl_source = selection.map(|predicate| {
+ fulfill_cx.register_predicate_obligation(&infcx, predicate);
+ });
+
+ // In principle, we only need to do this so long as `impl_source`
+ // contains unbound type parameters. It could be a slight
+ // optimization to stop iterating early.
+ let errors = fulfill_cx.select_all_or_error(&infcx);
+ if !errors.is_empty() {
+ // `rustc_monomorphize::collector` assumes there are no type errors.
+ // Cycle errors are the only post-monomorphization errors possible; emit them now so
+ // `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization.
+ for err in errors {
+ if let FulfillmentErrorCode::CodeCycle(cycle) = err.code {
+ infcx.err_ctxt().report_overflow_obligation_cycle(&cycle);
+ }
+ }
+ return Err(CodegenObligationError::FulfillmentError);
+ }
+
+ let impl_source = infcx.resolve_vars_if_possible(impl_source);
+ let impl_source = infcx.tcx.erase_regions(impl_source);
+
+ // Opaque types may have gotten their hidden types constrained, but we can ignore them safely
+ // as they will get constrained elsewhere, too.
+ // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass
+ let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+
+ Ok(&*tcx.arena.alloc(impl_source))
+}
extern crate rustc_middle;
mod chalk;
+mod codegen;
mod dropck_outlives;
mod evaluate_obligation;
mod implied_outlives_bounds;
normalize_projection_ty::provide(p);
normalize_erasing_regions::provide(p);
type_op::provide(p);
+ p.codegen_select_candidate = codegen::codegen_select_candidate;
}
bound_vars,
)
}
- ty::Generator(_, substs, _) => {
+ ty::Generator(did, substs, _) => {
let sig = substs.as_generator().poly_sig();
let bound_vars = tcx.mk_bound_variable_kinds(
let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
let sig = sig.skip_binder();
- let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
- let state_adt_ref = tcx.adt_def(state_did);
- let state_substs = tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
- let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
+ // The `FnSig` and the `ret_ty` here is for a generators main
+ // `Generator::resume(...) -> GeneratorState` function in case we
+ // have an ordinary generator, or the `Future::poll(...) -> Poll`
+ // function in case this is a special generator backing an async construct.
+ let ret_ty = if tcx.generator_is_async(did) {
+ let state_did = tcx.require_lang_item(LangItem::Poll, None);
+ let state_adt_ref = tcx.adt_def(state_did);
+ let state_substs = tcx.intern_substs(&[sig.return_ty.into()]);
+ tcx.mk_adt(state_adt_ref, state_substs)
+ } else {
+ let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
+ let state_adt_ref = tcx.adt_def(state_did);
+ let state_substs = tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
+ tcx.mk_adt(state_adt_ref, state_substs)
+ };
+
ty::Binder::bind_with_vars(
tcx.mk_fn_sig(
[env_ty, sig.resume_ty].iter(),
+use hir::def_id::DefId;
use rustc_hir as hir;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
};
use rustc_middle::ty::{
- self, subst::SubstsRef, EarlyBinder, ReprOptions, Ty, TyCtxt, TypeVisitable,
+ self, subst::SubstsRef, AdtDef, EarlyBinder, ReprOptions, Ty, TyCtxt, TypeVisitable,
};
use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
use rustc_span::symbol::Symbol;
);
};
- let adt_def = match *layout.ty.kind() {
- ty::Adt(ref adt_def, _) => {
+ match *layout.ty.kind() {
+ ty::Adt(adt_def, _) => {
debug!("print-type-size t: `{:?}` process adt", layout.ty);
- adt_def
+ let adt_kind = adt_def.adt_kind();
+ let adt_packed = adt_def.repr().pack.is_some();
+ let (variant_infos, opt_discr_size) = variant_info_for_adt(cx, layout, adt_def);
+ record(adt_kind.into(), adt_packed, opt_discr_size, variant_infos);
+ }
+
+ ty::Generator(def_id, substs, _) => {
+ debug!("print-type-size t: `{:?}` record generator", layout.ty);
+ // Generators always have a begin/poisoned/end state with additional suspend points
+ let (variant_infos, opt_discr_size) =
+ variant_info_for_generator(cx, layout, def_id, substs);
+ record(DataTypeKind::Generator, false, opt_discr_size, variant_infos);
}
ty::Closure(..) => {
debug!("print-type-size t: `{:?}` record closure", layout.ty);
record(DataTypeKind::Closure, false, None, vec![]);
- return;
}
_ => {
debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty);
- return;
}
};
+}
- let adt_kind = adt_def.adt_kind();
- let adt_packed = adt_def.repr().pack.is_some();
-
+fn variant_info_for_adt<'tcx>(
+ cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
+ layout: TyAndLayout<'tcx>,
+ adt_def: AdtDef<'tcx>,
+) -> (Vec<VariantInfo>, Option<Size>) {
let build_variant_info = |n: Option<Symbol>, flds: &[Symbol], layout: TyAndLayout<'tcx>| {
let mut min_size = Size::ZERO;
let field_info: Vec<_> = flds
.map(|(i, &name)| {
let field_layout = layout.field(cx, i);
let offset = layout.fields.offset(i);
- let field_end = offset + field_layout.size;
- if min_size < field_end {
- min_size = field_end;
- }
+ min_size = min_size.max(offset + field_layout.size);
FieldInfo {
name,
offset: offset.bytes(),
debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variant(index).name);
let variant_def = &adt_def.variant(index);
let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect();
- record(
- adt_kind.into(),
- adt_packed,
- None,
- vec![build_variant_info(Some(variant_def.name), &fields, layout)],
- );
+ (vec![build_variant_info(Some(variant_def.name), &fields, layout)], None)
} else {
- // (This case arises for *empty* enums; so give it
- // zero variants.)
- record(adt_kind.into(), adt_packed, None, vec![]);
+ (vec![], None)
}
}
build_variant_info(Some(variant_def.name), &fields, layout.for_variant(cx, i))
})
.collect();
- record(
- adt_kind.into(),
- adt_packed,
+
+ (
+ variant_infos,
match tag_encoding {
TagEncoding::Direct => Some(tag.size(cx)),
_ => None,
},
- variant_infos,
- );
+ )
}
}
}
+
+fn variant_info_for_generator<'tcx>(
+ cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
+ layout: TyAndLayout<'tcx>,
+ def_id: DefId,
+ substs: ty::SubstsRef<'tcx>,
+) -> (Vec<VariantInfo>, Option<Size>) {
+ let Variants::Multiple { tag, ref tag_encoding, .. } = layout.variants else {
+ return (vec![], None);
+ };
+
+ let (generator, state_specific_names) = cx.tcx.generator_layout_and_saved_local_names(def_id);
+ let upvar_names = cx.tcx.closure_saved_names_of_captured_variables(def_id);
+
+ let mut upvars_size = Size::ZERO;
+ let upvar_fields: Vec<_> = substs
+ .as_generator()
+ .upvar_tys()
+ .zip(upvar_names)
+ .enumerate()
+ .map(|(field_idx, (_, name))| {
+ let field_layout = layout.field(cx, field_idx);
+ let offset = layout.fields.offset(field_idx);
+ upvars_size = upvars_size.max(offset + field_layout.size);
+ FieldInfo {
+ name: Symbol::intern(&name),
+ offset: offset.bytes(),
+ size: field_layout.size.bytes(),
+ align: field_layout.align.abi.bytes(),
+ }
+ })
+ .collect();
+
+ let variant_infos: Vec<_> = generator
+ .variant_fields
+ .iter_enumerated()
+ .map(|(variant_idx, variant_def)| {
+ let variant_layout = layout.for_variant(cx, variant_idx);
+ let mut variant_size = Size::ZERO;
+ let fields = variant_def
+ .iter()
+ .enumerate()
+ .map(|(field_idx, local)| {
+ let field_layout = variant_layout.field(cx, field_idx);
+ let offset = variant_layout.fields.offset(field_idx);
+ // The struct is as large as the last field's end
+ variant_size = variant_size.max(offset + field_layout.size);
+ FieldInfo {
+ name: state_specific_names.get(*local).copied().flatten().unwrap_or(
+ Symbol::intern(&format!(".generator_field{}", local.as_usize())),
+ ),
+ offset: offset.bytes(),
+ size: field_layout.size.bytes(),
+ align: field_layout.align.abi.bytes(),
+ }
+ })
+ .chain(upvar_fields.iter().copied())
+ .collect();
+
+ // If the variant has no state-specific fields, then it's the size of the upvars.
+ if variant_size == Size::ZERO {
+ variant_size = upvars_size;
+ }
+ // We need to add the discriminant size back into min_size, since it is subtracted
+ // later during printing.
+ variant_size += match tag_encoding {
+ TagEncoding::Direct => tag.size(cx),
+ _ => Size::ZERO,
+ };
+
+ VariantInfo {
+ name: Some(Symbol::intern(&ty::GeneratorSubsts::variant_name(variant_idx))),
+ kind: SizeKind::Exact,
+ size: variant_size.bytes(),
+ align: variant_layout.align.abi.bytes(),
+ fields,
+ }
+ })
+ .collect();
+ (
+ variant_infos,
+ match tag_encoding {
+ TagEncoding::Direct => Some(tag.size(cx)),
+ _ => None,
+ },
+ )
+}
mod layout_sanity_check;
mod needs_drop;
pub mod representability;
+mod structural_match;
mod ty;
pub fn provide(providers: &mut Providers) {
representability::provide(providers);
ty::provide(providers);
instance::provide(providers);
+ structural_match::provide(providers);
}
--- /dev/null
+use rustc_hir::lang_items::LangItem;
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
+
+/// This method returns true if and only if `adt_ty` itself has been marked as
+/// eligible for structural-match: namely, if it implements both
+/// `StructuralPartialEq` and `StructuralEq` (which are respectively injected by
+/// `#[derive(PartialEq)]` and `#[derive(Eq)]`).
+///
+/// Note that this does *not* recursively check if the substructure of `adt_ty`
+/// implements the traits.
+fn has_structural_eq_impls<'tcx>(tcx: TyCtxt<'tcx>, adt_ty: Ty<'tcx>) -> bool {
+ let ref infcx = tcx.infer_ctxt().build();
+ let cause = ObligationCause::dummy();
+
+ let ocx = ObligationCtxt::new(infcx);
+ // require `#[derive(PartialEq)]`
+ let structural_peq_def_id =
+ infcx.tcx.require_lang_item(LangItem::StructuralPeq, Some(cause.span));
+ ocx.register_bound(cause.clone(), ty::ParamEnv::empty(), adt_ty, structural_peq_def_id);
+ // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around
+ // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.)
+ let structural_teq_def_id =
+ infcx.tcx.require_lang_item(LangItem::StructuralTeq, Some(cause.span));
+ ocx.register_bound(cause, ty::ParamEnv::empty(), adt_ty, structural_teq_def_id);
+
+ // We deliberately skip *reporting* fulfillment errors (via
+ // `report_fulfillment_errors`), for two reasons:
+ //
+ // 1. The error messages would mention `std::marker::StructuralPartialEq`
+ // (a trait which is solely meant as an implementation detail
+ // for now), and
+ //
+ // 2. We are sometimes doing future-incompatibility lints for
+ // now, so we do not want unconditional errors here.
+ ocx.select_all_or_error().is_empty()
+}
+
+pub fn provide(providers: &mut Providers) {
+ providers.has_structural_eq_impls = has_structural_eq_impls;
+}
pub mod codec;
pub mod sty;
+pub mod ty_info;
pub use codec::*;
pub use sty::*;
+pub use ty_info::*;
/// Needed so we can use #[derive(HashStable_Generic)]
pub trait HashStableContext {}
#![allow(rustc::usage_of_ty_tykind)]
-use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
+use std::cmp::Ordering;
use std::{fmt, hash};
use crate::DebruijnIndex;
impl<I: Interner> PartialEq for TyKind<I> {
#[inline]
fn eq(&self, other: &TyKind<I>) -> bool {
- let __self_vi = tykind_discriminant(self);
- let __arg_1_vi = tykind_discriminant(other);
- if __self_vi == __arg_1_vi {
- match (&*self, &*other) {
- (&Int(ref __self_0), &Int(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&Uint(ref __self_0), &Uint(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&Float(ref __self_0), &Float(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&Adt(ref __self_0, ref __self_1), &Adt(ref __arg_1_0, ref __arg_1_1)) => {
- __self_0 == __arg_1_0 && __self_1 == __arg_1_1
+ tykind_discriminant(self) == tykind_discriminant(other)
+ && match (self, other) {
+ (Int(a_i), Int(b_i)) => a_i == b_i,
+ (Uint(a_u), Uint(b_u)) => a_u == b_u,
+ (Float(a_f), Float(b_f)) => a_f == b_f,
+ (Adt(a_d, a_s), Adt(b_d, b_s)) => a_d == b_d && a_s == b_s,
+ (Foreign(a_d), Foreign(b_d)) => a_d == b_d,
+ (Array(a_t, a_c), Array(b_t, b_c)) => a_t == b_t && a_c == b_c,
+ (Slice(a_t), Slice(b_t)) => a_t == b_t,
+ (RawPtr(a_t), RawPtr(b_t)) => a_t == b_t,
+ (Ref(a_r, a_t, a_m), Ref(b_r, b_t, b_m)) => a_r == b_r && a_t == b_t && a_m == b_m,
+ (FnDef(a_d, a_s), FnDef(b_d, b_s)) => a_d == b_d && a_s == b_s,
+ (FnPtr(a_s), FnPtr(b_s)) => a_s == b_s,
+ (Dynamic(a_p, a_r, a_repr), Dynamic(b_p, b_r, b_repr)) => {
+ a_p == b_p && a_r == b_r && a_repr == b_repr
}
- (&Foreign(ref __self_0), &Foreign(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&Array(ref __self_0, ref __self_1), &Array(ref __arg_1_0, ref __arg_1_1)) => {
- __self_0 == __arg_1_0 && __self_1 == __arg_1_1
+ (Closure(a_d, a_s), Closure(b_d, b_s)) => a_d == b_d && a_s == b_s,
+ (Generator(a_d, a_s, a_m), Generator(b_d, b_s, b_m)) => {
+ a_d == b_d && a_s == b_s && a_m == b_m
}
- (&Slice(ref __self_0), &Slice(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&RawPtr(ref __self_0), &RawPtr(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (
- &Ref(ref __self_0, ref __self_1, ref __self_2),
- &Ref(ref __arg_1_0, ref __arg_1_1, ref __arg_1_2),
- ) => __self_0 == __arg_1_0 && __self_1 == __arg_1_1 && __self_2 == __arg_1_2,
- (&FnDef(ref __self_0, ref __self_1), &FnDef(ref __arg_1_0, ref __arg_1_1)) => {
- __self_0 == __arg_1_0 && __self_1 == __arg_1_1
+ (GeneratorWitness(a_g), GeneratorWitness(b_g)) => a_g == b_g,
+ (Tuple(a_t), Tuple(b_t)) => a_t == b_t,
+ (Projection(a_p), Projection(b_p)) => a_p == b_p,
+ (Opaque(a_d, a_s), Opaque(b_d, b_s)) => a_d == b_d && a_s == b_s,
+ (Param(a_p), Param(b_p)) => a_p == b_p,
+ (Bound(a_d, a_b), Bound(b_d, b_b)) => a_d == b_d && a_b == b_b,
+ (Placeholder(a_p), Placeholder(b_p)) => a_p == b_p,
+ (Infer(a_t), Infer(b_t)) => a_t == b_t,
+ (Error(a_e), Error(b_e)) => a_e == b_e,
+ (Bool, Bool) | (Char, Char) | (Str, Str) | (Never, Never) => true,
+ _ => {
+ debug_assert!(
+ false,
+ "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}"
+ );
+ true
}
- (&FnPtr(ref __self_0), &FnPtr(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (
- &Dynamic(ref __self_0, ref __self_1, ref self_repr),
- &Dynamic(ref __arg_1_0, ref __arg_1_1, ref arg_repr),
- ) => __self_0 == __arg_1_0 && __self_1 == __arg_1_1 && self_repr == arg_repr,
- (&Closure(ref __self_0, ref __self_1), &Closure(ref __arg_1_0, ref __arg_1_1)) => {
- __self_0 == __arg_1_0 && __self_1 == __arg_1_1
- }
- (
- &Generator(ref __self_0, ref __self_1, ref __self_2),
- &Generator(ref __arg_1_0, ref __arg_1_1, ref __arg_1_2),
- ) => __self_0 == __arg_1_0 && __self_1 == __arg_1_1 && __self_2 == __arg_1_2,
- (&GeneratorWitness(ref __self_0), &GeneratorWitness(ref __arg_1_0)) => {
- __self_0 == __arg_1_0
- }
- (&Tuple(ref __self_0), &Tuple(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&Projection(ref __self_0), &Projection(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&Opaque(ref __self_0, ref __self_1), &Opaque(ref __arg_1_0, ref __arg_1_1)) => {
- __self_0 == __arg_1_0 && __self_1 == __arg_1_1
- }
- (&Param(ref __self_0), &Param(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&Bound(ref __self_0, ref __self_1), &Bound(ref __arg_1_0, ref __arg_1_1)) => {
- __self_0 == __arg_1_0 && __self_1 == __arg_1_1
- }
- (&Placeholder(ref __self_0), &Placeholder(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&Infer(ref __self_0), &Infer(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&Error(ref __self_0), &Error(ref __arg_1_0)) => __self_0 == __arg_1_0,
- _ => true,
}
- } else {
- false
- }
}
}
impl<I: Interner> PartialOrd for TyKind<I> {
#[inline]
fn partial_cmp(&self, other: &TyKind<I>) -> Option<Ordering> {
- Some(Ord::cmp(self, other))
+ Some(self.cmp(other))
}
}
impl<I: Interner> Ord for TyKind<I> {
#[inline]
fn cmp(&self, other: &TyKind<I>) -> Ordering {
- let __self_vi = tykind_discriminant(self);
- let __arg_1_vi = tykind_discriminant(other);
- if __self_vi == __arg_1_vi {
- match (&*self, &*other) {
- (&Int(ref __self_0), &Int(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (&Uint(ref __self_0), &Uint(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (&Float(ref __self_0), &Float(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (&Adt(ref __self_0, ref __self_1), &Adt(ref __arg_1_0, ref __arg_1_1)) => {
- match Ord::cmp(__self_0, __arg_1_0) {
- Ordering::Equal => Ord::cmp(__self_1, __arg_1_1),
- cmp => cmp,
- }
- }
- (&Foreign(ref __self_0), &Foreign(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (&Array(ref __self_0, ref __self_1), &Array(ref __arg_1_0, ref __arg_1_1)) => {
- match Ord::cmp(__self_0, __arg_1_0) {
- Ordering::Equal => Ord::cmp(__self_1, __arg_1_1),
- cmp => cmp,
- }
- }
- (&Slice(ref __self_0), &Slice(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (&RawPtr(ref __self_0), &RawPtr(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (
- &Ref(ref __self_0, ref __self_1, ref __self_2),
- &Ref(ref __arg_1_0, ref __arg_1_1, ref __arg_1_2),
- ) => match Ord::cmp(__self_0, __arg_1_0) {
- Ordering::Equal => match Ord::cmp(__self_1, __arg_1_1) {
- Ordering::Equal => Ord::cmp(__self_2, __arg_1_2),
- cmp => cmp,
- },
- cmp => cmp,
- },
- (&FnDef(ref __self_0, ref __self_1), &FnDef(ref __arg_1_0, ref __arg_1_1)) => {
- match Ord::cmp(__self_0, __arg_1_0) {
- Ordering::Equal => Ord::cmp(__self_1, __arg_1_1),
- cmp => cmp,
- }
+ tykind_discriminant(self).cmp(&tykind_discriminant(other)).then_with(|| {
+ match (self, other) {
+ (Int(a_i), Int(b_i)) => a_i.cmp(b_i),
+ (Uint(a_u), Uint(b_u)) => a_u.cmp(b_u),
+ (Float(a_f), Float(b_f)) => a_f.cmp(b_f),
+ (Adt(a_d, a_s), Adt(b_d, b_s)) => a_d.cmp(b_d).then_with(|| a_s.cmp(b_s)),
+ (Foreign(a_d), Foreign(b_d)) => a_d.cmp(b_d),
+ (Array(a_t, a_c), Array(b_t, b_c)) => a_t.cmp(b_t).then_with(|| a_c.cmp(b_c)),
+ (Slice(a_t), Slice(b_t)) => a_t.cmp(b_t),
+ (RawPtr(a_t), RawPtr(b_t)) => a_t.cmp(b_t),
+ (Ref(a_r, a_t, a_m), Ref(b_r, b_t, b_m)) => {
+ a_r.cmp(b_r).then_with(|| a_t.cmp(b_t).then_with(|| a_m.cmp(b_m)))
}
- (&FnPtr(ref __self_0), &FnPtr(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (
- &Dynamic(ref __self_0, ref __self_1, ref self_repr),
- &Dynamic(ref __arg_1_0, ref __arg_1_1, ref arg_repr),
- ) => match Ord::cmp(__self_0, __arg_1_0) {
- Ordering::Equal => match Ord::cmp(__self_1, __arg_1_1) {
- Ordering::Equal => Ord::cmp(self_repr, arg_repr),
- cmp => cmp,
- },
- cmp => cmp,
- },
- (&Closure(ref __self_0, ref __self_1), &Closure(ref __arg_1_0, ref __arg_1_1)) => {
- match Ord::cmp(__self_0, __arg_1_0) {
- Ordering::Equal => Ord::cmp(__self_1, __arg_1_1),
- cmp => cmp,
- }
+ (FnDef(a_d, a_s), FnDef(b_d, b_s)) => a_d.cmp(b_d).then_with(|| a_s.cmp(b_s)),
+ (FnPtr(a_s), FnPtr(b_s)) => a_s.cmp(b_s),
+ (Dynamic(a_p, a_r, a_repr), Dynamic(b_p, b_r, b_repr)) => {
+ a_p.cmp(b_p).then_with(|| a_r.cmp(b_r).then_with(|| a_repr.cmp(b_repr)))
}
- (
- &Generator(ref __self_0, ref __self_1, ref __self_2),
- &Generator(ref __arg_1_0, ref __arg_1_1, ref __arg_1_2),
- ) => match Ord::cmp(__self_0, __arg_1_0) {
- Ordering::Equal => match Ord::cmp(__self_1, __arg_1_1) {
- Ordering::Equal => Ord::cmp(__self_2, __arg_1_2),
- cmp => cmp,
- },
- cmp => cmp,
- },
- (&GeneratorWitness(ref __self_0), &GeneratorWitness(ref __arg_1_0)) => {
- Ord::cmp(__self_0, __arg_1_0)
+ (Closure(a_p, a_s), Closure(b_p, b_s)) => a_p.cmp(b_p).then_with(|| a_s.cmp(b_s)),
+ (Generator(a_d, a_s, a_m), Generator(b_d, b_s, b_m)) => {
+ a_d.cmp(b_d).then_with(|| a_s.cmp(b_s).then_with(|| a_m.cmp(b_m)))
}
- (&Tuple(ref __self_0), &Tuple(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (&Projection(ref __self_0), &Projection(ref __arg_1_0)) => {
- Ord::cmp(__self_0, __arg_1_0)
+ (GeneratorWitness(a_g), GeneratorWitness(b_g)) => a_g.cmp(b_g),
+ (Tuple(a_t), Tuple(b_t)) => a_t.cmp(b_t),
+ (Projection(a_p), Projection(b_p)) => a_p.cmp(b_p),
+ (Opaque(a_d, a_s), Opaque(b_d, b_s)) => a_d.cmp(b_d).then_with(|| a_s.cmp(b_s)),
+ (Param(a_p), Param(b_p)) => a_p.cmp(b_p),
+ (Bound(a_d, a_b), Bound(b_d, b_b)) => a_d.cmp(b_d).then_with(|| a_b.cmp(b_b)),
+ (Placeholder(a_p), Placeholder(b_p)) => a_p.cmp(b_p),
+ (Infer(a_t), Infer(b_t)) => a_t.cmp(b_t),
+ (Error(a_e), Error(b_e)) => a_e.cmp(b_e),
+ (Bool, Bool) | (Char, Char) | (Str, Str) | (Never, Never) => Ordering::Equal,
+ _ => {
+ debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}");
+ Ordering::Equal
}
- (&Opaque(ref __self_0, ref __self_1), &Opaque(ref __arg_1_0, ref __arg_1_1)) => {
- match Ord::cmp(__self_0, __arg_1_0) {
- Ordering::Equal => Ord::cmp(__self_1, __arg_1_1),
- cmp => cmp,
- }
- }
- (&Param(ref __self_0), &Param(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (&Bound(ref __self_0, ref __self_1), &Bound(ref __arg_1_0, ref __arg_1_1)) => {
- match Ord::cmp(__self_0, __arg_1_0) {
- Ordering::Equal => Ord::cmp(__self_1, __arg_1_1),
- cmp => cmp,
- }
- }
- (&Placeholder(ref __self_0), &Placeholder(ref __arg_1_0)) => {
- Ord::cmp(__self_0, __arg_1_0)
- }
- (&Infer(ref __self_0), &Infer(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (&Error(ref __self_0), &Error(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- _ => Ordering::Equal,
}
- } else {
- Ord::cmp(&__self_vi, &__arg_1_vi)
- }
+ })
}
}
// This is manually implemented because a derive would require `I: Hash`
impl<I: Interner> hash::Hash for TyKind<I> {
fn hash<__H: hash::Hasher>(&self, state: &mut __H) -> () {
- match (&*self,) {
- (&Int(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Uint(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Float(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Adt(ref __self_0, ref __self_1),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state);
- hash::Hash::hash(__self_1, state)
- }
- (&Foreign(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Array(ref __self_0, ref __self_1),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state);
- hash::Hash::hash(__self_1, state)
- }
- (&Slice(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&RawPtr(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Ref(ref __self_0, ref __self_1, ref __self_2),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state);
- hash::Hash::hash(__self_1, state);
- hash::Hash::hash(__self_2, state)
- }
- (&FnDef(ref __self_0, ref __self_1),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state);
- hash::Hash::hash(__self_1, state)
- }
- (&FnPtr(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Dynamic(ref __self_0, ref __self_1, ref repr),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state);
- hash::Hash::hash(__self_1, state);
- hash::Hash::hash(repr, state)
- }
- (&Closure(ref __self_0, ref __self_1),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state);
- hash::Hash::hash(__self_1, state)
- }
- (&Generator(ref __self_0, ref __self_1, ref __self_2),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state);
- hash::Hash::hash(__self_1, state);
- hash::Hash::hash(__self_2, state)
- }
- (&GeneratorWitness(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Tuple(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Projection(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Opaque(ref __self_0, ref __self_1),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state);
- hash::Hash::hash(__self_1, state)
- }
- (&Param(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Bound(ref __self_0, ref __self_1),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state);
- hash::Hash::hash(__self_1, state)
- }
- (&Placeholder(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&Infer(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
+ tykind_discriminant(self).hash(state);
+ match self {
+ Int(i) => i.hash(state),
+ Uint(u) => u.hash(state),
+ Float(f) => f.hash(state),
+ Adt(d, s) => {
+ d.hash(state);
+ s.hash(state)
+ }
+ Foreign(d) => d.hash(state),
+ Array(t, c) => {
+ t.hash(state);
+ c.hash(state)
}
- (&Error(ref __self_0),) => {
- hash::Hash::hash(&tykind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
+ Slice(t) => t.hash(state),
+ RawPtr(t) => t.hash(state),
+ Ref(r, t, m) => {
+ r.hash(state);
+ t.hash(state);
+ m.hash(state)
+ }
+ FnDef(d, s) => {
+ d.hash(state);
+ s.hash(state)
+ }
+ FnPtr(s) => s.hash(state),
+ Dynamic(p, r, repr) => {
+ p.hash(state);
+ r.hash(state);
+ repr.hash(state)
+ }
+ Closure(d, s) => {
+ d.hash(state);
+ s.hash(state)
+ }
+ Generator(d, s, m) => {
+ d.hash(state);
+ s.hash(state);
+ m.hash(state)
+ }
+ GeneratorWitness(g) => g.hash(state),
+ Tuple(t) => t.hash(state),
+ Projection(p) => p.hash(state),
+ Opaque(d, s) => {
+ d.hash(state);
+ s.hash(state)
+ }
+ Param(p) => p.hash(state),
+ Bound(d, b) => {
+ d.hash(state);
+ b.hash(state)
}
- _ => hash::Hash::hash(&tykind_discriminant(self), state),
+ Placeholder(p) => p.hash(state),
+ Infer(t) => t.hash(state),
+ Error(e) => e.hash(state),
+ Bool | Char | Str | Never => (),
}
}
}
// This is manually implemented because a derive would require `I: Debug`
impl<I: Interner> fmt::Debug for TyKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use std::fmt::*;
match self {
- Bool => Formatter::write_str(f, "Bool"),
- Char => Formatter::write_str(f, "Char"),
- Int(f0) => Formatter::debug_tuple_field1_finish(f, "Int", f0),
- Uint(f0) => Formatter::debug_tuple_field1_finish(f, "Uint", f0),
- Float(f0) => Formatter::debug_tuple_field1_finish(f, "Float", f0),
- Adt(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Adt", f0, f1),
- Foreign(f0) => Formatter::debug_tuple_field1_finish(f, "Foreign", f0),
- Str => Formatter::write_str(f, "Str"),
- Array(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Array", f0, f1),
- Slice(f0) => Formatter::debug_tuple_field1_finish(f, "Slice", f0),
- RawPtr(f0) => Formatter::debug_tuple_field1_finish(f, "RawPtr", f0),
- Ref(f0, f1, f2) => Formatter::debug_tuple_field3_finish(f, "Ref", f0, f1, f2),
- FnDef(f0, f1) => Formatter::debug_tuple_field2_finish(f, "FnDef", f0, f1),
- FnPtr(f0) => Formatter::debug_tuple_field1_finish(f, "FnPtr", f0),
- Dynamic(f0, f1, f2) => Formatter::debug_tuple_field3_finish(f, "Dynamic", f0, f1, f2),
- Closure(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Closure", f0, f1),
- Generator(f0, f1, f2) => {
- Formatter::debug_tuple_field3_finish(f, "Generator", f0, f1, f2)
- }
- GeneratorWitness(f0) => Formatter::debug_tuple_field1_finish(f, "GeneratorWitness", f0),
- Never => Formatter::write_str(f, "Never"),
- Tuple(f0) => Formatter::debug_tuple_field1_finish(f, "Tuple", f0),
- Projection(f0) => Formatter::debug_tuple_field1_finish(f, "Projection", f0),
- Opaque(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Opaque", f0, f1),
- Param(f0) => Formatter::debug_tuple_field1_finish(f, "Param", f0),
- Bound(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Bound", f0, f1),
- Placeholder(f0) => Formatter::debug_tuple_field1_finish(f, "Placeholder", f0),
- Infer(f0) => Formatter::debug_tuple_field1_finish(f, "Infer", f0),
- TyKind::Error(f0) => Formatter::debug_tuple_field1_finish(f, "Error", f0),
+ Bool => f.write_str("Bool"),
+ Char => f.write_str("Char"),
+ Int(i) => f.debug_tuple_field1_finish("Int", i),
+ Uint(u) => f.debug_tuple_field1_finish("Uint", u),
+ Float(float) => f.debug_tuple_field1_finish("Float", float),
+ Adt(d, s) => f.debug_tuple_field2_finish("Adt", d, s),
+ Foreign(d) => f.debug_tuple_field1_finish("Foreign", d),
+ Str => f.write_str("Str"),
+ Array(t, c) => f.debug_tuple_field2_finish("Array", t, c),
+ Slice(t) => f.debug_tuple_field1_finish("Slice", t),
+ RawPtr(t) => f.debug_tuple_field1_finish("RawPtr", t),
+ Ref(r, t, m) => f.debug_tuple_field3_finish("Ref", r, t, m),
+ FnDef(d, s) => f.debug_tuple_field2_finish("FnDef", d, s),
+ FnPtr(s) => f.debug_tuple_field1_finish("FnPtr", s),
+ Dynamic(p, r, repr) => f.debug_tuple_field3_finish("Dynamic", p, r, repr),
+ Closure(d, s) => f.debug_tuple_field2_finish("Closure", d, s),
+ Generator(d, s, m) => f.debug_tuple_field3_finish("Generator", d, s, m),
+ GeneratorWitness(g) => f.debug_tuple_field1_finish("GeneratorWitness", g),
+ Never => f.write_str("Never"),
+ Tuple(t) => f.debug_tuple_field1_finish("Tuple", t),
+ Projection(p) => f.debug_tuple_field1_finish("Projection", p),
+ Opaque(d, s) => f.debug_tuple_field2_finish("Opaque", d, s),
+ Param(p) => f.debug_tuple_field1_finish("Param", p),
+ Bound(d, b) => f.debug_tuple_field2_finish("Bound", d, b),
+ Placeholder(p) => f.debug_tuple_field1_finish("Placeholder", p),
+ Infer(t) => f.debug_tuple_field1_finish("Infer", t),
+ TyKind::Error(e) => f.debug_tuple_field1_finish("Error", e),
}
}
}
impl<I: Interner> Clone for RegionKind<I> {
fn clone(&self) -> Self {
match self {
- ReEarlyBound(a) => ReEarlyBound(a.clone()),
- ReLateBound(a, b) => ReLateBound(a.clone(), b.clone()),
- ReFree(a) => ReFree(a.clone()),
+ ReEarlyBound(r) => ReEarlyBound(r.clone()),
+ ReLateBound(d, r) => ReLateBound(d.clone(), r.clone()),
+ ReFree(r) => ReFree(r.clone()),
ReStatic => ReStatic,
- ReVar(a) => ReVar(a.clone()),
- RePlaceholder(a) => RePlaceholder(a.clone()),
+ ReVar(r) => ReVar(r.clone()),
+ RePlaceholder(r) => RePlaceholder(r.clone()),
ReErased => ReErased,
}
}
impl<I: Interner> PartialEq for RegionKind<I> {
#[inline]
fn eq(&self, other: &RegionKind<I>) -> bool {
- let __self_vi = regionkind_discriminant(self);
- let __arg_1_vi = regionkind_discriminant(other);
- if __self_vi == __arg_1_vi {
- match (&*self, &*other) {
- (&ReEarlyBound(ref __self_0), &ReEarlyBound(ref __arg_1_0)) => {
- __self_0 == __arg_1_0
+ regionkind_discriminant(self) == regionkind_discriminant(other)
+ && match (self, other) {
+ (ReEarlyBound(a_r), ReEarlyBound(b_r)) => a_r == b_r,
+ (ReLateBound(a_d, a_r), ReLateBound(b_d, b_r)) => a_d == b_d && a_r == b_r,
+ (ReFree(a_r), ReFree(b_r)) => a_r == b_r,
+ (ReStatic, ReStatic) => true,
+ (ReVar(a_r), ReVar(b_r)) => a_r == b_r,
+ (RePlaceholder(a_r), RePlaceholder(b_r)) => a_r == b_r,
+ (ReErased, ReErased) => true,
+ _ => {
+ debug_assert!(
+ false,
+ "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}"
+ );
+ true
}
- (
- &ReLateBound(ref __self_0, ref __self_1),
- &ReLateBound(ref __arg_1_0, ref __arg_1_1),
- ) => __self_0 == __arg_1_0 && __self_1 == __arg_1_1,
- (&ReFree(ref __self_0), &ReFree(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&ReStatic, &ReStatic) => true,
- (&ReVar(ref __self_0), &ReVar(ref __arg_1_0)) => __self_0 == __arg_1_0,
- (&RePlaceholder(ref __self_0), &RePlaceholder(ref __arg_1_0)) => {
- __self_0 == __arg_1_0
- }
- (&ReErased, &ReErased) => true,
- _ => true,
}
- } else {
- false
- }
}
}
impl<I: Interner> PartialOrd for RegionKind<I> {
#[inline]
fn partial_cmp(&self, other: &RegionKind<I>) -> Option<Ordering> {
- Some(Ord::cmp(self, other))
+ Some(self.cmp(other))
}
}
impl<I: Interner> Ord for RegionKind<I> {
#[inline]
fn cmp(&self, other: &RegionKind<I>) -> Ordering {
- let __self_vi = regionkind_discriminant(self);
- let __arg_1_vi = regionkind_discriminant(other);
- if __self_vi == __arg_1_vi {
- match (&*self, &*other) {
- (&ReEarlyBound(ref __self_0), &ReEarlyBound(ref __arg_1_0)) => {
- Ord::cmp(__self_0, __arg_1_0)
+ regionkind_discriminant(self).cmp(®ionkind_discriminant(other)).then_with(|| {
+ match (self, other) {
+ (ReEarlyBound(a_r), ReEarlyBound(b_r)) => a_r.cmp(b_r),
+ (ReLateBound(a_d, a_r), ReLateBound(b_d, b_r)) => {
+ a_d.cmp(b_d).then_with(|| a_r.cmp(b_r))
}
- (
- &ReLateBound(ref __self_0, ref __self_1),
- &ReLateBound(ref __arg_1_0, ref __arg_1_1),
- ) => match Ord::cmp(__self_0, __arg_1_0) {
- Ordering::Equal => Ord::cmp(__self_1, __arg_1_1),
- cmp => cmp,
- },
- (&ReFree(ref __self_0), &ReFree(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (&ReStatic, &ReStatic) => Ordering::Equal,
- (&ReVar(ref __self_0), &ReVar(ref __arg_1_0)) => Ord::cmp(__self_0, __arg_1_0),
- (&RePlaceholder(ref __self_0), &RePlaceholder(ref __arg_1_0)) => {
- Ord::cmp(__self_0, __arg_1_0)
+ (ReFree(a_r), ReFree(b_r)) => a_r.cmp(b_r),
+ (ReStatic, ReStatic) => Ordering::Equal,
+ (ReVar(a_r), ReVar(b_r)) => a_r.cmp(b_r),
+ (RePlaceholder(a_r), RePlaceholder(b_r)) => a_r.cmp(b_r),
+ (ReErased, ReErased) => Ordering::Equal,
+ _ => {
+ debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}");
+ Ordering::Equal
}
- (&ReErased, &ReErased) => Ordering::Equal,
- _ => Ordering::Equal,
}
- } else {
- Ord::cmp(&__self_vi, &__arg_1_vi)
- }
+ })
}
}
// This is manually implemented because a derive would require `I: Hash`
impl<I: Interner> hash::Hash for RegionKind<I> {
- fn hash<__H: hash::Hasher>(&self, state: &mut __H) -> () {
- match (&*self,) {
- (&ReEarlyBound(ref __self_0),) => {
- hash::Hash::hash(®ionkind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&ReLateBound(ref __self_0, ref __self_1),) => {
- hash::Hash::hash(®ionkind_discriminant(self), state);
- hash::Hash::hash(__self_0, state);
- hash::Hash::hash(__self_1, state)
- }
- (&ReFree(ref __self_0),) => {
- hash::Hash::hash(®ionkind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&ReStatic,) => {
- hash::Hash::hash(®ionkind_discriminant(self), state);
- }
- (&ReVar(ref __self_0),) => {
- hash::Hash::hash(®ionkind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&RePlaceholder(ref __self_0),) => {
- hash::Hash::hash(®ionkind_discriminant(self), state);
- hash::Hash::hash(__self_0, state)
- }
- (&ReErased,) => {
- hash::Hash::hash(®ionkind_discriminant(self), state);
- }
+ fn hash<H: hash::Hasher>(&self, state: &mut H) -> () {
+ regionkind_discriminant(self).hash(state);
+ match self {
+ ReEarlyBound(r) => r.hash(state),
+ ReLateBound(d, r) => {
+ d.hash(state);
+ r.hash(state)
+ }
+ ReFree(r) => r.hash(state),
+ ReStatic => (),
+ ReVar(r) => r.hash(state),
+ RePlaceholder(r) => r.hash(state),
+ ReErased => (),
}
}
}
impl<I: Interner> fmt::Debug for RegionKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- ReEarlyBound(ref data) => write!(f, "ReEarlyBound({:?})", data),
+ ReEarlyBound(data) => write!(f, "ReEarlyBound({:?})", data),
- ReLateBound(binder_id, ref bound_region) => {
+ ReLateBound(binder_id, bound_region) => {
write!(f, "ReLateBound({:?}, {:?})", binder_id, bound_region)
}
- ReFree(ref fr) => fr.fmt(f),
+ ReFree(fr) => fr.fmt(f),
- ReStatic => write!(f, "ReStatic"),
+ ReStatic => f.write_str("ReStatic"),
- ReVar(ref vid) => vid.fmt(f),
+ ReVar(vid) => vid.fmt(f),
RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder),
- ReErased => write!(f, "ReErased"),
+ ReErased => f.write_str("ReErased"),
}
}
}
ReErased | ReStatic => {
// No variant fields to hash for these ...
}
- ReLateBound(db, br) => {
- db.hash_stable(hcx, hasher);
- br.hash_stable(hcx, hasher);
+ ReLateBound(d, r) => {
+ d.hash_stable(hcx, hasher);
+ r.hash_stable(hcx, hasher);
}
- ReEarlyBound(eb) => {
- eb.hash_stable(hcx, hasher);
+ ReEarlyBound(r) => {
+ r.hash_stable(hcx, hasher);
}
- ReFree(ref free_region) => {
- free_region.hash_stable(hcx, hasher);
+ ReFree(r) => {
+ r.hash_stable(hcx, hasher);
}
- RePlaceholder(p) => {
- p.hash_stable(hcx, hasher);
+ RePlaceholder(r) => {
+ r.hash_stable(hcx, hasher);
}
ReVar(_) => {
panic!("region variables should not be hashed: {self:?}")
--- /dev/null
+use std::{
+ cmp::Ordering,
+ hash::{Hash, Hasher},
+ ops::Deref,
+};
+
+use rustc_data_structures::{
+ fingerprint::Fingerprint,
+ stable_hasher::{HashStable, StableHasher},
+};
+
+use crate::{DebruijnIndex, TypeFlags};
+
+/// A helper type that you can wrap round your own type in order to automatically
+/// cache the stable hash, type flags and debruijn index on creation and
+/// not recompute it whenever the information is needed.
+/// This is only done in incremental mode. You can also opt out of caching by using
+/// StableHash::ZERO for the hash, in which case the hash gets computed each time.
+/// This is useful if you have values that you intern but never (can?) use for stable
+/// hashing.
+#[derive(Copy, Clone)]
+pub struct WithCachedTypeInfo<T> {
+ pub internee: T,
+ pub stable_hash: Fingerprint,
+
+ /// This field provides fast access to information that is also contained
+ /// in `kind`.
+ ///
+ /// This field shouldn't be used directly and may be removed in the future.
+ /// Use `Ty::flags()` instead.
+ pub flags: TypeFlags,
+
+ /// This field provides fast access to information that is also contained
+ /// in `kind`.
+ ///
+ /// This is a kind of confusing thing: it stores the smallest
+ /// binder such that
+ ///
+ /// (a) the binder itself captures nothing but
+ /// (b) all the late-bound things within the type are captured
+ /// by some sub-binder.
+ ///
+ /// So, for a type without any late-bound things, like `u32`, this
+ /// will be *innermost*, because that is the innermost binder that
+ /// captures nothing. But for a type `&'D u32`, where `'D` is a
+ /// late-bound region with De Bruijn index `D`, this would be `D + 1`
+ /// -- the binder itself does not capture `D`, but `D` is captured
+ /// by an inner binder.
+ ///
+ /// We call this concept an "exclusive" binder `D` because all
+ /// De Bruijn indices within the type are contained within `0..D`
+ /// (exclusive).
+ pub outer_exclusive_binder: DebruijnIndex,
+}
+
+impl<T: PartialEq> PartialEq for WithCachedTypeInfo<T> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.internee.eq(&other.internee)
+ }
+}
+
+impl<T: Eq> Eq for WithCachedTypeInfo<T> {}
+
+impl<T: Ord> PartialOrd for WithCachedTypeInfo<T> {
+ fn partial_cmp(&self, other: &WithCachedTypeInfo<T>) -> Option<Ordering> {
+ Some(self.internee.cmp(&other.internee))
+ }
+}
+
+impl<T: Ord> Ord for WithCachedTypeInfo<T> {
+ fn cmp(&self, other: &WithCachedTypeInfo<T>) -> Ordering {
+ self.internee.cmp(&other.internee)
+ }
+}
+
+impl<T> Deref for WithCachedTypeInfo<T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &T {
+ &self.internee
+ }
+}
+
+impl<T: Hash> Hash for WithCachedTypeInfo<T> {
+ #[inline]
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ if self.stable_hash != Fingerprint::ZERO {
+ self.stable_hash.hash(s)
+ } else {
+ self.internee.hash(s)
+ }
+ }
+}
+
+impl<T: HashStable<CTX>, CTX> HashStable<CTX> for WithCachedTypeInfo<T> {
+ fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
+ if self.stable_hash == Fingerprint::ZERO || cfg!(debug_assertions) {
+ // No cached hash available. This can only mean that incremental is disabled.
+ // We don't cache stable hashes in non-incremental mode, because they are used
+ // so rarely that the performance actually suffers.
+
+ // We need to build the hash as if we cached it and then hash that hash, as
+ // otherwise the hashes will differ between cached and non-cached mode.
+ let stable_hash: Fingerprint = {
+ let mut hasher = StableHasher::new();
+ self.internee.hash_stable(hcx, &mut hasher);
+ hasher.finish()
+ };
+ if cfg!(debug_assertions) && self.stable_hash != Fingerprint::ZERO {
+ assert_eq!(
+ stable_hash, self.stable_hash,
+ "cached stable hash does not match freshly computed stable hash"
+ );
+ }
+ stable_hash.hash_stable(hcx, hasher);
+ } else {
+ self.stable_hash.hash_stable(hcx, hasher);
+ }
+ }
+}
# Unless you're developing for a target where Rust CI doesn't build a compiler
# toolchain or changing LLVM locally, you probably want to set this to true.
#
-# This is false by default so that distributions don't unexpectedly download
-# LLVM from the internet.
-#
# All tier 1 targets are currently supported; set this to `"if-available"` if
# you are not sure whether you're on a tier 1 target.
#
#
# Note that many of the LLVM options are not currently supported for
# downloading. Currently only the "assertions" option can be toggled.
-#download-ci-llvm = false
+#
+# Defaults to "if-available" when `channel = "dev"` and "false" otherwise.
+#download-ci-llvm = "if-available"
# Indicates whether LLVM rebuild should be skipped when running bootstrap. If
# this is `false` then the compiler's LLVM will be rebuilt whenever the built
# Defaults to the Python interpreter used to execute x.py
#python = "python"
+# The path to the REUSE executable to use. Note that REUSE is not required in
+# most cases, as our tooling relies on a cached (and shrinked) copy of the
+# REUSE output present in the git repository and in our source tarballs.
+#
+# REUSE is only needed if your changes caused the overral licensing of the
+# repository to change, and the cached copy has to be regenerated.
+#
+# Defaults to the "reuse" command in the system path.
+#reuse = "reuse"
+
# Force Cargo to check that Cargo.lock describes the precise dependency
# set that all the Cargo.toml files create, instead of updating it.
#locked-deps = false
pub(super) fn new(inner: VecDeque<T, A>) -> Self {
IntoIter { inner }
}
+
+ pub(super) fn into_vecdeque(self) -> VecDeque<T, A> {
+ self.inner
+ }
}
#[stable(feature = "collection_debug", since = "1.17.0")]
mod spec_extend;
+use self::spec_from_iter::SpecFromIter;
+
+mod spec_from_iter;
+
#[cfg(test)]
mod tests;
///
/// let deque: VecDeque<u32> = VecDeque::new();
/// ```
- // FIXME: This should probably be const
#[inline]
#[unstable(feature = "allocator_api", issue = "32838")]
- pub fn new_in(alloc: A) -> VecDeque<T, A> {
+ pub const fn new_in(alloc: A) -> VecDeque<T, A> {
VecDeque { head: 0, len: 0, buf: RawVec::new_in(alloc) }
}
VecDeque { head: 0, len: 0, buf: RawVec::with_capacity_in(capacity, alloc) }
}
+ /// Creates a `VecDeque` from a raw allocation, when the initialized
+ /// part of that allocation forms a *contiguous* subslice thereof.
+ ///
+ /// For use by `vec::IntoIter::into_vecdeque`
+ ///
+ /// # Safety
+ ///
+ /// All the usual requirements on the allocated memory like in
+ /// `Vec::from_raw_parts_in`, but takes a *range* of elements that are
+ /// initialized rather than only supporting `0..len`. Requires that
+ /// `initialized.start` ≤ `initialized.end` ≤ `capacity`.
+ #[inline]
+ pub(crate) unsafe fn from_contiguous_raw_parts_in(
+ ptr: *mut T,
+ initialized: Range<usize>,
+ capacity: usize,
+ alloc: A,
+ ) -> Self {
+ debug_assert!(initialized.start <= initialized.end);
+ debug_assert!(initialized.end <= capacity);
+
+ // SAFETY: Our safety precondition guarantees the range length won't wrap,
+ // and that the allocation is valid for use in `RawVec`.
+ unsafe {
+ VecDeque {
+ head: initialized.start,
+ len: initialized.end.unchecked_sub(initialized.start),
+ buf: RawVec::from_raw_parts_in(ptr, capacity, alloc),
+ }
+ }
+ }
+
/// Provides a reference to the element at the given index.
///
/// Element at index 0 is the front of the queue.
self.head = tail;
} else {
- // ´free` is smaller than both `head_len` and `tail_len`.
+ // `free` is smaller than both `head_len` and `tail_len`.
// the general algorithm for this first moves the slices
// right next to each other and then uses `slice::rotate`
// to rotate them into place:
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> FromIterator<T> for VecDeque<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> VecDeque<T> {
- let iterator = iter.into_iter();
- let (lower, _) = iterator.size_hint();
- let mut deq = VecDeque::with_capacity(lower);
- deq.extend(iterator);
- deq
+ SpecFromIter::spec_from_iter(iter.into_iter())
}
}
/// In its current implementation, this is a very cheap
/// conversion. This isn't yet a guarantee though, and
/// shouldn't be relied on.
+ #[inline]
fn from(other: Vec<T, A>) -> Self {
let (ptr, len, cap, alloc) = other.into_raw_parts_with_alloc();
Self { head: 0, len, buf: unsafe { RawVec::from_raw_parts_in(ptr, cap, alloc) } }
--- /dev/null
+use super::{IntoIter, VecDeque};
+
+/// Specialization trait used for `VecDeque::from_iter`
+pub(super) trait SpecFromIter<T, I> {
+ fn spec_from_iter(iter: I) -> Self;
+}
+
+impl<T, I> SpecFromIter<T, I> for VecDeque<T>
+where
+ I: Iterator<Item = T>,
+{
+ default fn spec_from_iter(iterator: I) -> Self {
+ // Since converting is O(1) now, just re-use the `Vec` logic for
+ // anything where we can't do something extra-special for `VecDeque`,
+ // especially as that could save us some monomorphiziation work
+ // if one uses the same iterators (like slice ones) with both.
+ crate::vec::Vec::from_iter(iterator).into()
+ }
+}
+
+impl<T> SpecFromIter<T, crate::vec::IntoIter<T>> for VecDeque<T> {
+ #[inline]
+ fn spec_from_iter(iterator: crate::vec::IntoIter<T>) -> Self {
+ iterator.into_vecdeque()
+ }
+}
+
+impl<T> SpecFromIter<T, IntoIter<T>> for VecDeque<T> {
+ #[inline]
+ fn spec_from_iter(iterator: IntoIter<T>) -> Self {
+ iterator.into_vecdeque()
+ }
+}
#[cfg(not(no_global_oom_handling))]
use super::AsVecIntoIter;
use crate::alloc::{Allocator, Global};
+#[cfg(not(no_global_oom_handling))]
+use crate::collections::VecDeque;
use crate::raw_vec::RawVec;
use core::array;
use core::fmt;
pub(crate) fn forget_remaining_elements(&mut self) {
self.ptr = self.end;
}
+
+ #[cfg(not(no_global_oom_handling))]
+ #[inline]
+ pub(crate) fn into_vecdeque(self) -> VecDeque<T, A> {
+ // Keep our `Drop` impl from dropping the elements and the allocator
+ let mut this = ManuallyDrop::new(self);
+
+ // SAFETY: This allocation originally came from a `Vec`, so it passes
+ // all those checks. We have `this.buf` ≤ `this.ptr` ≤ `this.end`,
+ // so the `sub_ptr`s below cannot wrap, and will produce a well-formed
+ // range. `end` ≤ `buf + cap`, so the range will be in-bounds.
+ // Taking `alloc` is ok because nothing else is going to look at it,
+ // since our `Drop` impl isn't going to run so there's no more code.
+ unsafe {
+ let buf = this.buf.as_ptr();
+ let initialized = if T::IS_ZST {
+ // All the pointers are the same for ZSTs, so it's fine to
+ // say that they're all at the beginning of the "allocation".
+ 0..this.len()
+ } else {
+ this.ptr.sub_ptr(buf)..this.end.sub_ptr(buf)
+ };
+ let cap = this.cap;
+ let alloc = ManuallyDrop::take(&mut this.alloc);
+ VecDeque::from_contiguous_raw_parts_in(buf, initialized, cap, alloc)
+ }
+ }
}
#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")]
/// Converts the vector into [`Box<[T]>`][owned slice].
///
- /// Note that this will drop any excess capacity.
+ /// If the vector has excess capacity, its items will be moved into a
+ /// newly-allocated buffer with exactly the right capacity.
///
/// [owned slice]: Box
///
/// ```
/// assert_eq!(Box::from(vec![1, 2, 3]), vec![1, 2, 3].into_boxed_slice());
/// ```
+ ///
+ /// Any excess capacity is removed:
+ /// ```
+ /// let mut vec = Vec::with_capacity(10);
+ /// vec.extend([1, 2, 3]);
+ ///
+ /// assert_eq!(Box::from(vec), vec![1, 2, 3].into_boxed_slice());
+ /// ```
fn from(v: Vec<T, A>) -> Self {
v.into_boxed_slice()
}
d.resize(1, v);
assert_eq!(d[0].capacity(), 1234);
}
+
+#[test]
+fn test_collect_from_into_iter_keeps_allocation() {
+ let mut v = Vec::with_capacity(13);
+ v.extend(0..7);
+ check(v.as_ptr(), v.last().unwrap(), v.into_iter());
+
+ let mut v = VecDeque::with_capacity(13);
+ v.extend(0..7);
+ check(&v[0], &v[v.len() - 1], v.into_iter());
+
+ fn check(buf: *const i32, last: *const i32, mut it: impl Iterator<Item = i32>) {
+ assert_eq!(it.next(), Some(0));
+ assert_eq!(it.next(), Some(1));
+
+ let mut v: VecDeque<i32> = it.collect();
+ assert_eq!(v.capacity(), 13);
+ assert_eq!(v.as_slices().0.as_ptr(), buf.wrapping_add(2));
+ assert_eq!(&v[v.len() - 1] as *const _, last);
+
+ assert_eq!(v.as_slices(), ([2, 3, 4, 5, 6].as_slice(), [].as_slice()));
+ v.push_front(7);
+ assert_eq!(v.as_slices(), ([7, 2, 3, 4, 5, 6].as_slice(), [].as_slice()));
+ v.push_front(8);
+ assert_eq!(v.as_slices(), ([8, 7, 2, 3, 4, 5, 6].as_slice(), [].as_slice()));
+
+ // Now that we've adding thing in place of the two that we removed from
+ // the front of the iterator, we're back to matching the buffer pointer.
+ assert_eq!(v.as_slices().0.as_ptr(), buf);
+ assert_eq!(&v[v.len() - 1] as *const _, last);
+
+ v.push_front(9);
+ assert_eq!(v.as_slices(), ([9].as_slice(), [8, 7, 2, 3, 4, 5, 6].as_slice()));
+ assert_eq!(v.capacity(), 13);
+ }
+}
/// are implemented in `traits::SelectionContext::copy_clone_conditions()`
/// in `rustc_trait_selection`.
mod impls {
-
use super::Clone;
macro_rules! impl_clone {
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_clone", issue = "91805")]
impl const Clone for $t {
- #[inline]
+ #[inline(always)]
fn clone(&self) -> Self {
*self
}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_clone", issue = "91805")]
impl<T: ?Sized> const Clone for *const T {
- #[inline]
+ #[inline(always)]
fn clone(&self) -> Self {
*self
}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_clone", issue = "91805")]
impl<T: ?Sized> const Clone for *mut T {
- #[inline]
+ #[inline(always)]
fn clone(&self) -> Self {
*self
}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_clone", issue = "91805")]
impl<T: ?Sized> const Clone for &T {
- #[inline]
+ #[inline(always)]
#[rustc_diagnostic_item = "noop_method_clone"]
fn clone(&self) -> Self {
*self
use self::Ordering::*;
-/// Trait for equality comparisons which are [partial equivalence
-/// relations](https://en.wikipedia.org/wiki/Partial_equivalence_relation).
+/// Trait for equality comparisons.
///
/// `x.eq(y)` can also be written `x == y`, and `x.ne(y)` can be written `x != y`.
/// We use the easier-to-read infix notation in the remainder of this documentation.
/// This trait allows for partial equality, for types that do not have a full
/// equivalence relation. For example, in floating point numbers `NaN != NaN`,
/// so floating point types implement `PartialEq` but not [`trait@Eq`].
+/// Formally speaking, when `Rhs == Self`, this trait corresponds to a [partial equivalence
+/// relation](https://en.wikipedia.org/wiki/Partial_equivalence_relation).
///
/// Implementations must ensure that `eq` and `ne` are consistent with each other:
///
/// ```
#[stable(feature = "convert_id", since = "1.33.0")]
#[rustc_const_stable(feature = "const_identity", since = "1.33.0")]
-#[inline]
+#[inline(always)]
pub const fn identity<T>(x: T) -> T {
x
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsRef<[T]> for [T] {
+ #[inline(always)]
fn as_ref(&self) -> &[T] {
self
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsMut<[T]> for [T] {
+ #[inline(always)]
fn as_mut(&mut self) -> &mut [T] {
self
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<str> for str {
- #[inline]
+ #[inline(always)]
fn as_ref(&self) -> &str {
self
}
#[stable(feature = "as_mut_str_for_str", since = "1.51.0")]
impl AsMut<str> for str {
- #[inline]
+ #[inline(always)]
fn as_mut(&mut self) -> &mut str {
self
}
// Rustdocs on the impl block show a "[+] show undocumented items" toggle.
// Rustdocs on functions do not.
#[doc = $doc]
- #[inline]
+ #[inline(always)]
fn from(small: $Small) -> Self {
small as Self
}
/// non-Send/Sync as well, and we don't want that.
///
/// It also simplifies the HIR lowering of `.await`.
-#[cfg_attr(not(bootstrap), lang = "ResumeTy")]
+// FIXME(swatinem): This type can be removed when bumping the bootstrap compiler
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[derive(Debug, Copy, Clone)]
/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give
/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`).
// This is `const` to avoid extra errors after we recover from `const async fn`
+// FIXME(swatinem): This fn can be removed when bumping the bootstrap compiler
#[cfg_attr(bootstrap, lang = "from_generator")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
GenFuture(gen)
}
-#[lang = "get_context"]
+// FIXME(swatinem): This fn can be removed when bumping the bootstrap compiler
+#[cfg_attr(bootstrap, lang = "get_context")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[must_use]
unsafe { &mut *cx.0.as_ptr().cast() }
}
+// FIXME(swatinem): This fn is currently needed to work around shortcomings
+// in type and lifetime inference.
+// See the comment at the bottom of `LoweringContext::make_async_expr` and
+// <https://github.com/rust-lang/rust/issues/104826>.
#[cfg_attr(not(bootstrap), lang = "identity_future")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
/// ```
///
/// [`thread::yield_now`]: ../../std/thread/fn.yield_now.html
-#[inline]
+#[inline(always)]
#[stable(feature = "renamed_spin_loop", since = "1.49.0")]
pub fn spin_loop() {
#[cfg(target_arch = "x86")]
#[unstable(feature = "hint_must_use", issue = "94745")]
#[rustc_const_unstable(feature = "hint_must_use", issue = "94745")]
#[must_use] // <-- :)
+#[inline(always)]
pub const fn must_use<T>(value: T) -> T {
value
}
/// #![feature(iter_repeat_n)]
/// use std::iter;
///
-/// // four of the the number four:
+/// // four of the number four:
/// let mut four_fours = iter::repeat_n(4, 4);
///
/// assert_eq!(Some(4), four_fours.next());
/// [`sum()`]: Iterator::sum
/// [`FromIterator`]: iter::FromIterator
#[stable(feature = "iter_arith_traits", since = "1.12.0")]
+#[rustc_on_unimplemented(
+ message = "a value of type `{Self}` cannot be made by summing an iterator over elements of type `{A}`",
+ label = "value of type `{Self}` cannot be made by summing a `std::iter::Iterator<Item={A}>`"
+)]
pub trait Sum<A = Self>: Sized {
/// Method which takes an iterator and generates `Self` from the elements by
/// "summing up" the items.
/// [`product()`]: Iterator::product
/// [`FromIterator`]: iter::FromIterator
#[stable(feature = "iter_arith_traits", since = "1.12.0")]
+#[rustc_on_unimplemented(
+ message = "a value of type `{Self}` cannot be made by multiplying all elements of type `{A}` from an iterator",
+ label = "value of type `{Self}` cannot be made by multiplying all elements from a `std::iter::Iterator<Item={A}>`"
+)]
pub trait Product<A = Self>: Sized {
/// Method which takes an iterator and generates `Self` from the elements by
/// multiplying the items.
/* compiler built-in */
}
+ /// Unstable placeholder for type ascription.
+ #[rustc_builtin_macro]
+ #[unstable(
+ feature = "type_ascription",
+ issue = "23416",
+ reason = "placeholder syntax for type ascription"
+ )]
+ #[cfg(not(bootstrap))]
+ pub macro type_ascribe($expr:expr, $ty:ty) {
+ /* compiler built-in */
+ }
+
/// Unstable implementation detail of the `rustc` compiler, do not use.
#[rustc_builtin_macro]
#[stable(feature = "rust1", since = "1.0.0")]
use crate::ascii;
use crate::convert::TryInto;
-use crate::error::Error;
use crate::intrinsics;
use crate::mem;
use crate::ops::{Add, Mul, Sub};
use crate::str::FromStr;
+#[cfg(not(no_fp_fmt_parse))]
+use crate::error::Error;
+
// Used because the `?` operator is not allowed in a const context.
macro_rules! try_opt {
($e:expr) => {
/// # Examples
///
/// ```
- /// #![feature(nonzero_bits)]
#[doc = concat!("# use std::num::", stringify!($Ty), ";")]
///
#[doc = concat!("assert_eq!(", stringify!($Ty), "::BITS, ", stringify!($Int), "::BITS);")]
/// ```
- #[unstable(feature = "nonzero_bits", issue = "94881")]
+ #[stable(feature = "nonzero_bits", since = "CURRENT_RUSTC_VERSION")]
pub const BITS: u32 = <$Int>::BITS;
}
)+
//! should have some resemblance to multiplication (and share expected
//! properties like associativity).
//!
-//! Note that the `&&` and `||` operators short-circuit, i.e., they only
-//! evaluate their second operand if it contributes to the result. Since this
-//! behavior is not enforceable by traits, `&&` and `||` are not supported as
-//! overloadable operators.
+//! Note that the `&&` and `||` operators are currently not supported for
+//! overloading. Due to their short circuiting nature, they require a different
+//! design from traits for other operators like [`BitAnd`]. Designs for them are
+//! under discussion.
//!
//! Many of the operators take their operands by value. In non-generic
//! contexts involving built-in types, this is usually not a problem.
/// site as much as possible (so that `panic!()` has as low an impact
/// on (e.g.) the inlining of other functions as possible), by moving
/// the actual formatting into this shared place.
-#[cold]
// If panic_immediate_abort, inline the abort call,
// otherwise avoid inlining because of it is cold path.
-#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[lang = "panic_fmt"] // needed for const-evaluated panics
/// Like panic_fmt, but without unwinding and track_caller to reduce the impact on codesize.
/// Also just works on `str`, as a `fmt::Arguments` needs more space to be passed.
-#[cold]
-#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[rustc_nounwind]
pub fn panic_str_nounwind(msg: &'static str) -> ! {
// above.
/// The underlying implementation of libcore's `panic!` macro when no formatting is used.
-#[cold]
// never inline unless panic_immediate_abort to avoid code
// bloat at the call sites as much as possible
-#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[rustc_const_unstable(feature = "core_panic", issue = "none")]
panic_fmt(format_args!("{}", *x));
}
-#[cold]
-#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
+#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access
fn panic_bounds_check(index: usize, len: usize) -> ! {
///
/// This function is called directly by the codegen backend, and must not have
/// any extra arguments (including those synthesized by track_caller).
-#[cold]
-#[inline(never)]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
+#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function
#[rustc_nounwind]
fn panic_no_unwind() -> ! {
}
/// Internal function for `assert_eq!` and `assert_ne!` macros
-#[cold]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
+#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[doc(hidden)]
pub fn assert_failed<T, U>(
}
/// Internal function for `assert_match!`
-#[cold]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
+#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[doc(hidden)]
pub fn assert_matches_failed<T: fmt::Debug + ?Sized>(
}
/// Non-generic version of the above functions, to avoid code bloat.
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
+#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
fn assert_failed_inner(
kind: AssertKind,
reason = "`cfg_eval` is a recently implemented feature"
)]
pub use crate::macros::builtin::cfg_eval;
+
+#[unstable(
+ feature = "type_ascription",
+ issue = "23416",
+ reason = "placeholder syntax for type ascription"
+)]
+#[cfg(not(bootstrap))]
+pub use crate::macros::builtin::type_ascribe;
/// Casts to a pointer of another type.
#[stable(feature = "ptr_cast", since = "1.38.0")]
#[rustc_const_stable(feature = "const_ptr_cast", since = "1.38.0")]
- #[inline]
+ #[inline(always)]
pub const fn cast<U>(self) -> *const U {
self as _
}
/// refactored.
#[stable(feature = "ptr_const_cast", since = "1.65.0")]
#[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")]
+ #[inline(always)]
pub const fn cast_mut(self) -> *mut T {
self as _
}
note = "replaced by the `exposed_addr` method, or update your code \
to follow the strict provenance rules using its APIs"
)]
+ #[inline(always)]
pub fn to_bits(self) -> usize
where
T: Sized,
your code to follow the strict provenance rules using its APIs"
)]
#[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function
+ #[inline(always)]
pub fn from_bits(bits: usize) -> Self
where
T: Sized,
/// might change in the future (including possibly weakening this so it becomes wholly
/// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details.
#[must_use]
- #[inline]
+ #[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
pub fn addr(self) -> usize
where
///
/// [`from_exposed_addr`]: from_exposed_addr
#[must_use]
- #[inline]
+ #[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
pub fn expose_addr(self) -> usize
where
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
- #[inline]
+ #[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub const unsafe fn sub(self, count: usize) -> Self
where
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_sub(self, count: usize) -> Self
where
T: Sized,
/// This API and its claimed semantics are part of the Strict Provenance experiment, see the
/// [module documentation][crate::ptr] for details.
#[must_use]
-#[inline]
+#[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[allow(fuzzy_provenance_casts)] // this *is* the strict provenance API one should use instead
/// This API and its claimed semantics are part of the Strict Provenance experiment, see the
/// [module documentation][crate::ptr] for details.
#[must_use]
-#[inline]
+#[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[allow(fuzzy_provenance_casts)] // this *is* the strict provenance API one should use instead
/// (which is what the `PartialEq for &T` implementation does).
///
/// When comparing wide pointers, both the address and the metadata are tested for equality.
-/// However, note that comparing trait object pointers (`*const dyn Trait`) is unrealiable: pointers
+/// However, note that comparing trait object pointers (`*const dyn Trait`) is unreliable: pointers
/// to values of the same underlying type can compare inequal (because vtables are duplicated in
/// multiple codegen units), and pointers to values of *different* underlying type can compare equal
/// (since identical vtables can be deduplicated within a codegen unit).
/// assert!(!std::ptr::eq(&a[0..2], &a[1..3]));
/// ```
#[stable(feature = "ptr_eq", since = "1.17.0")]
-#[inline]
+#[inline(always)]
pub fn eq<T: ?Sized>(a: *const T, b: *const T) -> bool {
a == b
}
/// [`cast_mut`]: #method.cast_mut
#[stable(feature = "ptr_const_cast", since = "1.65.0")]
#[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")]
+ #[inline(always)]
pub const fn cast_const(self) -> *const T {
self as _
}
note = "replaced by the `exposed_addr` method, or update your code \
to follow the strict provenance rules using its APIs"
)]
+ #[inline(always)]
pub fn to_bits(self) -> usize
where
T: Sized,
update your code to follow the strict provenance rules using its APIs"
)]
#[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function
+ #[inline(always)]
pub fn from_bits(bits: usize) -> Self
where
T: Sized,
/// might change in the future (including possibly weakening this so it becomes wholly
/// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details.
#[must_use]
- #[inline]
+ #[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
pub fn addr(self) -> usize
where
///
/// [`from_exposed_addr_mut`]: from_exposed_addr_mut
#[must_use]
- #[inline]
+ #[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
pub fn expose_addr(self) -> usize
where
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
- #[inline]
+ #[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub const unsafe fn sub(self, count: usize) -> Self
where
#[stable(feature = "pointer_methods", since = "1.26.0")]
#[must_use = "returns a new pointer rather than modifying its argument"]
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
- #[inline]
+ #[inline(always)]
pub const fn wrapping_sub(self, count: usize) -> Self
where
T: Sized,
#[stable(feature = "nonnull", since = "1.25.0")]
#[rustc_const_stable(feature = "const_nonnull_as_ptr", since = "1.32.0")]
#[must_use]
- #[inline]
+ #[inline(always)]
pub const fn as_ptr(self) -> *mut T {
self.pointer as *mut T
}
#[stable(feature = "nonnull", since = "1.25.0")]
#[rustc_const_unstable(feature = "const_ptr_as_ref", issue = "91822")]
#[must_use]
- #[inline]
+ #[inline(always)]
pub const unsafe fn as_ref<'a>(&self) -> &'a T {
// SAFETY: the caller must guarantee that `self` meets all the
// requirements for a reference.
#[stable(feature = "nonnull", since = "1.25.0")]
#[rustc_const_unstable(feature = "const_ptr_as_ref", issue = "91822")]
#[must_use]
- #[inline]
+ #[inline(always)]
pub const unsafe fn as_mut<'a>(&mut self) -> &'a mut T {
// SAFETY: the caller must guarantee that `self` meets all the
// requirements for a mutable reference.
#[stable(feature = "nonnull", since = "1.25.0")]
#[rustc_const_unstable(feature = "const_clone", issue = "91805")]
impl<T: ?Sized> const Clone for NonNull<T> {
- #[inline]
+ #[inline(always)]
fn clone(&self) -> Self {
*self
}
}
}
-#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
-#[cold]
#[track_caller]
#[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
const fn slice_start_index_len_fail(index: usize, len: usize) -> ! {
}
// FIXME const-hack
+#[inline]
#[track_caller]
fn slice_start_index_len_fail_rt(index: usize, len: usize) -> ! {
panic!("range start index {index} out of range for slice of length {len}");
}
+#[inline]
#[track_caller]
const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! {
panic!("slice start index is out of range for slice");
}
-#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
-#[cold]
#[track_caller]
#[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
const fn slice_end_index_len_fail(index: usize, len: usize) -> ! {
}
// FIXME const-hack
+#[inline]
#[track_caller]
fn slice_end_index_len_fail_rt(index: usize, len: usize) -> ! {
panic!("range end index {index} out of range for slice of length {len}");
}
+#[inline]
#[track_caller]
const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! {
panic!("slice end index is out of range for slice");
}
-#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
-#[cold]
#[track_caller]
#[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
const fn slice_index_order_fail(index: usize, end: usize) -> ! {
}
// FIXME const-hack
+#[inline]
#[track_caller]
fn slice_index_order_fail_rt(index: usize, end: usize) -> ! {
panic!("slice index starts at {index} but ends at {end}");
}
+#[inline]
#[track_caller]
const fn slice_index_order_fail_ct(_: usize, _: usize) -> ! {
panic!("slice index start is larger than end");
}
-#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
-#[cold]
#[track_caller]
const fn slice_start_index_overflow_fail() -> ! {
panic!("attempted to index slice from after maximum usize");
}
-#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
-#[cold]
#[track_caller]
const fn slice_end_index_overflow_fail() -> ! {
panic!("attempted to index slice up to maximum usize");
/// [`as_mut_ptr`]: slice::as_mut_ptr
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")]
- #[inline]
+ #[inline(always)]
#[must_use]
pub const fn as_ptr(&self) -> *const T {
self as *const [T] as *const T
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
#[rustc_allow_const_fn_unstable(const_mut_refs)]
- #[inline]
+ #[inline(always)]
#[must_use]
pub const fn as_mut_ptr(&mut self) -> *mut T {
self as *mut [T] as *mut T
/// maintained.
///
/// This method splits the slice into three distinct slices: prefix, correctly aligned middle
- /// slice of a new type, and the suffix slice. The method may make the middle slice the greatest
- /// length possible for a given type and input slice, but only your algorithm's performance
- /// should depend on that, not its correctness. It is permissible for all of the input data to
- /// be returned as the prefix or suffix slice.
+ /// slice of a new type, and the suffix slice. How exactly the slice is split up is not
+ /// specified; the middle part may be smaller than necessary. However, if this fails to return a
+ /// maximal middle part, that is because code is running in a context where performance does not
+ /// matter, such as a sanitizer attempting to find alignment bugs. Regular code running
+ /// in a default (debug or release) execution *will* return a maximal middle part.
///
/// This method has no purpose when either input element `T` or output element `U` are
/// zero-sized and will return the original slice without splitting anything.
/// types is maintained.
///
/// This method splits the slice into three distinct slices: prefix, correctly aligned middle
- /// slice of a new type, and the suffix slice. The method may make the middle slice the greatest
- /// length possible for a given type and input slice, but only your algorithm's performance
- /// should depend on that, not its correctness. It is permissible for all of the input data to
- /// be returned as the prefix or suffix slice.
+ /// slice of a new type, and the suffix slice. How exactly the slice is split up is not
+ /// specified; the middle part may be smaller than necessary. However, if this fails to return a
+ /// maximal middle part, that is because code is running in a context where performance does not
+ /// matter, such as a sanitizer attempting to find alignment bugs. Regular code running
+ /// in a default (debug or release) execution *will* return a maximal middle part.
///
/// This method has no purpose when either input element `T` or output element `U` are
/// zero-sized and will return the original slice without splitting anything.
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "rustc_str_as_ptr", since = "1.32.0")]
#[must_use]
- #[inline]
+ #[inline(always)]
pub const fn as_ptr(&self) -> *const u8 {
self as *const str as *const u8
}
/// modified in a way that it remains valid UTF-8.
#[stable(feature = "str_as_mut_ptr", since = "1.36.0")]
#[must_use]
- #[inline]
+ #[inline(always)]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self as *mut str as *mut u8
}
// Thus, derefencing both `px` and `py` in the loop below is safe.
//
// Moreover, we set `pxend` and `pyend` to be 4 bytes before the actual
- // end of of `px` and `py`. Thus, the final dereference outside of the
+ // end of `px` and `py`. Thus, the final dereference outside of the
// loop is guaranteed to be valid. (The final comparison will overlap with
// the last comparison done in the loop for lengths that aren't multiples
// of four.)
/// Currently, `Context` only serves to provide access to a [`&Waker`](Waker)
/// which can be used to wake the current task.
#[stable(feature = "futures_api", since = "1.36.0")]
+#[cfg_attr(not(bootstrap), lang = "Context")]
pub struct Context<'a> {
waker: &'a Waker,
// Ensure we future-proof against variance changes by forcing
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core" }
-libc = { version = "0.2.135", default-features = false, features = ['rustc-dep-of-std'] }
-compiler_builtins = { version = "0.1.82" }
+libc = { version = "0.2.138", default-features = false, features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "0.1.85" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
hashbrown = { version = "0.12", default-features = false, features = ['rustc-dep-of-std'] }
///
/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-getuserprofiledirectorya
///
+/// # Deprecation
+///
+/// This function is deprecated because the behaviour on Windows is not correct.
+/// The 'HOME' environment variable is not standard on Windows, and may not produce
+/// desired results; for instance, under Cygwin or Mingw it will return `/home/you`
+/// when it should return `C:\Users\you`.
+///
/// # Examples
///
/// ```
/// ```
#[deprecated(
since = "1.29.0",
- note = "This function's behavior is unexpected and probably not what you want. \
+ note = "This function's behavior may be unexpected on Windows. \
Consider using a crate from crates.io instead."
)]
#[must_use]
/// Ok(())
/// }
/// ```
- #[unstable(feature = "file_create_new", issue = "none")]
+ #[unstable(feature = "file_create_new", issue = "105135")]
pub fn create_new<P: AsRef<Path>>(path: P) -> io::Result<File> {
OpenOptions::new().read(true).write(true).create_new(true).open(path.as_ref())
}
/// # Errors
///
/// This function will return an error if the file is not opened for writing.
- /// Also, std::io::ErrorKind::InvalidInput will be returned if the desired
- /// length would cause an overflow due to the implementation specifics.
+ /// Also, [`std::io::ErrorKind::InvalidInput`](crate::io::ErrorKind::InvalidInput)
+ /// will be returned if the desired length would cause an overflow due to
+ /// the implementation specifics.
///
/// # Examples
///
fs::metadata(hiberfil).unwrap();
assert_eq!(true, hiberfil.exists());
}
+
+/// Test that two different ways of obtaining the FileType give the same result.
+/// Cf. https://github.com/rust-lang/rust/issues/104900
+#[test]
+fn test_eq_direntry_metadata() {
+ let tmpdir = tmpdir();
+ let file_path = tmpdir.join("file");
+ File::create(file_path).unwrap();
+ for e in fs::read_dir(tmpdir.path()).unwrap() {
+ let e = e.unwrap();
+ let p = e.path();
+ let ft1 = e.file_type().unwrap();
+ let ft2 = p.metadata().unwrap().file_type();
+ assert_eq!(ft1, ft2);
+ }
+}
// lang item for CTFE panic support
// never inline unless panic_immediate_abort to avoid code
// bloat at the call sites as much as possible
-#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
-#[cold]
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
+#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[rustc_do_not_const_check] // hooked by const-eval
pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
)]
pub use core::prelude::v1::cfg_eval;
+// Do not `doc(no_inline)` either.
+#[unstable(
+ feature = "type_ascription",
+ issue = "23416",
+ reason = "placeholder syntax for type ascription"
+)]
+#[cfg(not(bootstrap))]
+pub use core::prelude::v1::type_ascribe;
+
// The file so far is equivalent to src/libcore/prelude/v1.rs,
// and below to src/liballoc/prelude.rs.
// Those files are duplicated rather than using glob imports
let slot = unsafe { self.buffer.get_unchecked(index) };
let stamp = slot.stamp.load(Ordering::Acquire);
- // If the the stamp is ahead of the head by 1, we may attempt to pop.
+ // If the stamp is ahead of the head by 1, we may attempt to pop.
if head + 1 == stamp {
let new = if index + 1 < self.cap {
// Same lap, incremented index.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> Drop for Sender<T> {
- fn drop(&mut self) {
- let _ = self.inner;
- }
+ fn drop(&mut self) {}
}
#[stable(feature = "mpsc_debug", since = "1.8.0")]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> Drop for SyncSender<T> {
- fn drop(&mut self) {
- let _ = self.inner;
- }
+ fn drop(&mut self) {}
}
#[stable(feature = "mpsc_debug", since = "1.8.0")]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> Drop for Receiver<T> {
- fn drop(&mut self) {
- let _ = self.inner;
- }
+ fn drop(&mut self) {}
}
#[stable(feature = "mpsc_debug", since = "1.8.0")]
}
}
- unsafe { mutex.lock() };
+ mutex.lock();
}
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
// we woke up because of `notify_*`.
let success = self.waiters.with_locked(|waiters| unsafe { !waiters.remove(waiter) });
- unsafe { mutex.lock() };
+ mutex.lock();
success
}
}
impl<'a> MutexGuard<'a> {
#[inline]
pub(super) fn lock(x: &'a Mutex) -> Self {
- unsafe { x.lock() };
+ x.lock();
Self(x)
}
}
ffi::CStr,
hint, io,
mem::ManuallyDrop,
+ ptr::NonNull,
sync::atomic::{AtomicUsize, Ordering},
sys::thread_local_dtor::run_dtors,
time::Duration,
};
pub struct Thread {
- inner: ManuallyDrop<Box<ThreadInner>>,
+ p_inner: NonNull<ThreadInner>,
/// The ID of the underlying task.
task: abi::ID,
}
+// Safety: There's nothing in `Thread` that ties it to the original creator. It
+// can be dropped by any threads.
+unsafe impl Send for Thread {}
+// Safety: `Thread` provides no methods that take `&self`.
+unsafe impl Sync for Thread {}
+
/// State data shared between a parent thread and child thread. It's dropped on
/// a transition to one of the final states.
struct ThreadInner {
});
unsafe extern "C" fn trampoline(exinf: isize) {
+ let p_inner: *mut ThreadInner = crate::ptr::from_exposed_addr_mut(exinf as usize);
// Safety: `ThreadInner` is alive at this point
- let inner = unsafe { &*(exinf as *const ThreadInner) };
+ let inner = unsafe { &*p_inner };
// Safety: Since `trampoline` is called only once for each
// `ThreadInner` and only `trampoline` touches `start`,
// No one will ever join, so we'll ask the collector task to
// delete the task.
- // In this case, `inner`'s ownership has been moved to us,
- // And we are responsible for dropping it. The acquire
+ // In this case, `*p_inner`'s ownership has been moved to
+ // us, and we are responsible for dropping it. The acquire
// ordering is not necessary because the parent thread made
// no memory access needing synchronization since the call
// to `acre_tsk`.
// Safety: See above.
- let _ = unsafe { Box::from_raw(inner as *const _ as *mut ThreadInner) };
+ let _ = unsafe { Box::from_raw(p_inner) };
// Safety: There are no pinned references to the stack
unsafe { terminate_and_delete_current_task() };
}
}
- let inner_ptr = (&*inner) as *const ThreadInner;
+ // Safety: `Box::into_raw` returns a non-null pointer
+ let p_inner = unsafe { NonNull::new_unchecked(Box::into_raw(inner)) };
let new_task = ItronError::err_if_negative(unsafe {
abi::acre_tsk(&abi::T_CTSK {
// Activate this task immediately
tskatr: abi::TA_ACT,
- exinf: inner_ptr as abi::EXINF,
+ exinf: p_inner.as_ptr().expose_addr() as abi::EXINF,
// The entry point
task: Some(trampoline),
// Inherit the calling task's base priority
})
.map_err(|e| e.as_io_error())?;
- Ok(Self { inner: ManuallyDrop::new(inner), task: new_task })
+ Ok(Self { p_inner, task: new_task })
}
pub fn yield_now() {
}
}
- pub fn join(mut self) {
- let inner = &*self.inner;
+ pub fn join(self) {
+ // Safety: `ThreadInner` is alive at this point
+ let inner = unsafe { self.p_inner.as_ref() };
// Get the current task ID. Panicking here would cause a resource leak,
// so just abort on failure.
let current_task = task::current_task_id_aborting();
unsafe { terminate_and_delete_task(self.task) };
// In either case, we are responsible for dropping `inner`.
- // Safety: The contents of `self.inner` will not be accessed hereafter
- let _inner = unsafe { ManuallyDrop::take(&mut self.inner) };
+ // Safety: The contents of `*p_inner` will not be accessed hereafter
+ let _inner = unsafe { Box::from_raw(self.p_inner.as_ptr()) };
// Skip the destructor (because it would attempt to detach the thread)
crate::mem::forget(self);
impl Drop for Thread {
fn drop(&mut self) {
+ // Safety: `ThreadInner` is alive at this point
+ let inner = unsafe { self.p_inner.as_ref() };
+
// Detach the thread.
- match self.inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::Acquire) {
+ match inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::Acquire) {
LIFECYCLE_INIT => {
// [INIT → DETACHED]
// When the time comes, the child will figure out that no
// one will ever join it.
- // The ownership of `self.inner` is moved to the child thread.
+ // The ownership of `*p_inner` is moved to the child thread.
// However, the release ordering is not necessary because we
// made no memory access needing synchronization since the call
// to `acre_tsk`.
// delete by entering the `FINISHED` state.
unsafe { terminate_and_delete_task(self.task) };
- // Wwe are responsible for dropping `inner`.
- // Safety: The contents of `self.inner` will not be accessed
- // hereafter
- unsafe { ManuallyDrop::drop(&mut self.inner) };
+ // Wwe are responsible for dropping `*p_inner`.
+ // Safety: The contents of `*p_inner` will not be accessed hereafter
+ let _ = unsafe { Box::from_raw(self.p_inner.as_ptr()) };
}
_ => unsafe { hint::unreachable_unchecked() },
}
pub mod stdio;
pub mod thread;
pub mod thread_local_key;
+pub mod thread_parker;
pub mod time;
mod condvar;
/// execution. The signal is sent once all TLS destructors have finished at
/// which point no new thread locals should be created.
pub mod wait_notify {
- use super::super::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
+ use super::super::thread_parker::Parker;
+ use crate::pin::Pin;
use crate::sync::Arc;
- pub struct Notifier(Arc<SpinMutex<WaitVariable<bool>>>);
+ pub struct Notifier(Arc<Parker>);
impl Notifier {
/// Notify the waiter. The waiter is either notified right away (if
/// currently blocked in `Waiter::wait()`) or later when it calls the
/// `Waiter::wait()` method.
pub fn notify(self) {
- let mut guard = self.0.lock();
- *guard.lock_var_mut() = true;
- let _ = WaitQueue::notify_one(guard);
+ Pin::new(&*self.0).unpark()
}
}
- pub struct Waiter(Arc<SpinMutex<WaitVariable<bool>>>);
+ pub struct Waiter(Arc<Parker>);
impl Waiter {
/// Wait for a notification. If `Notifier::notify()` has already been
/// called, this will return immediately, otherwise the current thread
/// is blocked until notified.
pub fn wait(self) {
- let guard = self.0.lock();
- if *guard.lock_var() {
- return;
- }
- WaitQueue::wait(guard, || {});
+ // This is not actually `unsafe`, but it uses the `Parker` API,
+ // which needs `unsafe` on some platforms.
+ unsafe { Pin::new(&*self.0).park() }
}
}
pub fn new() -> (Notifier, Waiter) {
- let inner = Arc::new(SpinMutex::new(WaitVariable::new(false)));
+ let inner = Arc::new(Parker::new_internal());
(Notifier(inner.clone()), Waiter(inner))
}
}
--- /dev/null
+//! Thread parking based on SGX events.
+
+use super::abi::{thread, usercalls};
+use crate::io::ErrorKind;
+use crate::pin::Pin;
+use crate::ptr::{self, NonNull};
+use crate::sync::atomic::AtomicPtr;
+use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
+use crate::time::Duration;
+use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE};
+
+// The TCS structure must be page-aligned (this is checked by EENTER), so these cannot
+// be valid pointers
+const EMPTY: *mut u8 = ptr::invalid_mut(1);
+const NOTIFIED: *mut u8 = ptr::invalid_mut(2);
+
+pub struct Parker {
+ /// The park state. One of EMPTY, NOTIFIED or a TCS address.
+ /// A state change to NOTIFIED must be done with release ordering
+ /// and be observed with acquire ordering so that operations after
+ /// `thread::park` returns will not occur before the unpark message
+ /// was sent.
+ state: AtomicPtr<u8>,
+}
+
+impl Parker {
+ /// Construct the thread parker. The UNIX parker implementation
+ /// requires this to happen in-place.
+ pub unsafe fn new(parker: *mut Parker) {
+ unsafe { parker.write(Parker::new_internal()) }
+ }
+
+ pub(super) fn new_internal() -> Parker {
+ Parker { state: AtomicPtr::new(EMPTY) }
+ }
+
+ // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+ pub unsafe fn park(self: Pin<&Self>) {
+ if self.state.load(Acquire) != NOTIFIED {
+ let mut prev = EMPTY;
+ loop {
+ // Guard against changing TCS addresses by always setting the state to
+ // the current value.
+ let tcs = thread::current().as_ptr();
+ if self.state.compare_exchange(prev, tcs, Relaxed, Acquire).is_ok() {
+ let event = usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap();
+ assert!(event & EV_UNPARK == EV_UNPARK);
+ prev = tcs;
+ } else {
+ // The state was definitely changed by another thread at this point.
+ // The only time this occurs is when the state is changed to NOTIFIED.
+ // We observed this change with acquire ordering, so we can simply
+ // change the state to EMPTY with a relaxed store.
+ break;
+ }
+ }
+ }
+
+ // At this point, the token was definately read with acquire ordering,
+ // so this can be a relaxed store.
+ self.state.store(EMPTY, Relaxed);
+ }
+
+ // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+ let timeout = u128::min(dur.as_nanos(), WAIT_INDEFINITE as u128 - 1) as u64;
+ let tcs = thread::current().as_ptr();
+
+ if self.state.load(Acquire) != NOTIFIED {
+ if self.state.compare_exchange(EMPTY, tcs, Relaxed, Acquire).is_ok() {
+ match usercalls::wait(EV_UNPARK, timeout) {
+ Ok(event) => assert!(event & EV_UNPARK == EV_UNPARK),
+ Err(e) => {
+ assert!(matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock))
+ }
+ }
+
+ // Swap to provide acquire ordering even if the timeout occurred
+ // before the token was set. This situation can result in spurious
+ // wakeups on the next call to `park_timeout`, but it is better to let
+ // those be handled by the user than do some perhaps unnecessary, but
+ // always expensive guarding.
+ self.state.swap(EMPTY, Acquire);
+ return;
+ }
+ }
+
+ // The token was already read with `acquire` ordering, this can be a store.
+ self.state.store(EMPTY, Relaxed);
+ }
+
+ // This implementation doesn't require `Pin`, but other implementations do.
+ pub fn unpark(self: Pin<&Self>) {
+ let state = self.state.swap(NOTIFIED, Release);
+
+ if !matches!(state, EMPTY | NOTIFIED) {
+ // There is a thread waiting, wake it up.
+ let tcs = NonNull::new(state).unwrap();
+ // This will fail if the thread has already terminated or its TCS is destroyed
+ // by the time the signal is sent, but that is fine. If another thread receives
+ // the same TCS, it will receive this notification as a spurious wakeup, but
+ // all users of `wait` should and (internally) do guard against those where
+ // necessary.
+ let _ = usercalls::send(EV_UNPARK, Some(tcs));
+ }
+ }
+}
unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
}
}
+
+pub fn is_terminal<T>(_: &T) -> bool {
+ false
+}
use super::unsupported;
-use crate::convert::TryFrom;
use crate::error::Error as StdError;
-use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::ffi::{CStr, OsStr, OsString};
use crate::fmt;
use crate::io;
use crate::os::{
modified: Option<SystemTime>,
}
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Copy, Clone, Eq, Debug)]
pub struct FileType {
mode: mode_t,
}
+impl PartialEq for FileType {
+ fn eq(&self, other: &Self) -> bool {
+ self.masked() == other.masked()
+ }
+}
+
+impl core::hash::Hash for FileType {
+ fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+ self.masked().hash(state);
+ }
+}
+
#[derive(Debug)]
pub struct DirBuilder {
mode: mode_t,
}
pub fn is(&self, mode: mode_t) -> bool {
- self.mode & libc::S_IFMT == mode
+ self.masked() == mode
+ }
+
+ fn masked(&self) -> mode_t {
+ self.mode & libc::S_IFMT
}
}
use crate::ptr;
use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed};
use crate::sys::locks::{pthread_mutex, Mutex};
+use crate::sys::time::TIMESPEC_MAX;
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
use crate::time::Duration;
mutex: AtomicPtr<libc::pthread_mutex_t>,
}
-const TIMESPEC_MAX: libc::timespec =
- libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
-
-fn saturating_cast_to_time_t(value: u64) -> libc::time_t {
- if value > <libc::time_t>::MAX as u64 { <libc::time_t>::MAX } else { value as libc::time_t }
-}
-
#[inline]
fn raw(c: &Condvar) -> *mut libc::pthread_cond_t {
c.inner.0.get()
target_os = "horizon"
)))]
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
- use crate::mem;
+ use crate::sys::time::Timespec;
let mutex = pthread_mutex::raw(mutex);
self.verify(mutex);
- let mut now: libc::timespec = mem::zeroed();
- let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now);
- assert_eq!(r, 0);
-
- // Nanosecond calculations can't overflow because both values are below 1e9.
- let nsec = dur.subsec_nanos() + now.tv_nsec as u32;
-
- let sec = saturating_cast_to_time_t(dur.as_secs())
- .checked_add((nsec / 1_000_000_000) as libc::time_t)
- .and_then(|s| s.checked_add(now.tv_sec));
- let nsec = nsec % 1_000_000_000;
-
- let timeout =
- sec.map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec as _ }).unwrap_or(TIMESPEC_MAX);
-
+ let timeout = Timespec::now(libc::CLOCK_MONOTONIC)
+ .checked_add_duration(&dur)
+ .and_then(|t| t.to_timespec())
+ .unwrap_or(TIMESPEC_MAX);
let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout);
assert!(r == libc::ETIMEDOUT || r == 0);
r == 0
target_os = "espidf",
target_os = "horizon"
))]
- pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool {
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ use crate::sys::time::SystemTime;
use crate::time::Instant;
let mutex = pthread_mutex::raw(mutex);
self.verify(mutex);
- // 1000 years
- let max_dur = Duration::from_secs(1000 * 365 * 86400);
-
- if dur > max_dur {
- // OSX implementation of `pthread_cond_timedwait` is buggy
- // with super long durations. When duration is greater than
- // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
- // in macOS Sierra return error 316.
- //
- // This program demonstrates the issue:
- // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
- //
- // To work around this issue, and possible bugs of other OSes, timeout
- // is clamped to 1000 years, which is allowable per the API of `wait_timeout`
- // because of spurious wakeups.
-
- dur = max_dur;
- }
-
- // First, figure out what time it currently is, in both system and
- // stable time. pthread_cond_timedwait uses system time, but we want to
- // report timeout based on stable time.
- let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 };
- let stable_now = Instant::now();
- let r = libc::gettimeofday(&mut sys_now, ptr::null_mut());
- assert_eq!(r, 0, "unexpected error: {:?}", crate::io::Error::last_os_error());
-
- let nsec = dur.subsec_nanos() as libc::c_long + (sys_now.tv_usec * 1000) as libc::c_long;
- let extra = (nsec / 1_000_000_000) as libc::time_t;
- let nsec = nsec % 1_000_000_000;
- let seconds = saturating_cast_to_time_t(dur.as_secs());
-
- let timeout = sys_now
- .tv_sec
- .checked_add(extra)
- .and_then(|s| s.checked_add(seconds))
- .map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec })
+ // OSX implementation of `pthread_cond_timedwait` is buggy
+ // with super long durations. When duration is greater than
+ // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
+ // in macOS Sierra returns error 316.
+ //
+ // This program demonstrates the issue:
+ // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
+ //
+ // To work around this issue, and possible bugs of other OSes, timeout
+ // is clamped to 1000 years, which is allowable per the API of `wait_timeout`
+ // because of spurious wakeups.
+ let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400));
+
+ // pthread_cond_timedwait uses system time, but we want to report timeout
+ // based on stable time.
+ let now = Instant::now();
+
+ let timeout = SystemTime::now()
+ .t
+ .checked_add_duration(&dur)
+ .and_then(|t| t.to_timespec())
.unwrap_or(TIMESPEC_MAX);
- // And wait!
let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout);
debug_assert!(r == libc::ETIMEDOUT || r == 0);
// ETIMEDOUT is not a totally reliable method of determining timeout due
// to clock shifts, so do the check ourselves
- stable_now.elapsed() < dur
+ now.elapsed() < dur
}
}
unsafe {
// Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20.
- let name = truncate_cstr(name, TASK_COMM_LEN);
+ let name = truncate_cstr::<{ TASK_COMM_LEN }>(name);
let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr());
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
debug_assert_eq!(res, 0);
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
pub fn set_name(name: &CStr) {
unsafe {
- let name = truncate_cstr(name, libc::MAXTHREADNAMESIZE);
+ let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name);
let res = libc::pthread_setname_np(name.as_ptr());
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
debug_assert_eq!(res, 0);
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))]
-fn truncate_cstr(cstr: &CStr, max_with_nul: usize) -> crate::borrow::Cow<'_, CStr> {
- use crate::{borrow::Cow, ffi::CString};
-
- if cstr.to_bytes_with_nul().len() > max_with_nul {
- let bytes = cstr.to_bytes()[..max_with_nul - 1].to_vec();
- // SAFETY: the non-nul bytes came straight from a CStr.
- // (CString will add the terminating nul.)
- Cow::Owned(unsafe { CString::from_vec_unchecked(bytes) })
- } else {
- Cow::Borrowed(cstr)
+fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] {
+ let mut result = [0; MAX_WITH_NUL];
+ for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) {
+ *dst = *src as libc::c_char;
}
+ result
}
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
use crate::ptr::addr_of_mut;
use crate::sync::atomic::AtomicUsize;
use crate::sync::atomic::Ordering::SeqCst;
+use crate::sys::time::TIMESPEC_MAX;
use crate::time::Duration;
const EMPTY: usize = 0;
debug_assert_eq!(r, 0);
}
-const TIMESPEC_MAX: libc::timespec =
- libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
-
unsafe fn wait_timeout(
cond: *mut libc::pthread_cond_t,
lock: *mut libc::pthread_mutex_t,
const NSEC_PER_SEC: u64 = 1_000_000_000;
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
+#[allow(dead_code)] // Used for pthread condvar timeouts
+pub const TIMESPEC_MAX: libc::timespec =
+ libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
use crate::sync::atomic::{self, AtomicPtr, Ordering};
// We can use true weak linkage on ELF targets.
-#[cfg(not(any(target_os = "macos", target_os = "ios")))]
+#[cfg(all(not(any(target_os = "macos", target_os = "ios")), not(bootstrap)))]
+pub(crate) macro weak {
+ (fn $name:ident($($t:ty),*) -> $ret:ty) => (
+ let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
+ extern "C" {
+ #[linkage = "extern_weak"]
+ static $name: Option<unsafe extern "C" fn($($t),*) -> $ret>;
+ }
+ #[allow(unused_unsafe)]
+ ExternWeak::new(unsafe { $name })
+ };
+ )
+}
+
+#[cfg(all(not(any(target_os = "macos", target_os = "ios")), bootstrap))]
pub(crate) macro weak {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) use self::dlsym as weak;
+#[cfg(not(bootstrap))]
+pub(crate) struct ExternWeak<F: Copy> {
+ weak_ptr: Option<F>,
+}
+
+#[cfg(not(bootstrap))]
+impl<F: Copy> ExternWeak<F> {
+ #[inline]
+ pub(crate) fn new(weak_ptr: Option<F>) -> Self {
+ ExternWeak { weak_ptr }
+ }
+
+ #[inline]
+ pub(crate) fn get(&self) -> Option<F> {
+ self.weak_ptr
+ }
+}
+
+#[cfg(bootstrap)]
pub(crate) struct ExternWeak<F> {
weak_ptr: *const libc::c_void,
_marker: PhantomData<F>,
}
+#[cfg(bootstrap)]
impl<F> ExternWeak<F> {
#[inline]
pub(crate) fn new(weak_ptr: *const libc::c_void) -> Self {
}
}
+#[cfg(bootstrap)]
impl<F> ExternWeak<F> {
#[inline]
pub(crate) fn get(&self) -> Option<F> {
) -> io::Result<(Process, StdioPipes)> {
let maybe_env = self.env.capture_if_changed();
- let mut si = zeroed_startupinfo();
- si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
- si.dwFlags = c::STARTF_USESTDHANDLES;
-
let child_paths = if let Some(env) = maybe_env.as_ref() {
env.get(&EnvKey::new("PATH")).map(|s| s.as_os_str())
} else {
let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?;
let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?;
let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?;
- si.hStdInput = stdin.as_raw_handle();
- si.hStdOutput = stdout.as_raw_handle();
- si.hStdError = stderr.as_raw_handle();
+
+ let mut si = zeroed_startupinfo();
+ si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
+
+ // If at least one of stdin, stdout or stderr are set (i.e. are non null)
+ // then set the `hStd` fields in `STARTUPINFO`.
+ // Otherwise skip this and allow the OS to apply its default behaviour.
+ // This provides more consistent behaviour between Win7 and Win8+.
+ let is_set = |stdio: &Handle| !stdio.as_raw_handle().is_null();
+ if is_set(&stderr) || is_set(&stdout) || is_set(&stdin) {
+ si.dwFlags |= c::STARTF_USESTDHANDLES;
+ si.hStdInput = stdin.as_raw_handle();
+ si.hStdOutput = stdout.as_raw_handle();
+ si.hStdError = stderr.as_raw_handle();
+ }
unsafe {
cvt(c::CreateProcessW(
impl Stdio {
fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>) -> io::Result<Handle> {
match *self {
- // If no stdio handle is available, then inherit means that it
- // should still be unavailable so propagate the
- // INVALID_HANDLE_VALUE.
Stdio::Inherit => match stdio::get_handle(stdio_id) {
Ok(io) => unsafe {
let io = Handle::from_raw_handle(io);
io.into_raw_handle();
ret
},
- Err(..) => unsafe { Ok(Handle::from_raw_handle(c::INVALID_HANDLE_VALUE)) },
+ // If no stdio handle is available, then propagate the null value.
+ Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) },
},
Stdio::MakePipe => {
wShowWindow: 0,
cbReserved2: 0,
lpReserved2: ptr::null_mut(),
- hStdInput: c::INVALID_HANDLE_VALUE,
- hStdOutput: c::INVALID_HANDLE_VALUE,
- hStdError: c::INVALID_HANDLE_VALUE,
+ hStdInput: ptr::null_mut(),
+ hStdOutput: ptr::null_mut(),
+ hStdError: ptr::null_mut(),
}
}
pub use wait_flag::Parker;
} else if #[cfg(any(windows, target_family = "unix"))] {
pub use crate::sys::thread_parker::Parker;
+ } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
+ pub use crate::sys::thread_parker::Parker;
} else {
mod generic;
pub use generic::Parker;
// We check for 'overflow' with usize::MAX / 2, to make sure there's no
// chance it overflows to 0, which would result in unsoundness.
if self.num_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 {
- // This can only reasonably happen by mem::forget()'ing many many ScopedJoinHandles.
+ // This can only reasonably happen by mem::forget()'ing a lot of ScopedJoinHandles.
self.decrement_num_running_threads(false);
panic!("too many running threads in thread scope");
}
pub test_threads: Option<usize>,
pub skip: Vec<String>,
pub time_options: Option<TestTimeOptions>,
+ /// Stop at first failing test.
+ /// May run a few more tests due to threading, but will
+ /// abort as soon as possible.
+ pub fail_fast: bool,
pub options: Options,
}
skip,
time_options,
options,
+ fail_fast: false,
};
Ok(test_opts)
run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?;
st.exec_time = start_time.map(|t| TestSuiteExecTime(t.elapsed()));
- assert!(st.current_test_count() == st.total);
+ assert!(opts.fail_fast || st.current_test_count() == st.total);
out.write_run_finish(&st)
}
let mut completed_test = rx.recv().unwrap();
RunningTest { join_handle }.join(&mut completed_test);
+ let fail_fast = match completed_test.result {
+ TrIgnored | TrOk | TrBench(_) => false,
+ TrFailed | TrFailedMsg(_) | TrTimedFail => opts.fail_fast,
+ };
+
let event = TestEvent::TeResult(completed_test);
notify_about_test_event(event)?;
+
+ if fail_fast {
+ return Ok(());
+ }
}
} else {
while pending > 0 || !remaining.is_empty() {
let running_test = running_tests.remove(&completed_test.id).unwrap();
running_test.join(&mut completed_test);
+ let fail_fast = match completed_test.result {
+ TrIgnored | TrOk | TrBench(_) => false,
+ TrFailed | TrFailedMsg(_) | TrTimedFail => opts.fail_fast,
+ };
+
let event = TestEvent::TeResult(completed_test);
notify_about_test_event(event)?;
pending -= 1;
+
+ if fail_fast {
+ // Prevent remaining test threads from panicking
+ std::mem::forget(rx);
+ return Ok(());
+ }
}
}
skip: vec![],
time_options: None,
options: Options::new(),
+ fail_fast: false,
}
}
}
"memchr",
]
-[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
[[package]]
name = "autocfg"
version = "1.1.0"
[[package]]
name = "cpufeatures"
-version = "0.2.2"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
dependencies = [
"libc",
]
[[package]]
name = "crossbeam-channel"
-version = "0.5.4"
+version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if",
"crossbeam-utils",
[[package]]
name = "crossbeam-deque"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
+checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
"cfg-if",
"crossbeam-epoch",
[[package]]
name = "crossbeam-epoch"
-version = "0.9.8"
+version = "0.9.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
+checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
- "lazy_static",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.8"
+version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
+checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if",
- "lazy_static",
]
[[package]]
[[package]]
name = "memoffset"
-version = "0.6.5"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "pretty_assertions"
-version = "0.7.2"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b"
+checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
dependencies = [
- "ansi_term",
"ctor",
"diff",
"output_vt100",
+ "yansi",
]
[[package]]
[[package]]
name = "rayon"
-version = "1.5.3"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
+checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
dependencies = [
- "autocfg",
"crossbeam-deque",
"either",
"rayon-core",
[[package]]
name = "rayon-core"
-version = "1.9.3"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
+checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
dependencies = [
"lzma-sys",
]
+
+[[package]]
+name = "yansi"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
]
[dev-dependencies]
-pretty_assertions = "0.7"
+pretty_assertions = "1.2"
[features]
build-metrics = ["sysinfo"]
run::BumpStage0,
run::ReplaceVersionPlaceholder,
run::Miri,
+ run::CollectLicenseMetadata,
+ run::GenerateCopyright,
),
// These commands either don't use paths, or they're special-cased in Build::build()
Kind::Clean | Kind::Format | Kind::Setup => vec![],
//! This module implements parsing `config.toml` configuration files to tweak
//! how the build runs.
+#[cfg(test)]
+mod tests;
+
use std::cell::{Cell, RefCell};
use std::cmp;
use std::collections::{HashMap, HashSet};
pub npm: Option<PathBuf>,
pub gdb: Option<PathBuf>,
pub python: Option<PathBuf>,
+ pub reuse: Option<PathBuf>,
pub cargo_native_static: bool,
pub configure_args: Vec<String>,
nodejs: Option<String> = "nodejs",
npm: Option<String> = "npm",
python: Option<String> = "python",
+ reuse: Option<String> = "reuse",
locked_deps: Option<bool> = "locked-deps",
vendor: Option<bool> = "vendor",
full_bootstrap: Option<bool> = "full-bootstrap",
}
}
-#[derive(Deserialize)]
+#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum StringOrBool {
String(String),
}
pub fn parse(args: &[String]) -> Config {
+ #[cfg(test)]
+ let get_toml = |_: &_| TomlConfig::default();
+ #[cfg(not(test))]
+ let get_toml = |file: &Path| {
+ let contents =
+ t!(fs::read_to_string(file), format!("config file {} not found", file.display()));
+ // Deserialize to Value and then TomlConfig to prevent the Deserialize impl of
+ // TomlConfig and sub types to be monomorphized 5x by toml.
+ match toml::from_str(&contents)
+ .and_then(|table: toml::Value| TomlConfig::deserialize(table))
+ {
+ Ok(table) => table,
+ Err(err) => {
+ eprintln!("failed to parse TOML configuration '{}': {}", file.display(), err);
+ crate::detail_exit(2);
+ }
+ }
+ };
+
+ Self::parse_inner(args, get_toml)
+ }
+
+ fn parse_inner<'a>(args: &[String], get_toml: impl 'a + Fn(&Path) -> TomlConfig) -> Config {
let flags = Flags::parse(&args);
let mut config = Config::default_opts();
config.stage0_metadata = t!(serde_json::from_slice::<Stage0Metadata>(&stage0_json));
- #[cfg(test)]
- let get_toml = |_| TomlConfig::default();
- #[cfg(not(test))]
- let get_toml = |file: &Path| {
- let contents =
- t!(fs::read_to_string(file), format!("config file {} not found", file.display()));
- // Deserialize to Value and then TomlConfig to prevent the Deserialize impl of
- // TomlConfig and sub types to be monomorphized 5x by toml.
- match toml::from_str(&contents)
- .and_then(|table: toml::Value| TomlConfig::deserialize(table))
- {
- Ok(table) => table,
- Err(err) => {
- eprintln!("failed to parse TOML configuration '{}': {}", file.display(), err);
- crate::detail_exit(2);
- }
- }
- };
-
// Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`, then `config.toml` in the root directory.
let toml_path = flags
.config
config.npm = build.npm.map(PathBuf::from);
config.gdb = build.gdb.map(PathBuf::from);
config.python = build.python.map(PathBuf::from);
+ config.reuse = build.reuse.map(PathBuf::from);
config.submodules = build.submodules;
set(&mut config.low_priority, build.low_priority);
set(&mut config.compiler_docs, build.compiler_docs);
let mut optimize = None;
let mut ignore_git = None;
+ if let Some(rust) = toml.rust {
+ debug = rust.debug;
+ debug_assertions = rust.debug_assertions;
+ debug_assertions_std = rust.debug_assertions_std;
+ overflow_checks = rust.overflow_checks;
+ overflow_checks_std = rust.overflow_checks_std;
+ debug_logging = rust.debug_logging;
+ debuginfo_level = rust.debuginfo_level;
+ debuginfo_level_rustc = rust.debuginfo_level_rustc;
+ debuginfo_level_std = rust.debuginfo_level_std;
+ debuginfo_level_tools = rust.debuginfo_level_tools;
+ debuginfo_level_tests = rust.debuginfo_level_tests;
+ config.rust_split_debuginfo = rust
+ .split_debuginfo
+ .as_deref()
+ .map(SplitDebuginfo::from_str)
+ .map(|v| v.expect("invalid value for rust.split_debuginfo"))
+ .unwrap_or(SplitDebuginfo::default_for_platform(&config.build.triple));
+ optimize = rust.optimize;
+ ignore_git = rust.ignore_git;
+ config.rust_new_symbol_mangling = rust.new_symbol_mangling;
+ set(&mut config.rust_optimize_tests, rust.optimize_tests);
+ set(&mut config.codegen_tests, rust.codegen_tests);
+ set(&mut config.rust_rpath, rust.rpath);
+ set(&mut config.jemalloc, rust.jemalloc);
+ set(&mut config.test_compare_mode, rust.test_compare_mode);
+ set(&mut config.backtrace, rust.backtrace);
+ set(&mut config.channel, rust.channel);
+ config.description = rust.description;
+ set(&mut config.rust_dist_src, rust.dist_src);
+ set(&mut config.verbose_tests, rust.verbose_tests);
+ // in the case "false" is set explicitly, do not overwrite the command line args
+ if let Some(true) = rust.incremental {
+ config.incremental = true;
+ }
+ set(&mut config.use_lld, rust.use_lld);
+ set(&mut config.lld_enabled, rust.lld);
+ set(&mut config.llvm_tools_enabled, rust.llvm_tools);
+ config.rustc_parallel = rust.parallel_compiler.unwrap_or(false);
+ config.rustc_default_linker = rust.default_linker;
+ config.musl_root = rust.musl_root.map(PathBuf::from);
+ config.save_toolstates = rust.save_toolstates.map(PathBuf::from);
+ set(&mut config.deny_warnings, flags.deny_warnings.or(rust.deny_warnings));
+ set(&mut config.backtrace_on_ice, rust.backtrace_on_ice);
+ set(&mut config.rust_verify_llvm_ir, rust.verify_llvm_ir);
+ config.rust_thin_lto_import_instr_limit = rust.thin_lto_import_instr_limit;
+ set(&mut config.rust_remap_debuginfo, rust.remap_debuginfo);
+ set(&mut config.control_flow_guard, rust.control_flow_guard);
+ config.llvm_libunwind_default = rust
+ .llvm_libunwind
+ .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
+
+ if let Some(ref backends) = rust.codegen_backends {
+ config.rust_codegen_backends =
+ backends.iter().map(|s| INTERNER.intern_str(s)).collect();
+ }
+
+ config.rust_codegen_units = rust.codegen_units.map(threads_from_config);
+ config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config);
+ config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use);
+ config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate);
+ config.download_rustc_commit = config.download_ci_rustc_commit(rust.download_rustc);
+
+ config.rust_lto = rust
+ .lto
+ .as_deref()
+ .map(|value| RustcLto::from_str(value).unwrap())
+ .unwrap_or_default();
+ } else {
+ config.rust_profile_use = flags.rust_profile_use;
+ config.rust_profile_generate = flags.rust_profile_generate;
+ }
+
if let Some(llvm) = toml.llvm {
match llvm.ccache {
Some(StringOrBool::String(ref s)) => config.ccache = Some(s.to_string()),
config.llvm_polly = llvm.polly.unwrap_or(false);
config.llvm_clang = llvm.clang.unwrap_or(false);
config.llvm_build_config = llvm.build_config.clone().unwrap_or(Default::default());
+
+ let asserts = llvm_assertions.unwrap_or(false);
config.llvm_from_ci = match llvm.download_ci_llvm {
Some(StringOrBool::String(s)) => {
assert!(s == "if-available", "unknown option `{}` for download-ci-llvm", s);
- crate::native::is_ci_llvm_available(&config, llvm_assertions.unwrap_or(false))
+ crate::native::is_ci_llvm_available(&config, asserts)
}
Some(StringOrBool::Bool(b)) => b,
- None => false,
+ None => {
+ config.channel == "dev" && crate::native::is_ci_llvm_available(&config, asserts)
+ }
};
if config.llvm_from_ci {
// the link step) with each stage.
config.llvm_link_shared.set(Some(true));
}
- }
-
- if let Some(rust) = toml.rust {
- debug = rust.debug;
- debug_assertions = rust.debug_assertions;
- debug_assertions_std = rust.debug_assertions_std;
- overflow_checks = rust.overflow_checks;
- overflow_checks_std = rust.overflow_checks_std;
- debug_logging = rust.debug_logging;
- debuginfo_level = rust.debuginfo_level;
- debuginfo_level_rustc = rust.debuginfo_level_rustc;
- debuginfo_level_std = rust.debuginfo_level_std;
- debuginfo_level_tools = rust.debuginfo_level_tools;
- debuginfo_level_tests = rust.debuginfo_level_tests;
- config.rust_split_debuginfo = rust
- .split_debuginfo
- .as_deref()
- .map(SplitDebuginfo::from_str)
- .map(|v| v.expect("invalid value for rust.split_debuginfo"))
- .unwrap_or(SplitDebuginfo::default_for_platform(&config.build.triple));
- optimize = rust.optimize;
- ignore_git = rust.ignore_git;
- config.rust_new_symbol_mangling = rust.new_symbol_mangling;
- set(&mut config.rust_optimize_tests, rust.optimize_tests);
- set(&mut config.codegen_tests, rust.codegen_tests);
- set(&mut config.rust_rpath, rust.rpath);
- set(&mut config.jemalloc, rust.jemalloc);
- set(&mut config.test_compare_mode, rust.test_compare_mode);
- set(&mut config.backtrace, rust.backtrace);
- set(&mut config.channel, rust.channel);
- config.description = rust.description;
- set(&mut config.rust_dist_src, rust.dist_src);
- set(&mut config.verbose_tests, rust.verbose_tests);
- // in the case "false" is set explicitly, do not overwrite the command line args
- if let Some(true) = rust.incremental {
- config.incremental = true;
- }
- set(&mut config.use_lld, rust.use_lld);
- set(&mut config.lld_enabled, rust.lld);
- set(&mut config.llvm_tools_enabled, rust.llvm_tools);
- config.rustc_parallel = rust.parallel_compiler.unwrap_or(false);
- config.rustc_default_linker = rust.default_linker;
- config.musl_root = rust.musl_root.map(PathBuf::from);
- config.save_toolstates = rust.save_toolstates.map(PathBuf::from);
- set(&mut config.deny_warnings, flags.deny_warnings.or(rust.deny_warnings));
- set(&mut config.backtrace_on_ice, rust.backtrace_on_ice);
- set(&mut config.rust_verify_llvm_ir, rust.verify_llvm_ir);
- config.rust_thin_lto_import_instr_limit = rust.thin_lto_import_instr_limit;
- set(&mut config.rust_remap_debuginfo, rust.remap_debuginfo);
- set(&mut config.control_flow_guard, rust.control_flow_guard);
- config.llvm_libunwind_default = rust
- .llvm_libunwind
- .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
-
- if let Some(ref backends) = rust.codegen_backends {
- config.rust_codegen_backends =
- backends.iter().map(|s| INTERNER.intern_str(s)).collect();
- }
-
- config.rust_codegen_units = rust.codegen_units.map(threads_from_config);
- config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config);
- config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use);
- config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate);
- config.download_rustc_commit = config.download_ci_rustc_commit(rust.download_rustc);
-
- config.rust_lto = rust
- .lto
- .as_deref()
- .map(|value| RustcLto::from_str(value).unwrap())
- .unwrap_or_default();
} else {
- config.rust_profile_use = flags.rust_profile_use;
- config.rust_profile_generate = flags.rust_profile_generate;
+ config.llvm_from_ci =
+ config.channel == "dev" && crate::native::is_ci_llvm_available(&config, false);
}
if let Some(t) = toml.target {
--- /dev/null
+use super::{Config, TomlConfig};
+use std::path::Path;
+
+fn toml(config: &str) -> impl '_ + Fn(&Path) -> TomlConfig {
+ |&_| toml::from_str(config).unwrap()
+}
+
+fn parse(config: &str) -> Config {
+ Config::parse_inner(&["check".to_owned(), "--config=/does/not/exist".to_owned()], toml(config))
+}
+
+#[test]
+fn download_ci_llvm() {
+ let parse_llvm = |s| parse(s).llvm_from_ci;
+ let if_available = parse_llvm("llvm.download-ci-llvm = \"if-available\"");
+
+ assert!(parse_llvm("llvm.download-ci-llvm = true"));
+ assert!(!parse_llvm("llvm.download-ci-llvm = false"));
+ assert_eq!(parse_llvm(""), if_available);
+ assert_eq!(parse_llvm("rust.channel = \"dev\""), if_available);
+ assert!(!parse_llvm("rust.channel = \"stable\""));
+}
+
+// FIXME: add test for detecting `src` and `out`
doc-stage = 2
# When compiling from source, you usually want all tools.
extended = true
+
+[llvm]
+# Most users installing from source want to build all parts of the project from source, not just rustc itself.
+download-ci-llvm = false
args: Vec<String>,
},
Setup {
- profile: Profile,
+ profile: Option<Profile>,
},
}
|path| format!("{} is not a valid UTF8 string", path.to_string_lossy())
));
- profile_string.parse().unwrap_or_else(|err| {
+ let profile = profile_string.parse().unwrap_or_else(|err| {
eprintln!("error: {}", err);
eprintln!("help: the available profiles are:");
eprint!("{}", Profile::all_for_help("- "));
crate::detail_exit(1);
- })
+ });
+ Some(profile)
} else {
- t!(crate::setup::interactive_path())
+ None
};
Subcommand::Setup { profile }
}
metrics: metrics::BuildMetrics::init(),
};
- build.verbose("finding compilers");
- cc_detect::find(&mut build);
- // When running `setup`, the profile is about to change, so any requirements we have now may
- // be different on the next invocation. Don't check for them until the next time x.py is
- // run. This is ok because `setup` never runs any build commands, so it won't fail if commands are missing.
- if !matches!(build.config.cmd, Subcommand::Setup { .. }) {
- build.verbose("running sanity check");
- sanity::check(&mut build);
- }
-
// If local-rust is the same major.minor as the current version, then force a
// local-rebuild
let local_version_verbose =
build.local_rebuild = true;
}
- // Make sure we update these before gathering metadata so we don't get an error about missing
- // Cargo.toml files.
- let rust_submodules =
- ["src/tools/rust-installer", "src/tools/cargo", "library/backtrace", "library/stdarch"];
- for s in rust_submodules {
- build.update_submodule(Path::new(s));
- }
+ build.verbose("finding compilers");
+ cc_detect::find(&mut build);
+ // When running `setup`, the profile is about to change, so any requirements we have now may
+ // be different on the next invocation. Don't check for them until the next time x.py is
+ // run. This is ok because `setup` never runs any build commands, so it won't fail if commands are missing.
+ //
+ // Similarly, for `setup` we don't actually need submodules or cargo metadata.
+ if !matches!(build.config.cmd, Subcommand::Setup { .. }) {
+ build.verbose("running sanity check");
+ sanity::check(&mut build);
- build.verbose("learning about cargo");
- metadata::build(&mut build);
+ // Make sure we update these before gathering metadata so we don't get an error about missing
+ // Cargo.toml files.
+ let rust_submodules = [
+ "src/tools/rust-installer",
+ "src/tools/cargo",
+ "library/backtrace",
+ "library/stdarch",
+ ];
+ for s in rust_submodules {
+ build.update_submodule(Path::new(s));
+ }
+ // Now, update all existing submodules.
+ build.update_existing_submodules();
+
+ build.verbose("learning about cargo");
+ metadata::build(&mut build);
+ }
build
}
if !update(true).status().map_or(false, |status| status.success()) {
self.run(&mut update(false));
}
- self.run(Command::new("git").args(&["stash", "push"]).current_dir(&absolute_path));
+
+ // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
+ let has_local_modifications = !self.try_run(
+ Command::new("git")
+ .args(&["diff-index", "--quiet", "HEAD"])
+ .current_dir(&absolute_path),
+ );
+ if has_local_modifications {
+ self.run(Command::new("git").args(&["stash", "push"]).current_dir(&absolute_path));
+ }
+
self.run(Command::new("git").args(&["reset", "-q", "--hard"]).current_dir(&absolute_path));
self.run(Command::new("git").args(&["clean", "-qdfx"]).current_dir(&absolute_path));
- self.run(Command::new("git").args(&["stash", "pop"]).current_dir(absolute_path));
+
+ if has_local_modifications {
+ self.run(Command::new("git").args(&["stash", "pop"]).current_dir(absolute_path));
+ }
}
/// If any submodule has been initialized already, sync it unconditionally.
/// This avoids contributors checking in a submodule change by accident.
- pub fn maybe_update_submodules(&self) {
+ pub fn update_existing_submodules(&self) {
// Avoid running git when there isn't a git checkout.
if !self.config.submodules(&self.rust_info()) {
return;
job::setup(self);
}
- self.maybe_update_submodules();
-
if let Subcommand::Format { check, paths } = &self.config.cmd {
return format::format(&builder::Builder::new(&self), *check, &paths);
}
/// If the test is running and code is an error code, it will cause a panic.
fn detail_exit(code: i32) -> ! {
// if in test and code is an error code, panic with status code provided
- if cfg!(test) && code != 0 {
+ if cfg!(test) {
panic!("status code: {}", code);
} else {
- //otherwise,exit with provided status code
+ // otherwise,exit with provided status code
std::process::exit(code);
}
}
+use std::path::PathBuf;
use std::process::Command;
use crate::builder::{Builder, RunConfig, ShouldRun, Step};
builder.run(&mut miri);
}
}
+
+#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct CollectLicenseMetadata;
+
+impl Step for CollectLicenseMetadata {
+ type Output = PathBuf;
+ const ONLY_HOSTS: bool = true;
+
+ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+ run.path("src/tools/collect-license-metadata")
+ }
+
+ fn make_run(run: RunConfig<'_>) {
+ run.builder.ensure(CollectLicenseMetadata);
+ }
+
+ fn run(self, builder: &Builder<'_>) -> Self::Output {
+ let Some(reuse) = &builder.config.reuse else {
+ panic!("REUSE is required to collect the license metadata");
+ };
+
+ // Temporary location, it will be moved to src/etc once it's accurate.
+ let dest = builder.out.join("license-metadata.json");
+
+ let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
+ cmd.env("REUSE_EXE", reuse);
+ cmd.env("DEST", &dest);
+ builder.run(&mut cmd);
+
+ dest
+ }
+}
+
+#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct GenerateCopyright;
+
+impl Step for GenerateCopyright {
+ type Output = PathBuf;
+ const ONLY_HOSTS: bool = true;
+
+ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+ run.path("src/tools/generate-copyright")
+ }
+
+ fn make_run(run: RunConfig<'_>) {
+ run.builder.ensure(GenerateCopyright);
+ }
+
+ fn run(self, builder: &Builder<'_>) -> Self::Output {
+ let license_metadata = builder.ensure(CollectLicenseMetadata);
+
+ // Temporary location, it will be moved to the proper one once it's accurate.
+ let dest = builder.out.join("COPYRIGHT.md");
+
+ let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
+ cmd.env("LICENSE_METADATA", &license_metadata);
+ cmd.env("DEST", &dest);
+ builder.run(&mut cmd);
+
+ dest
+ }
+}
.map(|p| cmd_finder.must_have(p))
.or_else(|| cmd_finder.maybe_have("gdb"));
+ build.config.reuse = build
+ .config
+ .reuse
+ .take()
+ .map(|p| cmd_finder.must_have(p))
+ .or_else(|| cmd_finder.maybe_have("reuse"));
+
// We're gonna build some custom C code here and there, host triples
// also build some C++ shims for LLVM so we need a C++ compiler.
for target in &build.targets {
+use crate::Config;
use crate::{t, VERSION};
-use crate::{Config, TargetSelection};
use std::env::consts::EXE_SUFFIX;
use std::fmt::Write as _;
use std::fs::File;
+use std::io::Write;
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use std::process::Command;
use std::str::FromStr;
-use std::{
- env, fmt, fs,
- io::{self, Write},
-};
+use std::{fmt, fs, io};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Profile {
}
}
-pub fn setup(config: &Config, profile: Profile) {
- let path = &config.config.clone().unwrap_or(PathBuf::from("config.toml"));
-
- if path.exists() {
- eprintln!(
- "error: you asked `x.py` to setup a new config file, but one already exists at `{}`",
- path.display()
- );
- eprintln!("help: try adding `profile = \"{}\"` at the top of {}", profile, path.display());
- eprintln!(
- "note: this will use the configuration in {}",
- profile.include_path(&config.src).display()
- );
- crate::detail_exit(1);
- }
-
- let settings = format!(
- "# Includes one of the default files in src/bootstrap/defaults\n\
- profile = \"{}\"\n\
- changelog-seen = {}\n",
- profile, VERSION
- );
- t!(fs::write(path, settings));
-
- let include_path = profile.include_path(&config.src);
- println!("`x.py` will now use the configuration at {}", include_path.display());
-
- let build = TargetSelection::from_user(&env!("BUILD_TRIPLE"));
+pub fn setup(config: &Config, profile: Option<Profile>) {
+ let profile = profile.unwrap_or_else(|| t!(interactive_path()));
let stage_path =
- ["build", build.rustc_target_arg(), "stage1"].join(&MAIN_SEPARATOR.to_string());
-
- println!();
+ ["build", config.build.rustc_target_arg(), "stage1"].join(&MAIN_SEPARATOR.to_string());
if !rustup_installed() && profile != Profile::User {
eprintln!("`rustup` is not installed; cannot link `stage1` toolchain");
Profile::User => &["dist", "build"],
};
- println!();
-
t!(install_git_hook_maybe(&config));
println!();
"For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html"
);
}
+
+ let path = &config.config.clone().unwrap_or(PathBuf::from("config.toml"));
+ setup_config_toml(path, profile, config);
+}
+
+fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) {
+ if path.exists() {
+ eprintln!();
+ eprintln!(
+ "error: you asked `x.py` to setup a new config file, but one already exists at `{}`",
+ path.display()
+ );
+ eprintln!("help: try adding `profile = \"{}\"` at the top of {}", profile, path.display());
+ eprintln!(
+ "note: this will use the configuration in {}",
+ profile.include_path(&config.src).display()
+ );
+ crate::detail_exit(1);
+ }
+
+ let settings = format!(
+ "# Includes one of the default files in src/bootstrap/defaults\n\
+ profile = \"{}\"\n\
+ changelog-seen = {}\n",
+ profile, VERSION
+ );
+ t!(fs::write(path, settings));
+
+ let include_path = profile.include_path(&config.src);
+ println!("`x.py` will now use the configuration at {}", include_path.display());
}
fn rustup_installed() -> bool {
// install a git hook to automatically run tidy --bless, if they want
fn install_git_hook_maybe(config: &Config) -> io::Result<()> {
+ let git = t!(config.git().args(&["rev-parse", "--git-common-dir"]).output().map(|output| {
+ assert!(output.status.success(), "failed to run `git`");
+ PathBuf::from(t!(String::from_utf8(output.stdout)).trim())
+ }));
+ let dst = git.join("hooks").join("pre-push");
+ if dst.exists() {
+ // The git hook has already been set up, or the user already has a custom hook.
+ return Ok(());
+ }
+
let mut input = String::new();
+ println!();
println!(
"Rust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality.
If you'd like, x.py can install a git hook for you that will automatically run `tidy --bless` before
if should_install {
let src = config.src.join("src").join("etc").join("pre-push.sh");
- let git =
- t!(config.git().args(&["rev-parse", "--git-common-dir"]).output().map(|output| {
- assert!(output.status.success(), "failed to run `git`");
- PathBuf::from(t!(String::from_utf8(output.stdout)).trim())
- }));
- let dst = git.join("hooks").join("pre-push");
match fs::hard_link(src, &dst) {
Err(e) => eprintln!(
"error: could not create hook {}: do you already have the git hook installed?\n{}",
HtmlChecker, "src/tools/html-checker", "html-checker";
BumpStage0, "src/tools/bump-stage0", "bump-stage0";
ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
+ CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
+ GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
);
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- let builder = run.builder;
-
// Allow building `rust-analyzer-proc-macro-srv` both as part of the `rust-analyzer` and as a stand-alone tool.
run.path("src/tools/rust-analyzer")
.path("src/tools/rust-analyzer/crates/proc-macro-srv-cli")
- .default_condition(
- builder.config.extended
- && builder.config.tools.as_ref().map_or(true, |tools| {
- tools.iter().any(|tool| {
- tool == "rust-analyzer" || tool == "rust-analyzer-proc-macro-srv"
- })
- }),
- )
}
fn make_run(run: RunConfig<'_>) {
gdb \
libssl-dev \
pkg-config \
- xz-utils
+ xz-utils \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
wget \
xz \
zlib-devel.i686 \
- zlib-devel.x86_64
+ zlib-devel.x86_64 \
+ && yum clean all
RUN mkdir -p /rustroot/bin && ln -s /usr/bin/cmake3 /rustroot/bin/cmake
ENV HOSTS=powerpc64le-unknown-linux-gnu
-ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
+ENV RUST_CONFIGURE_ARGS --enable-extended --enable-profiler --disable-docs
ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS
set -ex
-# Originally from https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz
-curl https://ci-mirrors.rust-lang.org/rustc/2022-05-10-clang%2Bllvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz | \
+# Originally from https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.6/clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04.tar.xz
+curl https://ci-mirrors.rust-lang.org/rustc/2022-12-06-clang%2Bllvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04.tar.xz | \
tar xJf -
-bin="$PWD/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04/bin"
+bin="$PWD/clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04/bin"
git clone https://github.com/WebAssembly/wasi-libc
cd wasi-libc
-git reset --hard 9886d3d6200fcc3726329966860fc058707406cd
+git reset --hard 8b7148f69ae241a2749b3defe4606da8143b72e0
make -j$(nproc) \
CC="$bin/clang" \
NM="$bin/llvm-nm" \
xz-utils \
wget \
libssl-dev \
- pkg-config
+ pkg-config \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/freebsd-toolchain.sh /tmp/
RUN /tmp/freebsd-toolchain.sh x86_64
apt-get install -y --no-install-recommends \
libgmp-dev \
libmpfr-dev \
- libmpc-dev
+ libmpc-dev \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/illumos-toolchain.sh /tmp/
wget \
xz \
zlib-devel.i686 \
- zlib-devel.x86_64
+ zlib-devel.x86_64 \
+ && yum clean all
RUN mkdir -p /rustroot/bin && ln -s /usr/bin/cmake3 /rustroot/bin/cmake
gdb \
patch \
libssl-dev \
- pkg-config
+ pkg-config \
+ && rm -rf /var/lib/apt/lists/*
WORKDIR /build/
gdb \
zlib1g-dev \
lib32z1-dev \
- xz-utils
+ xz-utils \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
gdb \
zlib1g-dev \
lib32z1-dev \
- xz-utils
+ xz-utils \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
xz-utils \
libssl-dev \
pkg-config \
- mingw-w64
+ mingw-w64 \
+ && rm -rf /var/lib/apt/lists/*
RUN curl -sL https://nodejs.org/dist/v16.9.0/node-v16.9.0-linux-x64.tar.xz | tar -xJ
ENV PATH="/node-v16.9.0-linux-x64/bin:${PATH}"
# Install es-check
# Pin its version to prevent unrelated CI failures due to future es-check versions.
-RUN npm install es-check@6.1.1 -g
-RUN npm install eslint@8.6.0 -g
+RUN npm install es-check@6.1.1 eslint@8.6.0 -g
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
COPY host-x86_64/mingw-check/reuse-requirements.txt /tmp/
-RUN pip3 install --no-deps --require-hashes -r /tmp/reuse-requirements.txt
+RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-requirements.txt
COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/
ovmf \
qemu-efi-aarch64 \
qemu-system-arm \
- qemu-system-x86
+ qemu-system-x86 \
+ && rm -rf /var/lib/apt/lists/*
RUN curl -sL https://nodejs.org/dist/v15.14.0/node-v15.14.0-linux-x64.tar.xz | \
tar -xJ
sudo \
gdb \
xz-utils \
- bzip2
+ bzip2 \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/emscripten.sh /scripts/
RUN bash /scripts/emscripten.sh
llvm-dev \
libfreetype6-dev \
libexpat1-dev \
- tidy
+ tidy \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
pkg-config \
xz-utils \
lld \
- clang
+ clang \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
gdb \
xz-utils \
libssl-dev \
- pkg-config
+ pkg-config \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
pkg-config \
zlib1g-dev \
xz-utils \
- nodejs
+ nodejs \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
pkg-config \
zlib1g-dev \
xz-utils \
- nodejs
-
+ nodejs \
+ \
# Install powershell so we can test x.ps1 on Linux
-RUN apt-get update && \
- apt-get install -y apt-transport-https software-properties-common && \
+ apt-transport-https software-properties-common && \
curl -s "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb" > packages-microsoft-prod.deb && \
dpkg -i packages-microsoft-prod.deb && \
apt-get update && \
- apt-get install -y powershell
+ apt-get install -y powershell \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
gdb \
libssl-dev \
pkg-config \
- xz-utils
+ xz-utils \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
libssl-dev \
sudo \
xz-utils \
- tidy
-
+ tidy \
+ \
# Install dependencies for chromium browser
-RUN apt-get install -y \
gconf-service \
libasound2 \
libatk1.0-0 \
libnss3 \
lsb-release \
xdg-utils \
- wget
+ wget \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
gdb \
libssl-dev \
pkg-config \
- xz-utils
+ xz-utils \
+ && rm -rf /var/lib/apt/lists/*
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
for lib in c cxxrt gcc_s m thr util; do
files_to_extract=("${files_to_extract[@]}" "./lib/lib${lib}.*" "./usr/lib/lib${lib}.*")
done
-for lib in c++ c_nonshared compiler_rt execinfo gcc pthread rt ssp_nonshared procstat devstat kvm; do
+for lib in c++ c_nonshared compiler_rt execinfo gcc pthread rt ssp_nonshared procstat devstat kvm memstat; do
files_to_extract=("${files_to_extract[@]}" "./usr/lib/lib${lib}.*")
done
<<: *base-job
- &job-macos-xl
- os: macos-latest # We don't have an XL builder for this
+ os: macos-12-xl
<<: *base-job
- &job-windows-xl
- name: dist-x86_64-apple
env:
SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin
- RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false
+ RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false --set rust.lto=thin
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
MACOSX_DEPLOYMENT_TARGET: 10.7
SELECT_XCODE: /Applications/Xcode_13.4.1.app
--target=x86_64-pc-windows-msvc
--enable-full-tools
--enable-profiler
+ --set rust.lto=thin
SCRIPT: PGO_HOST=x86_64-pc-windows-msvc src/ci/pgo.sh python x.py dist bootstrap --include-default-paths
DIST_REQUIRE_ALL_TOOLS: 1
<<: *job-windows-xl
-Subproject commit 3f64052c048c6def93b94a2b514ee88bba918744
+Subproject commit a60f4316ec923a5ac2ed6a2eba6960edb832d855
-Subproject commit c533348edd69f11a8f4225d633a05d7093fddbf3
+Subproject commit 19f798d448835a4888e3b3eae7fe69f1d61d8681
-Subproject commit 05532356e7a4dbea2330aabb77611f5179493bb8
+Subproject commit ae406aa5287a9e025abb72343aaceec98458c117
-Subproject commit 9f0cc13ffcd27c1fbe1ab766a9491e15ddcf4d19
+Subproject commit 3ae62681ff236d5528ef7c8c28ba7c6b2ecc6731
-Subproject commit 2b15c0abf2bada6e00553814336bc3e2d8399097
+Subproject commit a9869b4a3c4cac3bc6099b41f088679e268400b8
-Subproject commit d0dc6c97a6486f68bac782fff135086eae6d77ec
+Subproject commit e269950a57fa6fcda356426545fb5aa3691a7ced
pathname syntax. For example `--remap-path-prefix foo=bar` will match
`foo/lib.rs` but not `./foo/lib.rs`.
+When multiple remappings are given and several of them match, the **last**
+matching one is applied.
+
<a id="option-json"></a>
## `--json`: configure json messages printed by the compiler
forward-edge control flow protection.
* [HWAddressSanitizer](#hwaddresssanitizer) a memory error detector similar to
AddressSanitizer, but based on partial hardware assistance.
+* [KernelControlFlowIntegrity](#kernelcontrolflowintegrity) LLVM Kernel Control
+ Flow Integrity (KCFI) provides forward-edge control flow protection for
+ operating systems kernels.
* [LeakSanitizer](#leaksanitizer) a run-time memory leak detector.
* [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads.
* [MemTagSanitizer](#memtagsanitizer) fast memory error detector based on
SUMMARY: HWAddressSanitizer: tag-mismatch (/.../main+0x54a94)
```
+# KernelControlFlowIntegrity
+
+The LLVM Kernel Control Flow Integrity (CFI) support to the Rust compiler
+initially provides forward-edge control flow protection for operating systems
+kernels for Rust-compiled code only by aggregating function pointers in groups
+identified by their return and parameter types. (See [LLVM commit cff5bef "KCFI
+sanitizer"](https://github.com/llvm/llvm-project/commit/cff5bef948c91e4919de8a5fb9765e0edc13f3de).)
+
+Forward-edge control flow protection for C or C++ and Rust -compiled code "mixed
+binaries" (i.e., for when C or C++ and Rust -compiled code share the same
+virtual address space) will be provided in later work by defining and using
+compatible type identifiers (see Type metadata in the design document in the
+tracking issue [#89653](https://github.com/rust-lang/rust/issues/89653)).
+
+LLVM KCFI can be enabled with `-Zsanitizer=kcfi`.
+
+LLVM KCFI is supported on the following targets:
+
+* `aarch64-linux-android`
+* `aarch64-unknown-linux-gnu`
+* `x86_64-linux-android`
+* `x86_64-unknown-linux-gnu`
+
+See the [Clang KernelControlFlowIntegrity documentation][clang-kcfi] for more
+details.
+
# LeakSanitizer
LeakSanitizer is run-time memory leak detector.
[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
[clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html
[clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
+[clang-kcfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html#fsanitize-kcfi
[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html
[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
[clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html
Example:
-```rust
+```rust,ignore (not-all-targets-support-uefi)
#![feature(abi_efiapi)]
extern "efiapi" { fn f1(); }
rayon = "1.5.1"
[dev-dependencies]
-expect-test = "1.0"
+expect-test = "1.4.0"
[features]
jemalloc = []
use rustc_ast as ast;
use rustc_attr as attr;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
})
.collect::<Vec<_>>();
+ let mut bound_predicates = FxIndexMap::default();
+ let mut region_predicates = FxIndexMap::default();
+ let mut eq_predicates = ThinVec::default();
+ for pred in gens.predicates.iter().filter_map(|x| clean_where_predicate(x, cx)) {
+ match pred {
+ WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
+ match bound_predicates.entry(ty) {
+ IndexEntry::Vacant(v) => {
+ v.insert((bounds, bound_params));
+ }
+ IndexEntry::Occupied(mut o) => {
+ // we merge both bounds.
+ for bound in bounds {
+ if !o.get().0.contains(&bound) {
+ o.get_mut().0.push(bound);
+ }
+ }
+ for bound_param in bound_params {
+ if !o.get().1.contains(&bound_param) {
+ o.get_mut().1.push(bound_param);
+ }
+ }
+ }
+ }
+ }
+ WherePredicate::RegionPredicate { lifetime, bounds } => {
+ match region_predicates.entry(lifetime) {
+ IndexEntry::Vacant(v) => {
+ v.insert(bounds);
+ }
+ IndexEntry::Occupied(mut o) => {
+ // we merge both bounds.
+ for bound in bounds {
+ if !o.get().contains(&bound) {
+ o.get_mut().push(bound);
+ }
+ }
+ }
+ }
+ }
+ WherePredicate::EqPredicate { lhs, rhs, bound_params } => {
+ eq_predicates.push(WherePredicate::EqPredicate { lhs, rhs, bound_params });
+ }
+ }
+ }
+
let mut params = ThinVec::with_capacity(gens.params.len());
+ // In this loop, we gather the generic parameters (`<'a, B: 'a>`) and check if they have
+ // bounds in the where predicates. If so, we move their bounds into the where predicates
+ // while also preventing duplicates.
for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
- let p = clean_generic_param(cx, Some(gens), p);
+ let mut p = clean_generic_param(cx, Some(gens), p);
+ match &mut p.kind {
+ GenericParamDefKind::Lifetime { ref mut outlives } => {
+ if let Some(region_pred) = region_predicates.get_mut(&Lifetime(p.name)) {
+ // We merge bounds in the `where` clause.
+ for outlive in outlives.drain(..) {
+ let outlive = GenericBound::Outlives(outlive);
+ if !region_pred.contains(&outlive) {
+ region_pred.push(outlive);
+ }
+ }
+ }
+ }
+ GenericParamDefKind::Type { bounds, synthetic: false, .. } => {
+ if let Some(bound_pred) = bound_predicates.get_mut(&Type::Generic(p.name)) {
+ // We merge bounds in the `where` clause.
+ for bound in bounds.drain(..) {
+ if !bound_pred.0.contains(&bound) {
+ bound_pred.0.push(bound);
+ }
+ }
+ }
+ }
+ GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
+ // nothing to do here.
+ }
+ }
params.push(p);
}
params.extend(impl_trait_params);
- let mut generics = Generics {
+ Generics {
params,
- where_predicates: gens
- .predicates
- .iter()
- .filter_map(|x| clean_where_predicate(x, cx))
+ where_predicates: bound_predicates
+ .into_iter()
+ .map(|(ty, (bounds, bound_params))| WherePredicate::BoundPredicate {
+ ty,
+ bounds,
+ bound_params,
+ })
+ .chain(
+ region_predicates
+ .into_iter()
+ .map(|(lifetime, bounds)| WherePredicate::RegionPredicate { lifetime, bounds }),
+ )
+ .chain(eq_predicates.into_iter())
.collect(),
- };
-
- // Some duplicates are generated for ?Sized bounds between type params and where
- // predicates. The point in here is to move the bounds definitions from type params
- // to where predicates when such cases occur.
- for where_pred in &mut generics.where_predicates {
- match *where_pred {
- WherePredicate::BoundPredicate { ty: Generic(ref name), ref mut bounds, .. } => {
- if bounds.is_empty() {
- for param in &mut generics.params {
- match param.kind {
- GenericParamDefKind::Lifetime { .. } => {}
- GenericParamDefKind::Type { bounds: ref mut ty_bounds, .. } => {
- if ¶m.name == name {
- mem::swap(bounds, ty_bounds);
- break;
- }
- }
- GenericParamDefKind::Const { .. } => {}
- }
- }
- }
- }
- _ => continue,
- }
}
- generics
}
fn clean_ty_generics<'tcx>(
}
fn clean_use_statement<'tcx>(
+ import: &hir::Item<'tcx>,
+ name: Symbol,
+ path: &hir::UsePath<'tcx>,
+ kind: hir::UseKind,
+ cx: &mut DocContext<'tcx>,
+ inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
+) -> Vec<Item> {
+ let mut items = Vec::new();
+ let hir::UsePath { segments, ref res, span } = *path;
+ for &res in res {
+ if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
+ continue;
+ }
+ let path = hir::Path { segments, res, span };
+ items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names));
+ }
+ items
+}
+
+fn clean_use_statement_inner<'tcx>(
import: &hir::Item<'tcx>,
name: Symbol,
path: &hir::Path<'tcx>,
hir::ItemKind::Use(path, hir::UseKind::Single)
if tcx.visibility(id.owner_id).is_public() =>
{
- as_keyword(path.res.expect_non_local())
+ path.res
+ .iter()
+ .find_map(|res| as_keyword(res.expect_non_local()))
.map(|(_, prim)| (id.owner_id.to_def_id(), prim))
}
_ => None,
hir::ItemKind::Use(path, hir::UseKind::Single)
if tcx.visibility(id.owner_id).is_public() =>
{
- as_primitive(path.res.expect_non_local()).map(|(_, prim)| {
+ path.res
+ .iter()
+ .find_map(|res| as_primitive(res.expect_non_local()))
// Pretend the primitive is local.
- (id.owner_id.to_def_id(), prim)
- })
+ .map(|(_, prim)| (id.owner_id.to_def_id(), prim))
}
_ => None,
}
pub(crate) input: PathBuf,
/// The name of the crate being documented.
pub(crate) crate_name: Option<String>,
+ /// Whether or not this is a bin crate
+ pub(crate) bin_crate: bool,
/// Whether or not this is a proc-macro crate
pub(crate) proc_macro_crate: bool,
/// How to format errors and warnings.
f.debug_struct("Options")
.field("input", &self.input)
.field("crate_name", &self.crate_name)
+ .field("bin_crate", &self.bin_crate)
.field("proc_macro_crate", &self.proc_macro_crate)
.field("error_format", &self.error_format)
.field("libs", &self.libs)
None => OutputFormat::default(),
};
let crate_name = matches.opt_str("crate-name");
+ let bin_crate = crate_types.contains(&CrateType::Executable);
let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
let playground_url = matches.opt_str("playground-url");
let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref());
let options = Options {
input,
+ bin_crate,
proc_macro_crate,
error_format,
diagnostic_width,
self.tcx.hir()
}
- fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
+ fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
debug!("visiting path {:?}", path);
if path.res == Res::Err {
// We have less context here than in rustc_resolve,
use super::format::{self, Buffer};
/// This type is needed in case we want to render links on items to allow to go to their definition.
-pub(crate) struct HrefContext<'a, 'b, 'c> {
- pub(crate) context: &'a Context<'b>,
+pub(crate) struct HrefContext<'a, 'tcx> {
+ pub(crate) context: &'a Context<'tcx>,
/// This span contains the current file we're going through.
pub(crate) file_span: Span,
/// This field is used to know "how far" from the top of the directory we are to link to either
/// documentation pages or other source pages.
- pub(crate) root_path: &'c str,
+ pub(crate) root_path: &'a str,
/// This field is used to calculate precise local URLs.
- pub(crate) current_href: &'c str,
+ pub(crate) current_href: String,
}
/// Decorations are represented as a map from CSS class to vector of character ranges.
src: &str,
out: &mut Buffer,
line_numbers: Buffer,
- href_context: HrefContext<'_, '_, '_>,
+ href_context: HrefContext<'_, '_>,
decoration_info: DecorationInfo,
extra: Option<&str>,
) {
/// This type is used as a conveniency to prevent having to pass all its fields as arguments into
/// the various functions (which became its methods).
-struct TokenHandler<'a, 'b, 'c, 'd, 'e> {
+struct TokenHandler<'a, 'tcx> {
out: &'a mut Buffer,
/// It contains the closing tag and the associated `Class`.
closing_tags: Vec<(&'static str, Class)>,
current_class: Option<Class>,
/// We need to keep the `Class` for each element because it could contain a `Span` which is
/// used to generate links.
- pending_elems: Vec<(&'b str, Option<Class>)>,
- href_context: Option<HrefContext<'c, 'd, 'e>>,
+ pending_elems: Vec<(&'a str, Option<Class>)>,
+ href_context: Option<HrefContext<'a, 'tcx>>,
}
-impl<'a, 'b, 'c, 'd, 'e> TokenHandler<'a, 'b, 'c, 'd, 'e> {
+impl<'a, 'tcx> TokenHandler<'a, 'tcx> {
fn handle_exit_span(&mut self) {
// We can't get the last `closing_tags` element using `pop()` because `closing_tags` is
// being used in `write_pending_elems`.
}
}
-impl<'a, 'b, 'c, 'd, 'e> Drop for TokenHandler<'a, 'b, 'c, 'd, 'e> {
+impl<'a, 'tcx> Drop for TokenHandler<'a, 'tcx> {
/// When leaving, we need to flush all pending data to not have missing content.
fn drop(&mut self) {
if self.pending_exit_span.is_some() {
fn write_code(
out: &mut Buffer,
src: &str,
- href_context: Option<HrefContext<'_, '_, '_>>,
+ href_context: Option<HrefContext<'_, '_>>,
decoration_info: Option<DecorationInfo>,
) {
// This replace allows to fix how the code source with DOS backline characters is displayed.
/// Processes program tokens, classifying strings of text by highlighting
/// category (`Class`).
-struct Classifier<'a> {
- tokens: PeekIter<'a>,
+struct Classifier<'src> {
+ tokens: PeekIter<'src>,
in_attribute: bool,
in_macro: bool,
in_macro_nonterminal: bool,
byte_pos: u32,
file_span: Span,
- src: &'a str,
+ src: &'src str,
decorations: Option<Decorations>,
}
-impl<'a> Classifier<'a> {
+impl<'src> Classifier<'src> {
/// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
/// file span which will be used later on by the `span_correspondance_map`.
fn new(src: &str, file_span: Span, decoration_info: Option<DecorationInfo>) -> Classifier<'_> {
///
/// It returns the token's kind, the token as a string and its byte position in the source
/// string.
- fn next(&mut self) -> Option<(TokenKind, &'a str, u32)> {
+ fn next(&mut self) -> Option<(TokenKind, &'src str, u32)> {
if let Some((kind, text)) = self.tokens.next() {
let before = self.byte_pos;
self.byte_pos += text.len() as u32;
/// The general structure for this method is to iterate over each token,
/// possibly giving it an HTML span with a class specifying what flavor of
/// token is used.
- fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'a>)) {
+ fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'src>)) {
loop {
if let Some(decs) = self.decorations.as_mut() {
let byte_pos = self.byte_pos;
fn advance(
&mut self,
token: TokenKind,
- text: &'a str,
- sink: &mut dyn FnMut(Highlight<'a>),
+ text: &'src str,
+ sink: &mut dyn FnMut(Highlight<'src>),
before: u32,
) {
let lookahead = self.peek();
fn enter_span(
out: &mut Buffer,
klass: Class,
- href_context: &Option<HrefContext<'_, '_, '_>>,
+ href_context: &Option<HrefContext<'_, '_>>,
) -> &'static str {
string_without_closing_tag(out, "", Some(klass), href_context, true).expect(
"internal error: enter_span was called with Some(klass) but did not return a \
out: &mut Buffer,
text: T,
klass: Option<Class>,
- href_context: &Option<HrefContext<'_, '_, '_>>,
+ href_context: &Option<HrefContext<'_, '_>>,
open_tag: bool,
) {
if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag)
out: &mut Buffer,
text: T,
klass: Option<Class>,
- href_context: &Option<HrefContext<'_, '_, '_>>,
+ href_context: &Option<HrefContext<'_, '_>>,
open_tag: bool,
) -> Option<&'static str> {
let Some(klass) = klass
// https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338
match href {
LinkFromSrc::Local(span) => {
- context.href_from_span_relative(*span, href_context.current_href)
+ context.href_from_span_relative(*span, &href_context.current_href)
}
LinkFromSrc::External(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
_ => {}
}
}
- let lines = origtext.lines().filter_map(|l| map_line(l).for_html());
- let text = lines.intersperse("\n".into()).collect::<String>();
let parse_result = match kind {
CodeBlockKind::Fenced(ref lang) => {
<pre class=\"language-{}\"><code>{}</code></pre>\
</div>",
lang,
- Escape(&text),
+ Escape(&origtext),
)
.into(),
));
CodeBlockKind::Indented => Default::default(),
};
+ let lines = origtext.lines().filter_map(|l| map_line(l).for_html());
+ let text = lines.intersperse("\n".into()).collect::<String>();
+
compile_fail = parse_result.compile_fail;
should_panic = parse_result.should_panic;
ignore = parse_result.ignore;
t("```rust\n```\n```rust\n```", &[1, 3]);
t("```rust\n```\n ```rust\n```", &[1, 3]);
}
+
+#[test]
+fn test_ascii_with_prepending_hashtag() {
+ fn t(input: &str, expect: &str) {
+ let mut map = IdMap::new();
+ let output = Markdown {
+ content: input,
+ links: &[],
+ ids: &mut map,
+ error_codes: ErrorCodes::Yes,
+ edition: DEFAULT_EDITION,
+ playground: &None,
+ heading_offset: HeadingOffset::H2,
+ }
+ .into_string();
+ assert_eq!(output, expect, "original: {}", input);
+ }
+
+ t(
+ r#"```ascii
+#..#.####.#....#.....##..
+#..#.#....#....#....#..#.
+####.###..#....#....#..#.
+#..#.#....#....#....#..#.
+#..#.#....#....#....#..#.
+#..#.####.####.####..##..
+```"#,
+ "<div class=\"example-wrap\"><pre class=\"language-ascii\"><code>\
+#..#.####.#....#.....##..
+#..#.#....#....#....#..#.
+####.###..#....#....#..#.
+#..#.#....#....#....#..#.
+#..#.#....#....#....#..#.
+#..#.####.####.####..##..
+</code></pre></div>",
+ );
+ t(
+ r#"```markdown
+# hello
+```"#,
+ "<div class=\"example-wrap\"><pre class=\"language-markdown\"><code>\
+# hello
+</code></pre></div>",
+ );
+}
// The call locations are output in sequence, so that sequence needs to be determined.
// Ideally the most "relevant" examples would be shown first, but there's no general algorithm
- // for determining relevance. Instead, we prefer the smallest examples being likely the easiest to
- // understand at a glance.
+ // for determining relevance. We instead proxy relevance with the following heuristics:
+ // 1. Code written to be an example is better than code not written to be an example, e.g.
+ // a snippet from examples/foo.rs is better than src/lib.rs. We don't know the Cargo
+ // directory structure in Rustdoc, so we proxy this by prioritizing code that comes from
+ // a --crate-type bin.
+ // 2. Smaller examples are better than large examples. So we prioritize snippets that have
+ // the smallest number of lines in their enclosing item.
+ // 3. Finally we sort by the displayed file name, which is arbitrary but prevents the
+ // ordering of examples from randomly changing between Rustdoc invocations.
let ordered_locations = {
- let sort_criterion = |(_, call_data): &(_, &CallData)| {
+ fn sort_criterion<'a>(
+ (_, call_data): &(&PathBuf, &'a CallData),
+ ) -> (bool, u32, &'a String) {
// Use the first location because that's what the user will see initially
let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
- hi - lo
- };
+ (!call_data.is_bin, hi - lo, &call_data.display_name)
+ }
let mut locs = call_locations.iter().collect::<Vec<_>>();
locs.sort_by_key(sort_criterion);
self.tcx.hir()
}
- fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
+ fn visit_path(&mut self, path: &rustc_hir::Path<'tcx>, _id: HirId) {
if self.handle_macro(path.span) {
return;
}
}
intravisit::walk_expr(self, expr);
}
-
- fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) {
- if self.handle_macro(path.span) {
- return;
- }
- self.handle_path(path);
- intravisit::walk_use(self, path, id);
- }
}
let mut line_numbers = Buffer::empty_from(buf);
let extra;
line_numbers.write_str("<pre class=\"src-line-numbers\">");
- let current_href = &context
+ let current_href = context
.href_from_span(clean::Span::new(file_span), false)
.expect("only local crates should have sources emitted");
match source_context {
span.since,
a.srclink,
#help-button > a,
-details.rustdoc-toggle.top-doc > summary,
-details.rustdoc-toggle.non-exhaustive > summary,
-.scraped-example-title,
-.more-examples-toggle summary, .more-examples-toggle .hide-more,
-.example-links a,
+summary.hideme,
+.scraped-example-list,
/* This selector is for the items listed in the "all items" page. */
ul.all-items {
font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
.mobile-topbar h2 a,
h1 a,
.search-results a,
-.module-item .stab,
-.import-item .stab,
+.stab,
.result-name .primitive > i, .result-name .keyword > i {
color: var(--main-color);
}
.docblock table {
margin: .5em 0;
- width: calc(100% - 2px);
- overflow-x: auto;
- display: block;
border-collapse: collapse;
}
-.docblock table td {
+.docblock table td, .docblock table th {
padding: .5em;
- border: 1px dashed var(--border-color);
- vertical-align: top;
+ border: 1px solid var(--border-color);
}
-.docblock table th {
- padding: .5em;
- text-align: left;
- border: 1px solid var(--border-color);
+.docblock table tbody tr:nth-child(2n) {
+ background: var(--table-alt-row-background-color);
}
/* Shift "where ..." part of method or fn definition down a line */
.popover {
position: absolute;
+ top: 100%;
right: 0;
z-index: 2;
display: block;
}
.item-info .stab {
- width: fit-content;
/* This min-height is needed to unify the height of the stab elements because some of them
have emojis.
*/
min-height: 36px;
display: flex;
- align-items: center;
- white-space: pre-wrap;
-}
-.stab {
padding: 3px;
margin-bottom: 5px;
+}
+.item-left .stab {
+ margin-left: 0.3125em;
+}
+.stab {
+ padding: 0 2px;
font-size: 0.875rem;
font-weight: normal;
color: var(--main-color);
background-color: var(--stab-background-color);
+ width: fit-content;
+ align-items: center;
+ white-space: pre-wrap;
+ border-radius: 3px;
+ display: inline-flex;
+ vertical-align: text-bottom;
}
.stab.portability > code {
margin-right: 0.3rem;
}
-/* This is to prevent the `.stab` elements to overflow the .docblock elements. */
-.docblock .stab {
- padding: 0 0.125em;
- margin-bottom: 0;
-}
-
/* Black one-pixel outline around emoji shapes */
.emoji {
text-shadow:
0 -1px 0 black;
}
-.module-item .stab,
-.import-item .stab {
- border-radius: 3px;
- display: inline-block;
- font-size: 0.875rem;
- line-height: 1.2;
- margin-bottom: 0;
- margin-left: 0.3125em;
- padding: 2px;
- vertical-align: text-bottom;
-}
-
.module-item.unstable,
.import-item.unstable {
opacity: 0.65;
}
#settings-menu, #help-button {
margin-left: 4px;
- outline: none;
+ display: flex;
}
#settings-menu > a, #help-button > a, #copy-path {
width: 33px;
- line-height: 1.5;
}
#settings-menu > a, #help-button > a {
- padding: 5px;
- height: 100%;
- display: block;
+ display: flex;
+ align-items: center;
+ justify-content: center;
background-color: var(--button-background-color);
border: 1px solid var(--border-color);
border-radius: 2px;
color: var(--settings-button-color);
+ /* Rare exception to specifying font sizes in rem. Since this is acting
+ as an icon, it's okay to specify their sizes in pixels. */
+ font-size: 20px;
}
#settings-menu > a:hover, #settings-menu > a:focus,
animation: rotating 2s linear infinite;
}
-#help-button > a {
- text-align: center;
- /* Rare exception to specifying font sizes in rem. Since this is acting
- as an icon, it's okay to specify their sizes in pixels. */
- font-size: 20px;
- padding-top: 2px;
-}
-
kbd {
display: inline-block;
padding: 3px 5px;
"Expand description" or "Show methods". */
details.rustdoc-toggle > summary.hideme {
cursor: pointer;
+ font-size: 1rem;
}
details.rustdoc-toggle > summary {
display: inline-block;
vertical-align: middle;
opacity: .5;
+ filter: var(--toggle-filter);
}
details.rustdoc-toggle > summary.hideme > span,
outline-offset: 1px;
}
-details.rustdoc-toggle.top-doc > summary,
-details.rustdoc-toggle.top-doc > summary::before,
-details.rustdoc-toggle.non-exhaustive > summary,
-details.rustdoc-toggle.non-exhaustive > summary::before {
- font-size: 1rem;
-}
-
details.non-exhaustive {
margin-bottom: 8px;
}
display: none;
}
-details.rustdoc-toggle[open] > summary::before,
-details.rustdoc-toggle[open] > summary.hideme::before {
+details.rustdoc-toggle[open] > summary::before {
background: url("toggle-minus-31bbd6e4c77f5c96.svg") no-repeat top left;
- width: 16px;
- height: 16px;
- display: inline-block;
- content: "";
}
-details.rustdoc-toggle[open] > summary::after,
-details.rustdoc-toggle[open] > summary.hideme::after {
+details.rustdoc-toggle[open] > summary::after {
content: "Collapse";
}
content: "Since ";
}
- #copy-path {
- display: none;
- }
-
/* Hide the logo and item name from the sidebar. Those are displayed
in the mobile-topbar instead. */
.sidebar .sidebar-logo,
/* Hide the sidebar offscreen while not in use. Doing this instead of display: none means
the sidebar stays visible for screen readers, which is useful for navigation. */
left: -1000px;
- margin: 0;
- padding: 0;
z-index: 11;
/* Reduce height slightly to account for mobile topbar. */
height: calc(100vh - 45px);
top: 100px;
width: 30px;
font-size: 1.5rem;
- text-align: center;
padding: 0;
z-index: 10;
border-top-right-radius: 3px;
border-bottom: 1px solid;
}
- /* We don't display the help button on mobile devices. */
- #help-button {
+ /* We don't display these buttons on mobile devices. */
+ #copy-path, #help-button {
display: none;
}
}
}
+/* Should have min-width: (N + 1)px where N is the mobile breakpoint above. */
+@media (min-width: 701px) {
+ /* Places file-link for a scraped example on top of the example to save space.
+ We only do this on large screens so the file-link doesn't overlap too much
+ with the example's content. */
+ .scraped-example-title {
+ position: absolute;
+ z-index: 10;
+ background: var(--main-background-color);
+ bottom: 8px;
+ right: 5px;
+ padding: 2px 4px;
+ box-shadow: 0 0 4px var(--main-background-color);
+ }
+}
+
@media print {
nav.sidebar, nav.sub, .out-of-band, a.srclink, #copy-path,
details.rustdoc-toggle[open] > summary::before, details.rustdoc-toggle > summary::before,
border-radius: 50px;
}
+.scraped-example {
+ /* So .scraped-example-title can be positioned absolutely */
+ position: relative;
+}
+
.scraped-example .code-wrapper {
position: relative;
display: flex;
}
.scraped-example:not(.expanded) .code-wrapper {
- max-height: 240px;
+ /* scrape-examples.js has a constant DEFAULT_MAX_LINES (call it N) for the number
+ * of lines shown in the un-expanded example code viewer. This pre needs to have
+ * a max-height equal to line-height * N. The line-height is currently 1.5em,
+ * and we include additional 10px for padding. */
+ max-height: calc(1.5em * 5 + 10px);
}
.scraped-example:not(.expanded) .code-wrapper pre {
overflow-y: hidden;
- max-height: 240px;
padding-bottom: 0;
+ /* See above comment, should be the same max-height. */
+ max-height: calc(1.5em * 5 + 10px);
+}
+
+.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,
+.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre {
+ /* See above comment, except this height is based on HIDDEN_MAX_LINES. */
+ max-height: calc(1.5em * 10 + 10px);
}
.scraped-example .code-wrapper .next,
.scraped-example .code-wrapper .prev,
.scraped-example .code-wrapper .expand {
+ color: var(--main-color);
position: absolute;
top: 0.25em;
z-index: 1;
}
.scraped-example .code-wrapper .example-wrap {
- flex: 1;
+ display: grid;
+ grid-template-columns: max-content auto;
+ width: 100%;
overflow-x: auto;
overflow-y: hidden;
margin-bottom: 0;
--right-side-color: grey;
--code-attribute-color: #999;
--toggles-color: #999;
+ --toggle-filter: invert(100%);
--search-input-focused-border-color: #5c6773; /* Same as `--border-color`. */
--copy-path-button-color: #fff;
--copy-path-img-filter: invert(70%);
--crate-search-hover-border: #e0e0e0;
--source-sidebar-background-selected: #14191f;
--source-sidebar-background-hover: #14191f;
+ --table-alt-row-background-color: #191f26;
}
h1, h2, h3, h4 {
background: #333;
}
-details.rustdoc-toggle > summary::before {
- filter: invert(100%);
-}
-
-.module-item .stab,
-.import-item .stab {
- color: #000;
-}
-
.result-name .primitive > i, .result-name .keyword > i {
color: #788797;
}
--right-side-color: grey;
--code-attribute-color: #999;
--toggles-color: #999;
+ --toggle-filter: invert(100%);
--search-input-focused-border-color: #008dfd;
--copy-path-button-color: #999;
--copy-path-img-filter: invert(50%);
--crate-search-hover-border: #2196f3;
--source-sidebar-background-selected: #333;
--source-sidebar-background-hover: #444;
+ --table-alt-row-background-color: #2A2A2A;
}
.content .item-info::before { color: #ccc; }
background: #333;
}
-details.rustdoc-toggle > summary::before {
- filter: invert(100%);
-}
-
#titles > button:not(.selected) {
background-color: #252525;
border-top-color: #252525;
--right-side-color: grey;
--code-attribute-color: #999;
--toggles-color: #999;
+ --toggle-filter: none;
--search-input-focused-border-color: #66afe9;
--copy-path-button-color: #999;
--copy-path-img-filter: invert(50%);
--crate-search-hover-border: #717171;
--source-sidebar-background-selected: #fff;
--source-sidebar-background-hover: #e0e0e0;
+ --table-alt-row-background-color: #F5F5F5;
}
.content .item-info::before { color: #ccc; }
const innerToggle = document.getElementById(toggleAllDocsId);
removeClass(innerToggle, "will-expand");
onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
- if (!hasClass(e, "type-contents-toggle")) {
+ if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) {
e.open = true;
}
});
"use strict";
(function() {
- // Number of lines shown when code viewer is not expanded
- const MAX_LINES = 10;
+ // Number of lines shown when code viewer is not expanded.
+ // DEFAULT is the first example shown by default, while HIDDEN is
+ // the examples hidden beneath the "More examples" toggle.
+ //
+ // NOTE: these values MUST be synchronized with certain rules in rustdoc.css!
+ const DEFAULT_MAX_LINES = 5;
+ const HIDDEN_MAX_LINES = 10;
// Scroll code block to the given code location
- function scrollToLoc(elt, loc) {
+ function scrollToLoc(elt, loc, isHidden) {
const lines = elt.querySelector(".src-line-numbers");
let scrollOffset;
// If the block is greater than the size of the viewer,
// then scroll to the top of the block. Otherwise scroll
// to the middle of the block.
- if (loc[1] - loc[0] > MAX_LINES) {
+ const maxLines = isHidden ? HIDDEN_MAX_LINES : DEFAULT_MAX_LINES;
+ if (loc[1] - loc[0] > maxLines) {
const line = Math.max(0, loc[0] - 1);
scrollOffset = lines.children[line].offsetTop;
} else {
const wrapper = elt.querySelector(".code-wrapper");
const halfHeight = wrapper.offsetHeight / 2;
- const offsetMid = (lines.children[loc[0]].offsetTop
- + lines.children[loc[1]].offsetTop) / 2;
+ const offsetTop = lines.children[loc[0]].offsetTop;
+ const lastLine = lines.children[loc[1]];
+ const offsetBot = lastLine.offsetTop + lastLine.offsetHeight;
+ const offsetMid = (offsetTop + offsetBot) / 2;
scrollOffset = offsetMid - halfHeight;
}
elt.querySelector(".rust").scrollTo(0, scrollOffset);
}
- function updateScrapedExample(example) {
+ function updateScrapedExample(example, isHidden) {
const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
let locIndex = 0;
const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight"));
const onChangeLoc = changeIndex => {
removeClass(highlights[locIndex], "focus");
changeIndex();
- scrollToLoc(example, locs[locIndex][0]);
+ scrollToLoc(example, locs[locIndex][0], isHidden);
addClass(highlights[locIndex], "focus");
const url = locs[locIndex][1];
});
});
- example.querySelector("next")
+ example.querySelector(".next")
.addEventListener("click", () => {
onChangeLoc(() => {
locIndex = (locIndex + 1) % locs.length;
expandButton.addEventListener("click", () => {
if (hasClass(example, "expanded")) {
removeClass(example, "expanded");
- scrollToLoc(example, locs[0][0]);
+ scrollToLoc(example, locs[0][0], isHidden);
} else {
addClass(example, "expanded");
}
}
// Start with the first example in view
- scrollToLoc(example, locs[0][0]);
+ scrollToLoc(example, locs[0][0], isHidden);
}
const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example");
- onEachLazy(firstExamples, updateScrapedExample);
+ onEachLazy(firstExamples, el => updateScrapedExample(el, false));
onEachLazy(document.querySelectorAll(".more-examples-toggle"), toggle => {
// Allow users to click the left border of the <details> section to close it,
// since the section can be large and finding the [+] button is annoying.
// depends on offsetHeight, a property that requires an element to be visible to
// compute correctly.
setTimeout(() => {
- onEachLazy(moreExamples, updateScrapedExample);
+ onEachLazy(moreExamples, el => updateScrapedExample(el, true));
});
}, {once: true});
});
-Rustdoc will automatically scrape examples of documented items from the `examples/` directory of a project. These examples will be included within the generated documentation for that item. For example, if your library contains a public function:
+Rustdoc will automatically scrape examples of documented items from a project's source code. These examples will be included within the generated documentation for that item. For example, if your library contains a public function:
```rust
// src/lib.rs
Then this code snippet will be included in the documentation for `a_func`.
+
## How to read scraped examples
Scraped examples are shown as blocks of code from a given file. The relevant item will be highlighted. If the file is larger than a couple lines, only a small window will be shown which you can expand by clicking ↕ in the top-right. If a file contains multiple instances of an item, you can use the ≺ and ≻ buttons to toggle through each instance.
## How Rustdoc scrapes examples
-When you run `cargo doc`, Rustdoc will analyze all the crates that match Cargo's `--examples` filter for instances of items that occur in the crates being documented. Then Rustdoc will include the source code of these instances in the generated documentation.
+When you run `cargo doc -Zunstable-options -Zrustdoc-scrape-examples`, Rustdoc will analyze all the documented crates for uses of documented items. Then Rustdoc will include the source code of these instances in the generated documentation.
Rustdoc has a few techniques to ensure this doesn't overwhelm documentation readers, and that it doesn't blow up the page size:
nanum_barun_gothic_license => "static/fonts/NanumBarunGothic-LICENSE.txt",
}
-pub(crate) static SCRAPE_EXAMPLES_HELP_MD: &str = include_str!("static/js/scrape-examples.js");
+pub(crate) static SCRAPE_EXAMPLES_HELP_MD: &str = include_str!("static/scrape-examples-help.md");
})
.unwrap_or_default()
}
-
- fn get_trait_items(&mut self) -> Vec<(types::Id, types::Item)> {
- debug!("Adding foreign trait items");
- Rc::clone(&self.cache)
- .traits
- .iter()
- .filter_map(|(&id, trait_item)| {
- // only need to synthesize items for external traits
- if !id.is_local() {
- for item in &trait_item.items {
- trace!("Adding subitem to {id:?}: {:?}", item.item_id);
- self.item(item.clone()).unwrap();
- }
- let item_id = from_item_id(id.into(), self.tcx);
- Some((
- item_id.clone(),
- types::Item {
- id: item_id,
- crate_id: id.krate.as_u32(),
- name: self
- .cache
- .paths
- .get(&id)
- .unwrap_or_else(|| {
- self.cache
- .external_paths
- .get(&id)
- .expect("Trait should either be in local or external paths")
- })
- .0
- .last()
- .map(|s| s.to_string()),
- visibility: types::Visibility::Public,
- inner: types::ItemEnum::Trait(trait_item.clone().into_tcx(self.tcx)),
- span: None,
- docs: Default::default(),
- links: Default::default(),
- attrs: Default::default(),
- deprecation: Default::default(),
- },
- ))
- } else {
- None
- }
- })
- .collect()
- }
}
impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
types::ItemEnum::Function(_)
| types::ItemEnum::Module(_)
+ | types::ItemEnum::Import(_)
| types::ItemEnum::AssocConst { .. }
| types::ItemEnum::AssocType { .. } => true,
types::ItemEnum::ExternCrate { .. }
- | types::ItemEnum::Import(_)
| types::ItemEnum::StructField(_)
| types::ItemEnum::Variant(_)
| types::ItemEnum::TraitAlias(_)
let e = ExternalCrate { crate_num: LOCAL_CRATE };
- // FIXME(adotinthevoid): Remove this, as it's not consistent with not
- // inlining foreign items.
- let foreign_trait_items = self.get_trait_items();
- let mut index = (*self.index).clone().into_inner();
- index.extend(foreign_trait_items);
+ let index = (*self.index).clone().into_inner();
debug!("Constructing Output");
// This needs to be the default HashMap for compatibility with the public interface for
fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> MainResult {
match res {
- Ok(()) => Ok(()),
+ Ok(()) => diag.has_errors().map_or(Ok(()), Err),
Err(err) => {
let reported = diag.struct_err(&err).emit();
Err(reported)
tcx: TyCtxt<'tcx>,
) -> MainResult {
match formats::run_format::<T>(krate, renderopts, cache, tcx) {
- Ok(_) => Ok(()),
+ Ok(_) => tcx.sess.has_errors().map_or(Ok(()), Err),
Err(e) => {
let mut msg =
tcx.sess.struct_err(&format!("couldn't generate documentation: {}", e.error));
let output_format = options.output_format;
let externs = options.externs.clone();
let scrape_examples_options = options.scrape_examples_options.clone();
+ let bin_crate = options.bin_crate;
let config = core::create_config(options);
let sess = compiler.session();
if sess.opts.describe_lints {
- let mut lint_store = rustc_lint::new_lint_store(
- sess.opts.unstable_opts.no_interleave_lints,
- sess.enable_internal_lints(),
- );
+ let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints());
let registered_lints = if let Some(register_lints) = compiler.register_lints() {
register_lints(sess, &mut lint_store);
true
info!("finished with rustc");
if let Some(options) = scrape_examples_options {
- return scrape_examples::run(krate, render_opts, cache, tcx, options);
+ return scrape_examples::run(
+ krate,
+ render_opts,
+ cache,
+ tcx,
+ options,
+ bin_crate,
+ );
}
cache.crate_version = crate_version;
+++ /dev/null
-//! Detects links that are not linkified, e.g., in Markdown such as `Go to https://example.com/.`
-//! Suggests wrapping the link with angle brackets: `Go to <https://example.com/>.` to linkify it.
-use super::Pass;
-use crate::clean::*;
-use crate::core::DocContext;
-use crate::html::markdown::main_body_opts;
-use crate::visit::DocVisitor;
-use core::ops::Range;
-use pulldown_cmark::{Event, Parser, Tag};
-use regex::Regex;
-use rustc_errors::Applicability;
-use std::mem;
-use std::sync::LazyLock;
-
-pub(crate) const CHECK_BARE_URLS: Pass = Pass {
- name: "check-bare-urls",
- run: check_bare_urls,
- description: "detects URLs that are not hyperlinks",
-};
-
-static URL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
- Regex::new(concat!(
- r"https?://", // url scheme
- r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains
- r"[a-zA-Z]{2,63}", // root domain
- r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments
- ))
- .expect("failed to build regex")
-});
-
-struct BareUrlsLinter<'a, 'tcx> {
- cx: &'a mut DocContext<'tcx>,
-}
-
-impl<'a, 'tcx> BareUrlsLinter<'a, 'tcx> {
- fn find_raw_urls(
- &self,
- text: &str,
- range: Range<usize>,
- f: &impl Fn(&DocContext<'_>, &str, &str, Range<usize>),
- ) {
- trace!("looking for raw urls in {}", text);
- // For now, we only check "full" URLs (meaning, starting with "http://" or "https://").
- for match_ in URL_REGEX.find_iter(text) {
- let url = match_.as_str();
- let url_range = match_.range();
- f(
- self.cx,
- "this URL is not a hyperlink",
- url,
- Range { start: range.start + url_range.start, end: range.start + url_range.end },
- );
- }
- }
-}
-
-pub(crate) fn check_bare_urls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
- BareUrlsLinter { cx }.visit_crate(&krate);
- krate
-}
-
-impl<'a, 'tcx> DocVisitor for BareUrlsLinter<'a, 'tcx> {
- fn visit_item(&mut self, item: &Item) {
- let Some(hir_id) = DocContext::as_local_hir_id(self.cx.tcx, item.item_id)
- else {
- // If non-local, no need to check anything.
- return;
- };
- let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
- if !dox.is_empty() {
- let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| {
- let sp = super::source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
- .unwrap_or_else(|| item.attr_span(cx.tcx));
- cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, msg, |lint| {
- lint.note("bare URLs are not automatically turned into clickable links")
- .span_suggestion(
- sp,
- "use an automatic link instead",
- format!("<{}>", url),
- Applicability::MachineApplicable,
- )
- });
- };
-
- let mut p = Parser::new_ext(&dox, main_body_opts()).into_offset_iter();
-
- while let Some((event, range)) = p.next() {
- match event {
- Event::Text(s) => self.find_raw_urls(&s, range, &report_diag),
- // We don't want to check the text inside code blocks or links.
- Event::Start(tag @ (Tag::CodeBlock(_) | Tag::Link(..))) => {
- while let Some((event, _)) = p.next() {
- match event {
- Event::End(end)
- if mem::discriminant(&end) == mem::discriminant(&tag) =>
- {
- break;
- }
- _ => {}
- }
- }
- }
- _ => {}
- }
- }
- }
-
- self.visit_item_recur(item)
- }
-}
+++ /dev/null
-//! Validates syntax inside Rust code blocks (\`\`\`rust).
-use rustc_data_structures::sync::{Lock, Lrc};
-use rustc_errors::{
- emitter::Emitter,
- translation::{to_fluent_args, Translate},
- Applicability, Diagnostic, Handler, LazyFallbackBundle,
-};
-use rustc_parse::parse_stream_from_source_str;
-use rustc_session::parse::ParseSess;
-use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
-use rustc_span::source_map::{FilePathMapping, SourceMap};
-use rustc_span::{FileName, InnerSpan, DUMMY_SP};
-
-use crate::clean;
-use crate::core::DocContext;
-use crate::html::markdown::{self, RustCodeBlock};
-use crate::passes::Pass;
-use crate::visit::DocVisitor;
-
-pub(crate) const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass {
- name: "check-code-block-syntax",
- run: check_code_block_syntax,
- description: "validates syntax inside Rust code blocks",
-};
-
-pub(crate) fn check_code_block_syntax(
- krate: clean::Crate,
- cx: &mut DocContext<'_>,
-) -> clean::Crate {
- SyntaxChecker { cx }.visit_crate(&krate);
- krate
-}
-
-struct SyntaxChecker<'a, 'tcx> {
- cx: &'a DocContext<'tcx>,
-}
-
-impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
- fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeBlock) {
- let buffer = Lrc::new(Lock::new(Buffer::default()));
- let fallback_bundle =
- rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
- let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle };
-
- let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
- let handler = Handler::with_emitter(false, None, Box::new(emitter));
- let source = dox[code_block.code].to_owned();
- let sess = ParseSess::with_span_handler(handler, sm);
-
- let edition = code_block.lang_string.edition.unwrap_or_else(|| self.cx.tcx.sess.edition());
- let expn_data = ExpnData::default(
- ExpnKind::AstPass(AstPass::TestHarness),
- DUMMY_SP,
- edition,
- None,
- None,
- );
- let expn_id =
- self.cx.tcx.with_stable_hashing_context(|hcx| LocalExpnId::fresh(expn_data, hcx));
- let span = DUMMY_SP.fresh_expansion(expn_id);
-
- let is_empty = rustc_driver::catch_fatal_errors(|| {
- parse_stream_from_source_str(
- FileName::Custom(String::from("doctest")),
- source,
- &sess,
- Some(span),
- )
- .is_empty()
- })
- .unwrap_or(false);
- let buffer = buffer.borrow();
-
- if !buffer.has_errors && !is_empty {
- // No errors in a non-empty program.
- return;
- }
-
- let Some(local_id) = item.item_id.as_def_id().and_then(|x| x.as_local())
- else {
- // We don't need to check the syntax for other crates so returning
- // without doing anything should not be a problem.
- return;
- };
-
- let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_id);
- let empty_block = code_block.lang_string == Default::default() && code_block.is_fenced;
- let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None;
-
- // The span and whether it is precise or not.
- let (sp, precise_span) = match super::source_span_for_markdown_range(
- self.cx.tcx,
- dox,
- &code_block.range,
- &item.attrs,
- ) {
- Some(sp) => (sp, true),
- None => (item.attr_span(self.cx.tcx), false),
- };
-
- let msg = if buffer.has_errors {
- "could not parse code block as Rust code"
- } else {
- "Rust code block is empty"
- };
-
- // Finally build and emit the completed diagnostic.
- // All points of divergence have been handled earlier so this can be
- // done the same way whether the span is precise or not.
- self.cx.tcx.struct_span_lint_hir(
- crate::lint::INVALID_RUST_CODEBLOCKS,
- hir_id,
- sp,
- msg,
- |lint| {
- let explanation = if is_ignore {
- "`ignore` code blocks require valid Rust code for syntax highlighting; \
- mark blocks that do not contain Rust code as text"
- } else {
- "mark blocks that do not contain Rust code as text"
- };
-
- if precise_span {
- if is_ignore {
- // giving an accurate suggestion is hard because `ignore` might not have come first in the list.
- // just give a `help` instead.
- lint.span_help(
- sp.from_inner(InnerSpan::new(0, 3)),
- &format!("{}: ```text", explanation),
- );
- } else if empty_block {
- lint.span_suggestion(
- sp.from_inner(InnerSpan::new(0, 3)).shrink_to_hi(),
- explanation,
- "text",
- Applicability::MachineApplicable,
- );
- }
- } else if empty_block || is_ignore {
- lint.help(&format!("{}: ```text", explanation));
- }
-
- // FIXME(#67563): Provide more context for these errors by displaying the spans inline.
- for message in buffer.messages.iter() {
- lint.note(message);
- }
-
- lint
- },
- );
- }
-}
-
-impl<'a, 'tcx> DocVisitor for SyntaxChecker<'a, 'tcx> {
- fn visit_item(&mut self, item: &clean::Item) {
- if let Some(dox) = &item.attrs.collapsed_doc_value() {
- let sp = item.attr_span(self.cx.tcx);
- let extra = crate::html::markdown::ExtraInfo::new_did(
- self.cx.tcx,
- item.item_id.expect_def_id(),
- sp,
- );
- for code_block in markdown::rust_code_blocks(dox, &extra) {
- self.check_rust_syntax(item, dox, code_block);
- }
- }
-
- self.visit_item_recur(item)
- }
-}
-
-#[derive(Default)]
-struct Buffer {
- messages: Vec<String>,
- has_errors: bool,
-}
-
-struct BufferEmitter {
- buffer: Lrc<Lock<Buffer>>,
- fallback_bundle: LazyFallbackBundle,
-}
-
-impl Translate for BufferEmitter {
- fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
- None
- }
-
- fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
- &**self.fallback_bundle
- }
-}
-
-impl Emitter for BufferEmitter {
- fn emit_diagnostic(&mut self, diag: &Diagnostic) {
- let mut buffer = self.buffer.borrow_mut();
-
- let fluent_args = to_fluent_args(diag.args());
- let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args);
-
- buffer.messages.push(format!("error from rustc: {}", translated_main_message));
- if diag.is_error() {
- buffer.has_errors = true;
- }
- }
-
- fn source_map(&self) -> Option<&Lrc<SourceMap>> {
- None
- }
-}
};
pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate {
+ // We need to check if there are errors before running this pass because it would crash when
+ // we try to get auto and blanket implementations.
+ if cx.tcx.sess.diagnostic().has_errors_or_lint_errors().is_some() {
+ return krate;
+ }
+
let synth_impls = cx.sess().time("collect_synthetic_impls", || {
let mut synth = SyntheticImplCollector { cx, impls: Vec::new() };
synth.visit_crate(&krate);
+++ /dev/null
-//! Detects invalid HTML (like an unclosed `<span>`) in doc comments.
-use super::Pass;
-use crate::clean::*;
-use crate::core::DocContext;
-use crate::html::markdown::main_body_opts;
-use crate::visit::DocVisitor;
-
-use pulldown_cmark::{BrokenLink, Event, LinkType, Parser, Tag};
-
-use std::iter::Peekable;
-use std::ops::Range;
-use std::str::CharIndices;
-
-pub(crate) const CHECK_INVALID_HTML_TAGS: Pass = Pass {
- name: "check-invalid-html-tags",
- run: check_invalid_html_tags,
- description: "detects invalid HTML tags in doc comments",
-};
-
-struct InvalidHtmlTagsLinter<'a, 'tcx> {
- cx: &'a mut DocContext<'tcx>,
-}
-
-pub(crate) fn check_invalid_html_tags(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
- let mut coll = InvalidHtmlTagsLinter { cx };
- coll.visit_crate(&krate);
- krate
-}
-
-const ALLOWED_UNCLOSED: &[&str] = &[
- "area", "base", "br", "col", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
- "source", "track", "wbr",
-];
-
-fn drop_tag(
- tags: &mut Vec<(String, Range<usize>)>,
- tag_name: String,
- range: Range<usize>,
- f: &impl Fn(&str, &Range<usize>, bool),
-) {
- let tag_name_low = tag_name.to_lowercase();
- if let Some(pos) = tags.iter().rposition(|(t, _)| t.to_lowercase() == tag_name_low) {
- // If the tag is nested inside a "<script>" or a "<style>" tag, no warning should
- // be emitted.
- let should_not_warn = tags.iter().take(pos + 1).any(|(at, _)| {
- let at = at.to_lowercase();
- at == "script" || at == "style"
- });
- for (last_tag_name, last_tag_span) in tags.drain(pos + 1..) {
- if should_not_warn {
- continue;
- }
- let last_tag_name_low = last_tag_name.to_lowercase();
- if ALLOWED_UNCLOSED.contains(&last_tag_name_low.as_str()) {
- continue;
- }
- // `tags` is used as a queue, meaning that everything after `pos` is included inside it.
- // So `<h2><h3></h2>` will look like `["h2", "h3"]`. So when closing `h2`, we will still
- // have `h3`, meaning the tag wasn't closed as it should have.
- f(&format!("unclosed HTML tag `{}`", last_tag_name), &last_tag_span, true);
- }
- // Remove the `tag_name` that was originally closed
- tags.pop();
- } else {
- // It can happen for example in this case: `<h2></script></h2>` (the `h2` tag isn't required
- // but it helps for the visualization).
- f(&format!("unopened HTML tag `{}`", tag_name), &range, false);
- }
-}
-
-fn extract_path_backwards(text: &str, end_pos: usize) -> Option<usize> {
- use rustc_lexer::{is_id_continue, is_id_start};
- let mut current_pos = end_pos;
- loop {
- if current_pos >= 2 && text[..current_pos].ends_with("::") {
- current_pos -= 2;
- }
- let new_pos = text[..current_pos]
- .char_indices()
- .rev()
- .take_while(|(_, c)| is_id_start(*c) || is_id_continue(*c))
- .reduce(|_accum, item| item)
- .and_then(|(new_pos, c)| is_id_start(c).then_some(new_pos));
- if let Some(new_pos) = new_pos {
- if current_pos != new_pos {
- current_pos = new_pos;
- continue;
- }
- }
- break;
- }
- if current_pos == end_pos { None } else { Some(current_pos) }
-}
-
-fn extract_path_forward(text: &str, start_pos: usize) -> Option<usize> {
- use rustc_lexer::{is_id_continue, is_id_start};
- let mut current_pos = start_pos;
- loop {
- if current_pos < text.len() && text[current_pos..].starts_with("::") {
- current_pos += 2;
- } else {
- break;
- }
- let mut chars = text[current_pos..].chars();
- if let Some(c) = chars.next() {
- if is_id_start(c) {
- current_pos += c.len_utf8();
- } else {
- break;
- }
- }
- while let Some(c) = chars.next() {
- if is_id_continue(c) {
- current_pos += c.len_utf8();
- } else {
- break;
- }
- }
- }
- if current_pos == start_pos { None } else { Some(current_pos) }
-}
-
-fn is_valid_for_html_tag_name(c: char, is_empty: bool) -> bool {
- // https://spec.commonmark.org/0.30/#raw-html
- //
- // > A tag name consists of an ASCII letter followed by zero or more ASCII letters, digits, or
- // > hyphens (-).
- c.is_ascii_alphabetic() || !is_empty && (c == '-' || c.is_ascii_digit())
-}
-
-fn extract_html_tag(
- tags: &mut Vec<(String, Range<usize>)>,
- text: &str,
- range: &Range<usize>,
- start_pos: usize,
- iter: &mut Peekable<CharIndices<'_>>,
- f: &impl Fn(&str, &Range<usize>, bool),
-) {
- let mut tag_name = String::new();
- let mut is_closing = false;
- let mut prev_pos = start_pos;
-
- loop {
- let (pos, c) = match iter.peek() {
- Some((pos, c)) => (*pos, *c),
- // In case we reached the of the doc comment, we want to check that it's an
- // unclosed HTML tag. For example "/// <h3".
- None => (prev_pos, '\0'),
- };
- prev_pos = pos;
- // Checking if this is a closing tag (like `</a>` for `<a>`).
- if c == '/' && tag_name.is_empty() {
- is_closing = true;
- } else if is_valid_for_html_tag_name(c, tag_name.is_empty()) {
- tag_name.push(c);
- } else {
- if !tag_name.is_empty() {
- let mut r = Range { start: range.start + start_pos, end: range.start + pos };
- if c == '>' {
- // In case we have a tag without attribute, we can consider the span to
- // refer to it fully.
- r.end += 1;
- }
- if is_closing {
- // In case we have "</div >" or even "</div >".
- if c != '>' {
- if !c.is_whitespace() {
- // It seems like it's not a valid HTML tag.
- break;
- }
- let mut found = false;
- for (new_pos, c) in text[pos..].char_indices() {
- if !c.is_whitespace() {
- if c == '>' {
- r.end = range.start + new_pos + 1;
- found = true;
- }
- break;
- }
- }
- if !found {
- break;
- }
- }
- drop_tag(tags, tag_name, r, f);
- } else {
- let mut is_self_closing = false;
- let mut quote_pos = None;
- if c != '>' {
- let mut quote = None;
- let mut after_eq = false;
- for (i, c) in text[pos..].char_indices() {
- if !c.is_whitespace() {
- if let Some(q) = quote {
- if c == q {
- quote = None;
- quote_pos = None;
- after_eq = false;
- }
- } else if c == '>' {
- break;
- } else if c == '/' && !after_eq {
- is_self_closing = true;
- } else {
- if is_self_closing {
- is_self_closing = false;
- }
- if (c == '"' || c == '\'') && after_eq {
- quote = Some(c);
- quote_pos = Some(pos + i);
- } else if c == '=' {
- after_eq = true;
- }
- }
- } else if quote.is_none() {
- after_eq = false;
- }
- }
- }
- if let Some(quote_pos) = quote_pos {
- let qr = Range { start: quote_pos, end: quote_pos };
- f(
- &format!("unclosed quoted HTML attribute on tag `{}`", tag_name),
- &qr,
- false,
- );
- }
- if is_self_closing {
- // https://html.spec.whatwg.org/#parse-error-non-void-html-element-start-tag-with-trailing-solidus
- let valid = ALLOWED_UNCLOSED.contains(&&tag_name[..])
- || tags.iter().take(pos + 1).any(|(at, _)| {
- let at = at.to_lowercase();
- at == "svg" || at == "math"
- });
- if !valid {
- f(&format!("invalid self-closing HTML tag `{}`", tag_name), &r, false);
- }
- } else {
- tags.push((tag_name, r));
- }
- }
- }
- break;
- }
- iter.next();
- }
-}
-
-fn extract_tags(
- tags: &mut Vec<(String, Range<usize>)>,
- text: &str,
- range: Range<usize>,
- is_in_comment: &mut Option<Range<usize>>,
- f: &impl Fn(&str, &Range<usize>, bool),
-) {
- let mut iter = text.char_indices().peekable();
-
- while let Some((start_pos, c)) = iter.next() {
- if is_in_comment.is_some() {
- if text[start_pos..].starts_with("-->") {
- *is_in_comment = None;
- }
- } else if c == '<' {
- if text[start_pos..].starts_with("<!--") {
- // We skip the "!--" part. (Once `advance_by` is stable, might be nice to use it!)
- iter.next();
- iter.next();
- iter.next();
- *is_in_comment = Some(Range {
- start: range.start + start_pos,
- end: range.start + start_pos + 3,
- });
- } else {
- extract_html_tag(tags, text, &range, start_pos, &mut iter, f);
- }
- }
- }
-}
-
-impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> {
- fn visit_item(&mut self, item: &Item) {
- let tcx = self.cx.tcx;
- let Some(hir_id) = DocContext::as_local_hir_id(tcx, item.item_id)
- // If non-local, no need to check anything.
- else { return };
- let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
- if !dox.is_empty() {
- let report_diag = |msg: &str, range: &Range<usize>, is_open_tag: bool| {
- let sp = match super::source_span_for_markdown_range(tcx, &dox, range, &item.attrs)
- {
- Some(sp) => sp,
- None => item.attr_span(tcx),
- };
- tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, msg, |lint| {
- use rustc_lint_defs::Applicability;
- // If a tag looks like `<this>`, it might actually be a generic.
- // We don't try to detect stuff `<like, this>` because that's not valid HTML,
- // and we don't try to detect stuff `<like this>` because that's not valid Rust.
- let mut generics_end = range.end;
- if let Some(Some(mut generics_start)) = (is_open_tag
- && dox[..generics_end].ends_with('>'))
- .then(|| extract_path_backwards(&dox, range.start))
- {
- while generics_start != 0
- && generics_end < dox.len()
- && dox.as_bytes()[generics_start - 1] == b'<'
- && dox.as_bytes()[generics_end] == b'>'
- {
- generics_end += 1;
- generics_start -= 1;
- if let Some(new_start) = extract_path_backwards(&dox, generics_start) {
- generics_start = new_start;
- }
- if let Some(new_end) = extract_path_forward(&dox, generics_end) {
- generics_end = new_end;
- }
- }
- if let Some(new_end) = extract_path_forward(&dox, generics_end) {
- generics_end = new_end;
- }
- let generics_sp = match super::source_span_for_markdown_range(
- tcx,
- &dox,
- &(generics_start..generics_end),
- &item.attrs,
- ) {
- Some(sp) => sp,
- None => item.attr_span(tcx),
- };
- // Sometimes, we only extract part of a path. For example, consider this:
- //
- // <[u32] as IntoIter<u32>>::Item
- // ^^^^^ unclosed HTML tag `u32`
- //
- // We don't have any code for parsing fully-qualified trait paths.
- // In theory, we could add it, but doing it correctly would require
- // parsing the entire path grammar, which is problematic because of
- // overlap between the path grammar and Markdown.
- //
- // The example above shows that ambiguity. Is `[u32]` intended to be an
- // intra-doc link to the u32 primitive, or is it intended to be a slice?
- //
- // If the below conditional were removed, we would suggest this, which is
- // not what the user probably wants.
- //
- // <[u32] as `IntoIter<u32>`>::Item
- //
- // We know that the user actually wants to wrap the whole thing in a code
- // block, but the only reason we know that is because `u32` does not, in
- // fact, implement IntoIter. If the example looks like this:
- //
- // <[Vec<i32>] as IntoIter<i32>::Item
- //
- // The ideal fix would be significantly different.
- if (generics_start > 0 && dox.as_bytes()[generics_start - 1] == b'<')
- || (generics_end < dox.len() && dox.as_bytes()[generics_end] == b'>')
- {
- return lint;
- }
- // multipart form is chosen here because ``Vec<i32>`` would be confusing.
- lint.multipart_suggestion(
- "try marking as source code",
- vec![
- (generics_sp.shrink_to_lo(), String::from("`")),
- (generics_sp.shrink_to_hi(), String::from("`")),
- ],
- Applicability::MaybeIncorrect,
- );
- }
-
- lint
- });
- };
-
- let mut tags = Vec::new();
- let mut is_in_comment = None;
- let mut in_code_block = false;
-
- let link_names = item.link_names(&self.cx.cache);
-
- let mut replacer = |broken_link: BrokenLink<'_>| {
- if let Some(link) =
- link_names.iter().find(|link| *link.original_text == *broken_link.reference)
- {
- Some((link.href.as_str().into(), link.new_text.as_str().into()))
- } else if matches!(
- &broken_link.link_type,
- LinkType::Reference | LinkType::ReferenceUnknown
- ) {
- // If the link is shaped [like][this], suppress any broken HTML in the [this] part.
- // The `broken_intra_doc_links` will report typos in there anyway.
- Some((
- broken_link.reference.to_string().into(),
- broken_link.reference.to_string().into(),
- ))
- } else {
- None
- }
- };
-
- let p =
- Parser::new_with_broken_link_callback(&dox, main_body_opts(), Some(&mut replacer))
- .into_offset_iter();
-
- for (event, range) in p {
- match event {
- Event::Start(Tag::CodeBlock(_)) => in_code_block = true,
- Event::Html(text) if !in_code_block => {
- extract_tags(&mut tags, &text, range, &mut is_in_comment, &report_diag)
- }
- Event::End(Tag::CodeBlock(_)) => in_code_block = false,
- _ => {}
- }
- }
-
- for (tag, range) in tags.iter().filter(|(t, _)| {
- let t = t.to_lowercase();
- !ALLOWED_UNCLOSED.contains(&t.as_str())
- }) {
- report_diag(&format!("unclosed HTML tag `{}`", tag), range, true);
- }
-
- if let Some(range) = is_in_comment {
- report_diag("Unclosed HTML comment", &range, false);
- }
- }
-
- self.visit_item_recur(item)
- }
-}
--- /dev/null
+//! Runs several rustdoc lints, consolidating them into a single pass for
+//! efficiency and simplicity.
+
+mod bare_urls;
+mod check_code_block_syntax;
+mod html_tags;
+
+use super::Pass;
+use crate::clean::*;
+use crate::core::DocContext;
+use crate::visit::DocVisitor;
+
+pub(crate) const RUN_LINTS: Pass =
+ Pass { name: "run-lints", run: run_lints, description: "runs some of rustdoc's lints" };
+
+struct Linter<'a, 'tcx> {
+ cx: &'a mut DocContext<'tcx>,
+}
+
+pub(crate) fn run_lints(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
+ Linter { cx }.visit_crate(&krate);
+ krate
+}
+
+impl<'a, 'tcx> DocVisitor for Linter<'a, 'tcx> {
+ fn visit_item(&mut self, item: &Item) {
+ bare_urls::visit_item(self.cx, item);
+ check_code_block_syntax::visit_item(self.cx, item);
+ html_tags::visit_item(self.cx, item);
+
+ self.visit_item_recur(item)
+ }
+}
--- /dev/null
+//! Detects links that are not linkified, e.g., in Markdown such as `Go to https://example.com/.`
+//! Suggests wrapping the link with angle brackets: `Go to <https://example.com/>.` to linkify it.
+
+use crate::clean::*;
+use crate::core::DocContext;
+use crate::html::markdown::main_body_opts;
+use crate::passes::source_span_for_markdown_range;
+use core::ops::Range;
+use pulldown_cmark::{Event, Parser, Tag};
+use regex::Regex;
+use rustc_errors::Applicability;
+use std::mem;
+use std::sync::LazyLock;
+
+pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item) {
+ let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id)
+ else {
+ // If non-local, no need to check anything.
+ return;
+ };
+ let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
+ if !dox.is_empty() {
+ let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| {
+ let sp = source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
+ .unwrap_or_else(|| item.attr_span(cx.tcx));
+ cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, msg, |lint| {
+ lint.note("bare URLs are not automatically turned into clickable links")
+ .span_suggestion(
+ sp,
+ "use an automatic link instead",
+ format!("<{}>", url),
+ Applicability::MachineApplicable,
+ )
+ });
+ };
+
+ let mut p = Parser::new_ext(&dox, main_body_opts()).into_offset_iter();
+
+ while let Some((event, range)) = p.next() {
+ match event {
+ Event::Text(s) => find_raw_urls(cx, &s, range, &report_diag),
+ // We don't want to check the text inside code blocks or links.
+ Event::Start(tag @ (Tag::CodeBlock(_) | Tag::Link(..))) => {
+ while let Some((event, _)) = p.next() {
+ match event {
+ Event::End(end)
+ if mem::discriminant(&end) == mem::discriminant(&tag) =>
+ {
+ break;
+ }
+ _ => {}
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+}
+
+static URL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
+ Regex::new(concat!(
+ r"https?://", // url scheme
+ r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains
+ r"[a-zA-Z]{2,63}", // root domain
+ r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments
+ ))
+ .expect("failed to build regex")
+});
+
+fn find_raw_urls(
+ cx: &DocContext<'_>,
+ text: &str,
+ range: Range<usize>,
+ f: &impl Fn(&DocContext<'_>, &str, &str, Range<usize>),
+) {
+ trace!("looking for raw urls in {}", text);
+ // For now, we only check "full" URLs (meaning, starting with "http://" or "https://").
+ for match_ in URL_REGEX.find_iter(text) {
+ let url = match_.as_str();
+ let url_range = match_.range();
+ f(
+ cx,
+ "this URL is not a hyperlink",
+ url,
+ Range { start: range.start + url_range.start, end: range.start + url_range.end },
+ );
+ }
+}
--- /dev/null
+//! Validates syntax inside Rust code blocks (\`\`\`rust).
+use rustc_data_structures::sync::{Lock, Lrc};
+use rustc_errors::{
+ emitter::Emitter,
+ translation::{to_fluent_args, Translate},
+ Applicability, Diagnostic, Handler, LazyFallbackBundle,
+};
+use rustc_parse::parse_stream_from_source_str;
+use rustc_session::parse::ParseSess;
+use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
+use rustc_span::source_map::{FilePathMapping, SourceMap};
+use rustc_span::{FileName, InnerSpan, DUMMY_SP};
+
+use crate::clean;
+use crate::core::DocContext;
+use crate::html::markdown::{self, RustCodeBlock};
+use crate::passes::source_span_for_markdown_range;
+
+pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
+ if let Some(dox) = &item.attrs.collapsed_doc_value() {
+ let sp = item.attr_span(cx.tcx);
+ let extra =
+ crate::html::markdown::ExtraInfo::new_did(cx.tcx, item.item_id.expect_def_id(), sp);
+ for code_block in markdown::rust_code_blocks(dox, &extra) {
+ check_rust_syntax(cx, item, dox, code_block);
+ }
+ }
+}
+
+fn check_rust_syntax(
+ cx: &DocContext<'_>,
+ item: &clean::Item,
+ dox: &str,
+ code_block: RustCodeBlock,
+) {
+ let buffer = Lrc::new(Lock::new(Buffer::default()));
+ let fallback_bundle =
+ rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle };
+
+ let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+ let handler = Handler::with_emitter(false, None, Box::new(emitter));
+ let source = dox[code_block.code].to_owned();
+ let sess = ParseSess::with_span_handler(handler, sm);
+
+ let edition = code_block.lang_string.edition.unwrap_or_else(|| cx.tcx.sess.edition());
+ let expn_data =
+ ExpnData::default(ExpnKind::AstPass(AstPass::TestHarness), DUMMY_SP, edition, None, None);
+ let expn_id = cx.tcx.with_stable_hashing_context(|hcx| LocalExpnId::fresh(expn_data, hcx));
+ let span = DUMMY_SP.fresh_expansion(expn_id);
+
+ let is_empty = rustc_driver::catch_fatal_errors(|| {
+ parse_stream_from_source_str(
+ FileName::Custom(String::from("doctest")),
+ source,
+ &sess,
+ Some(span),
+ )
+ .is_empty()
+ })
+ .unwrap_or(false);
+ let buffer = buffer.borrow();
+
+ if !buffer.has_errors && !is_empty {
+ // No errors in a non-empty program.
+ return;
+ }
+
+ let Some(local_id) = item.item_id.as_def_id().and_then(|x| x.as_local())
+ else {
+ // We don't need to check the syntax for other crates so returning
+ // without doing anything should not be a problem.
+ return;
+ };
+
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
+ let empty_block = code_block.lang_string == Default::default() && code_block.is_fenced;
+ let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None;
+
+ // The span and whether it is precise or not.
+ let (sp, precise_span) =
+ match source_span_for_markdown_range(cx.tcx, dox, &code_block.range, &item.attrs) {
+ Some(sp) => (sp, true),
+ None => (item.attr_span(cx.tcx), false),
+ };
+
+ let msg = if buffer.has_errors {
+ "could not parse code block as Rust code"
+ } else {
+ "Rust code block is empty"
+ };
+
+ // Finally build and emit the completed diagnostic.
+ // All points of divergence have been handled earlier so this can be
+ // done the same way whether the span is precise or not.
+ cx.tcx.struct_span_lint_hir(crate::lint::INVALID_RUST_CODEBLOCKS, hir_id, sp, msg, |lint| {
+ let explanation = if is_ignore {
+ "`ignore` code blocks require valid Rust code for syntax highlighting; \
+ mark blocks that do not contain Rust code as text"
+ } else {
+ "mark blocks that do not contain Rust code as text"
+ };
+
+ if precise_span {
+ if is_ignore {
+ // giving an accurate suggestion is hard because `ignore` might not have come first in the list.
+ // just give a `help` instead.
+ lint.span_help(
+ sp.from_inner(InnerSpan::new(0, 3)),
+ &format!("{}: ```text", explanation),
+ );
+ } else if empty_block {
+ lint.span_suggestion(
+ sp.from_inner(InnerSpan::new(0, 3)).shrink_to_hi(),
+ explanation,
+ "text",
+ Applicability::MachineApplicable,
+ );
+ }
+ } else if empty_block || is_ignore {
+ lint.help(&format!("{}: ```text", explanation));
+ }
+
+ // FIXME(#67563): Provide more context for these errors by displaying the spans inline.
+ for message in buffer.messages.iter() {
+ lint.note(message);
+ }
+
+ lint
+ });
+}
+
+#[derive(Default)]
+struct Buffer {
+ messages: Vec<String>,
+ has_errors: bool,
+}
+
+struct BufferEmitter {
+ buffer: Lrc<Lock<Buffer>>,
+ fallback_bundle: LazyFallbackBundle,
+}
+
+impl Translate for BufferEmitter {
+ fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
+ None
+ }
+
+ fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
+ &**self.fallback_bundle
+ }
+}
+
+impl Emitter for BufferEmitter {
+ fn emit_diagnostic(&mut self, diag: &Diagnostic) {
+ let mut buffer = self.buffer.borrow_mut();
+
+ let fluent_args = to_fluent_args(diag.args());
+ let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args);
+
+ buffer.messages.push(format!("error from rustc: {}", translated_main_message));
+ if diag.is_error() {
+ buffer.has_errors = true;
+ }
+ }
+
+ fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+ None
+ }
+}
--- /dev/null
+//! Detects invalid HTML (like an unclosed `<span>`) in doc comments.
+use crate::clean::*;
+use crate::core::DocContext;
+use crate::html::markdown::main_body_opts;
+use crate::passes::source_span_for_markdown_range;
+
+use pulldown_cmark::{BrokenLink, Event, LinkType, Parser, Tag};
+
+use std::iter::Peekable;
+use std::ops::Range;
+use std::str::CharIndices;
+
+pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
+ let tcx = cx.tcx;
+ let Some(hir_id) = DocContext::as_local_hir_id(tcx, item.item_id)
+ // If non-local, no need to check anything.
+ else { return };
+ let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
+ if !dox.is_empty() {
+ let report_diag = |msg: &str, range: &Range<usize>, is_open_tag: bool| {
+ let sp = match source_span_for_markdown_range(tcx, &dox, range, &item.attrs) {
+ Some(sp) => sp,
+ None => item.attr_span(tcx),
+ };
+ tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, msg, |lint| {
+ use rustc_lint_defs::Applicability;
+ // If a tag looks like `<this>`, it might actually be a generic.
+ // We don't try to detect stuff `<like, this>` because that's not valid HTML,
+ // and we don't try to detect stuff `<like this>` because that's not valid Rust.
+ let mut generics_end = range.end;
+ if let Some(Some(mut generics_start)) = (is_open_tag
+ && dox[..generics_end].ends_with('>'))
+ .then(|| extract_path_backwards(&dox, range.start))
+ {
+ while generics_start != 0
+ && generics_end < dox.len()
+ && dox.as_bytes()[generics_start - 1] == b'<'
+ && dox.as_bytes()[generics_end] == b'>'
+ {
+ generics_end += 1;
+ generics_start -= 1;
+ if let Some(new_start) = extract_path_backwards(&dox, generics_start) {
+ generics_start = new_start;
+ }
+ if let Some(new_end) = extract_path_forward(&dox, generics_end) {
+ generics_end = new_end;
+ }
+ }
+ if let Some(new_end) = extract_path_forward(&dox, generics_end) {
+ generics_end = new_end;
+ }
+ let generics_sp = match source_span_for_markdown_range(
+ tcx,
+ &dox,
+ &(generics_start..generics_end),
+ &item.attrs,
+ ) {
+ Some(sp) => sp,
+ None => item.attr_span(tcx),
+ };
+ // Sometimes, we only extract part of a path. For example, consider this:
+ //
+ // <[u32] as IntoIter<u32>>::Item
+ // ^^^^^ unclosed HTML tag `u32`
+ //
+ // We don't have any code for parsing fully-qualified trait paths.
+ // In theory, we could add it, but doing it correctly would require
+ // parsing the entire path grammar, which is problematic because of
+ // overlap between the path grammar and Markdown.
+ //
+ // The example above shows that ambiguity. Is `[u32]` intended to be an
+ // intra-doc link to the u32 primitive, or is it intended to be a slice?
+ //
+ // If the below conditional were removed, we would suggest this, which is
+ // not what the user probably wants.
+ //
+ // <[u32] as `IntoIter<u32>`>::Item
+ //
+ // We know that the user actually wants to wrap the whole thing in a code
+ // block, but the only reason we know that is because `u32` does not, in
+ // fact, implement IntoIter. If the example looks like this:
+ //
+ // <[Vec<i32>] as IntoIter<i32>::Item
+ //
+ // The ideal fix would be significantly different.
+ if (generics_start > 0 && dox.as_bytes()[generics_start - 1] == b'<')
+ || (generics_end < dox.len() && dox.as_bytes()[generics_end] == b'>')
+ {
+ return lint;
+ }
+ // multipart form is chosen here because ``Vec<i32>`` would be confusing.
+ lint.multipart_suggestion(
+ "try marking as source code",
+ vec![
+ (generics_sp.shrink_to_lo(), String::from("`")),
+ (generics_sp.shrink_to_hi(), String::from("`")),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ lint
+ });
+ };
+
+ let mut tags = Vec::new();
+ let mut is_in_comment = None;
+ let mut in_code_block = false;
+
+ let link_names = item.link_names(&cx.cache);
+
+ let mut replacer = |broken_link: BrokenLink<'_>| {
+ if let Some(link) =
+ link_names.iter().find(|link| *link.original_text == *broken_link.reference)
+ {
+ Some((link.href.as_str().into(), link.new_text.as_str().into()))
+ } else if matches!(
+ &broken_link.link_type,
+ LinkType::Reference | LinkType::ReferenceUnknown
+ ) {
+ // If the link is shaped [like][this], suppress any broken HTML in the [this] part.
+ // The `broken_intra_doc_links` will report typos in there anyway.
+ Some((
+ broken_link.reference.to_string().into(),
+ broken_link.reference.to_string().into(),
+ ))
+ } else {
+ None
+ }
+ };
+
+ let p = Parser::new_with_broken_link_callback(&dox, main_body_opts(), Some(&mut replacer))
+ .into_offset_iter();
+
+ for (event, range) in p {
+ match event {
+ Event::Start(Tag::CodeBlock(_)) => in_code_block = true,
+ Event::Html(text) if !in_code_block => {
+ extract_tags(&mut tags, &text, range, &mut is_in_comment, &report_diag)
+ }
+ Event::End(Tag::CodeBlock(_)) => in_code_block = false,
+ _ => {}
+ }
+ }
+
+ for (tag, range) in tags.iter().filter(|(t, _)| {
+ let t = t.to_lowercase();
+ !ALLOWED_UNCLOSED.contains(&t.as_str())
+ }) {
+ report_diag(&format!("unclosed HTML tag `{}`", tag), range, true);
+ }
+
+ if let Some(range) = is_in_comment {
+ report_diag("Unclosed HTML comment", &range, false);
+ }
+ }
+}
+
+const ALLOWED_UNCLOSED: &[&str] = &[
+ "area", "base", "br", "col", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
+ "source", "track", "wbr",
+];
+
+fn drop_tag(
+ tags: &mut Vec<(String, Range<usize>)>,
+ tag_name: String,
+ range: Range<usize>,
+ f: &impl Fn(&str, &Range<usize>, bool),
+) {
+ let tag_name_low = tag_name.to_lowercase();
+ if let Some(pos) = tags.iter().rposition(|(t, _)| t.to_lowercase() == tag_name_low) {
+ // If the tag is nested inside a "<script>" or a "<style>" tag, no warning should
+ // be emitted.
+ let should_not_warn = tags.iter().take(pos + 1).any(|(at, _)| {
+ let at = at.to_lowercase();
+ at == "script" || at == "style"
+ });
+ for (last_tag_name, last_tag_span) in tags.drain(pos + 1..) {
+ if should_not_warn {
+ continue;
+ }
+ let last_tag_name_low = last_tag_name.to_lowercase();
+ if ALLOWED_UNCLOSED.contains(&last_tag_name_low.as_str()) {
+ continue;
+ }
+ // `tags` is used as a queue, meaning that everything after `pos` is included inside it.
+ // So `<h2><h3></h2>` will look like `["h2", "h3"]`. So when closing `h2`, we will still
+ // have `h3`, meaning the tag wasn't closed as it should have.
+ f(&format!("unclosed HTML tag `{}`", last_tag_name), &last_tag_span, true);
+ }
+ // Remove the `tag_name` that was originally closed
+ tags.pop();
+ } else {
+ // It can happen for example in this case: `<h2></script></h2>` (the `h2` tag isn't required
+ // but it helps for the visualization).
+ f(&format!("unopened HTML tag `{}`", tag_name), &range, false);
+ }
+}
+
+fn extract_path_backwards(text: &str, end_pos: usize) -> Option<usize> {
+ use rustc_lexer::{is_id_continue, is_id_start};
+ let mut current_pos = end_pos;
+ loop {
+ if current_pos >= 2 && text[..current_pos].ends_with("::") {
+ current_pos -= 2;
+ }
+ let new_pos = text[..current_pos]
+ .char_indices()
+ .rev()
+ .take_while(|(_, c)| is_id_start(*c) || is_id_continue(*c))
+ .reduce(|_accum, item| item)
+ .and_then(|(new_pos, c)| is_id_start(c).then_some(new_pos));
+ if let Some(new_pos) = new_pos {
+ if current_pos != new_pos {
+ current_pos = new_pos;
+ continue;
+ }
+ }
+ break;
+ }
+ if current_pos == end_pos { None } else { Some(current_pos) }
+}
+
+fn extract_path_forward(text: &str, start_pos: usize) -> Option<usize> {
+ use rustc_lexer::{is_id_continue, is_id_start};
+ let mut current_pos = start_pos;
+ loop {
+ if current_pos < text.len() && text[current_pos..].starts_with("::") {
+ current_pos += 2;
+ } else {
+ break;
+ }
+ let mut chars = text[current_pos..].chars();
+ if let Some(c) = chars.next() {
+ if is_id_start(c) {
+ current_pos += c.len_utf8();
+ } else {
+ break;
+ }
+ }
+ while let Some(c) = chars.next() {
+ if is_id_continue(c) {
+ current_pos += c.len_utf8();
+ } else {
+ break;
+ }
+ }
+ }
+ if current_pos == start_pos { None } else { Some(current_pos) }
+}
+
+fn is_valid_for_html_tag_name(c: char, is_empty: bool) -> bool {
+ // https://spec.commonmark.org/0.30/#raw-html
+ //
+ // > A tag name consists of an ASCII letter followed by zero or more ASCII letters, digits, or
+ // > hyphens (-).
+ c.is_ascii_alphabetic() || !is_empty && (c == '-' || c.is_ascii_digit())
+}
+
+fn extract_html_tag(
+ tags: &mut Vec<(String, Range<usize>)>,
+ text: &str,
+ range: &Range<usize>,
+ start_pos: usize,
+ iter: &mut Peekable<CharIndices<'_>>,
+ f: &impl Fn(&str, &Range<usize>, bool),
+) {
+ let mut tag_name = String::new();
+ let mut is_closing = false;
+ let mut prev_pos = start_pos;
+
+ loop {
+ let (pos, c) = match iter.peek() {
+ Some((pos, c)) => (*pos, *c),
+ // In case we reached the of the doc comment, we want to check that it's an
+ // unclosed HTML tag. For example "/// <h3".
+ None => (prev_pos, '\0'),
+ };
+ prev_pos = pos;
+ // Checking if this is a closing tag (like `</a>` for `<a>`).
+ if c == '/' && tag_name.is_empty() {
+ is_closing = true;
+ } else if is_valid_for_html_tag_name(c, tag_name.is_empty()) {
+ tag_name.push(c);
+ } else {
+ if !tag_name.is_empty() {
+ let mut r = Range { start: range.start + start_pos, end: range.start + pos };
+ if c == '>' {
+ // In case we have a tag without attribute, we can consider the span to
+ // refer to it fully.
+ r.end += 1;
+ }
+ if is_closing {
+ // In case we have "</div >" or even "</div >".
+ if c != '>' {
+ if !c.is_whitespace() {
+ // It seems like it's not a valid HTML tag.
+ break;
+ }
+ let mut found = false;
+ for (new_pos, c) in text[pos..].char_indices() {
+ if !c.is_whitespace() {
+ if c == '>' {
+ r.end = range.start + new_pos + 1;
+ found = true;
+ }
+ break;
+ }
+ }
+ if !found {
+ break;
+ }
+ }
+ drop_tag(tags, tag_name, r, f);
+ } else {
+ let mut is_self_closing = false;
+ let mut quote_pos = None;
+ if c != '>' {
+ let mut quote = None;
+ let mut after_eq = false;
+ for (i, c) in text[pos..].char_indices() {
+ if !c.is_whitespace() {
+ if let Some(q) = quote {
+ if c == q {
+ quote = None;
+ quote_pos = None;
+ after_eq = false;
+ }
+ } else if c == '>' {
+ break;
+ } else if c == '/' && !after_eq {
+ is_self_closing = true;
+ } else {
+ if is_self_closing {
+ is_self_closing = false;
+ }
+ if (c == '"' || c == '\'') && after_eq {
+ quote = Some(c);
+ quote_pos = Some(pos + i);
+ } else if c == '=' {
+ after_eq = true;
+ }
+ }
+ } else if quote.is_none() {
+ after_eq = false;
+ }
+ }
+ }
+ if let Some(quote_pos) = quote_pos {
+ let qr = Range { start: quote_pos, end: quote_pos };
+ f(
+ &format!("unclosed quoted HTML attribute on tag `{}`", tag_name),
+ &qr,
+ false,
+ );
+ }
+ if is_self_closing {
+ // https://html.spec.whatwg.org/#parse-error-non-void-html-element-start-tag-with-trailing-solidus
+ let valid = ALLOWED_UNCLOSED.contains(&&tag_name[..])
+ || tags.iter().take(pos + 1).any(|(at, _)| {
+ let at = at.to_lowercase();
+ at == "svg" || at == "math"
+ });
+ if !valid {
+ f(&format!("invalid self-closing HTML tag `{}`", tag_name), &r, false);
+ }
+ } else {
+ tags.push((tag_name, r));
+ }
+ }
+ }
+ break;
+ }
+ iter.next();
+ }
+}
+
+fn extract_tags(
+ tags: &mut Vec<(String, Range<usize>)>,
+ text: &str,
+ range: Range<usize>,
+ is_in_comment: &mut Option<Range<usize>>,
+ f: &impl Fn(&str, &Range<usize>, bool),
+) {
+ let mut iter = text.char_indices().peekable();
+
+ while let Some((start_pos, c)) = iter.next() {
+ if is_in_comment.is_some() {
+ if text[start_pos..].starts_with("-->") {
+ *is_in_comment = None;
+ }
+ } else if c == '<' {
+ if text[start_pos..].starts_with("<!--") {
+ // We skip the "!--" part. (Once `advance_by` is stable, might be nice to use it!)
+ iter.next();
+ iter.next();
+ iter.next();
+ *is_in_comment = Some(Range {
+ start: range.start + start_pos,
+ end: range.start + start_pos + 3,
+ });
+ } else {
+ extract_html_tag(tags, text, &range, start_pos, &mut iter, f);
+ }
+ }
+ }
+}
mod stripper;
pub(crate) use stripper::*;
-mod bare_urls;
-pub(crate) use self::bare_urls::CHECK_BARE_URLS;
-
mod strip_hidden;
pub(crate) use self::strip_hidden::STRIP_HIDDEN;
mod collect_trait_impls;
pub(crate) use self::collect_trait_impls::COLLECT_TRAIT_IMPLS;
-mod check_code_block_syntax;
-pub(crate) use self::check_code_block_syntax::CHECK_CODE_BLOCK_SYNTAX;
-
mod calculate_doc_coverage;
pub(crate) use self::calculate_doc_coverage::CALCULATE_DOC_COVERAGE;
-mod html_tags;
-pub(crate) use self::html_tags::CHECK_INVALID_HTML_TAGS;
+mod lint;
+pub(crate) use self::lint::RUN_LINTS;
/// A single pass over the cleaned documentation.
///
STRIP_PRIV_IMPORTS,
PROPAGATE_DOC_CFG,
COLLECT_INTRA_DOC_LINKS,
- CHECK_CODE_BLOCK_SYNTAX,
COLLECT_TRAIT_IMPLS,
CALCULATE_DOC_COVERAGE,
- CHECK_INVALID_HTML_TAGS,
- CHECK_BARE_URLS,
+ RUN_LINTS,
];
/// The list of passes run by default.
ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate),
ConditionalPass::always(COLLECT_INTRA_DOC_LINKS),
- ConditionalPass::always(CHECK_CODE_BLOCK_SYNTAX),
- ConditionalPass::always(CHECK_INVALID_HTML_TAGS),
ConditionalPass::always(PROPAGATE_DOC_CFG),
- ConditionalPass::always(CHECK_BARE_URLS),
+ ConditionalPass::always(RUN_LINTS),
];
/// The list of default passes run when `--doc-coverage` is passed to rustdoc.
pub(crate) url: String,
pub(crate) display_name: String,
pub(crate) edition: Edition,
+ pub(crate) is_bin: bool,
}
pub(crate) type FnCallLocations = FxHashMap<PathBuf, CallData>;
cx: Context<'tcx>,
target_crates: Vec<CrateNum>,
calls: &'a mut AllCallLocations,
+ bin_crate: bool,
}
impl<'a, 'tcx> Visitor<'tcx> for FindCalls<'a, 'tcx>
let mk_call_data = || {
let display_name = file_path.display().to_string();
let edition = call_span.edition();
- CallData { locations: Vec::new(), url, display_name, edition }
+ let is_bin = self.bin_crate;
+
+ CallData { locations: Vec::new(), url, display_name, edition, is_bin }
};
let fn_key = tcx.def_path_hash(*def_id);
cache: formats::cache::Cache,
tcx: TyCtxt<'_>,
options: ScrapeExamplesOptions,
+ bin_crate: bool,
) -> interface::Result<()> {
let inner = move || -> Result<(), String> {
// Generates source files for examples
// Run call-finder on all items
let mut calls = FxHashMap::default();
- let mut finder = FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates };
+ let mut finder =
+ FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates, bin_crate };
tcx.hir().visit_all_item_likes_in_crate(&mut finder);
// The visitor might have found a type error, which we need to
hir::ItemKind::GlobalAsm(..) => {}
hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
hir::ItemKind::Use(path, kind) => {
- let is_glob = kind == hir::UseKind::Glob;
-
- // Struct and variant constructors and proc macro stubs always show up alongside
- // their definitions, we've already processed them so just discard these.
- if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res {
- return;
- }
-
- let attrs = self.cx.tcx.hir().attrs(item.hir_id());
+ for &res in &path.res {
+ // Struct and variant constructors and proc macro stubs always show up alongside
+ // their definitions, we've already processed them so just discard these.
+ if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
+ continue;
+ }
- // If there was a private module in the current path then don't bother inlining
- // anything as it will probably be stripped anyway.
- if is_pub && self.inside_public_path {
- let please_inline = attrs.iter().any(|item| match item.meta_item_list() {
- Some(ref list) if item.has_name(sym::doc) => {
- list.iter().any(|i| i.has_name(sym::inline))
+ let attrs = self.cx.tcx.hir().attrs(item.hir_id());
+
+ // If there was a private module in the current path then don't bother inlining
+ // anything as it will probably be stripped anyway.
+ if is_pub && self.inside_public_path {
+ let please_inline = attrs.iter().any(|item| match item.meta_item_list() {
+ Some(ref list) if item.has_name(sym::doc) => {
+ list.iter().any(|i| i.has_name(sym::inline))
+ }
+ _ => false,
+ });
+ let is_glob = kind == hir::UseKind::Glob;
+ let ident = if is_glob { None } else { Some(name) };
+ if self.maybe_inline_local(
+ item.hir_id(),
+ res,
+ ident,
+ is_glob,
+ om,
+ please_inline,
+ ) {
+ continue;
}
- _ => false,
- });
- let ident = if is_glob { None } else { Some(name) };
- if self.maybe_inline_local(
- item.hir_id(),
- path.res,
- ident,
- is_glob,
- om,
- please_inline,
- ) {
- return;
}
- }
- om.items.push((item, renamed, parent_id))
+ om.items.push((item, renamed, parent_id))
+ }
}
hir::ItemKind::Macro(ref macro_def, _) => {
// `#[macro_export] macro_rules!` items are handled separately in `visit()`,
-Subproject commit a1232c451fc27173f8718e05d174b2503ca0b607
+Subproject commit 3dfd4d93fa013e1c0578d3ceac5c8f4ebba4b6ec
--- /dev/null
+// assembly-output: emit-asm
+// compile-flags: --target aarch64-unknown-linux-gnu
+// needs-llvm-components: aarch64
+
+#![feature(no_core, lang_items, rustc_attrs)]
+#![crate_type = "rlib"]
+#![no_core]
+
+#[rustc_builtin_macro]
+macro_rules! asm {
+ () => {};
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+// CHECK-LABEL: ttbr0_el2:
+#[no_mangle]
+pub fn ttbr0_el2() {
+ // CHECK: //APP
+ // CHECK-NEXT: msr TTBR0_EL2, x0
+ // CHECK-NEXT: //NO_APP
+ unsafe {
+ asm!("msr ttbr0_el2, x0");
+ }
+}
+
+// CHECK-LABEL: vttbr_el2:
+#[no_mangle]
+pub fn vttbr_el2() {
+ // CHECK: //APP
+ // CHECK-NEXT: msr VTTBR_EL2, x0
+ // CHECK-NEXT: //NO_APP
+ unsafe {
+ asm!("msr vttbr_el2, x0");
+ }
+}
tst_use(arg.b);
tst_use(arg.c);
tst_use(arg.d);
+ tail_call_avoidance_fn();
}
extern "C" {
fn opaque_callee(arg: Franta, intarg: i32);
fn tst_use(arg: f32);
fn clobber();
+ // This exists so that post-https://reviews.llvm.org/D138741 LLVM doesn't
+ // tail-call away some of our assertions.
+ fn tail_call_avoidance_fn();
}
#[no_mangle]
// CHECK: call opaque_callee
// CHECK: mov 3, %o2
opaque_callee(Franta { a: 1.0, b: 2.0, c: 3.0, d: 4.0 }, 3);
+ tail_call_avoidance_fn();
}
// On riscv the closure is another function, placed before fn foo so CHECK can't
// find it
// ignore-riscv64 FIXME
+// On s390x the closure is also in another function
+// ignore-s390x FIXME
#![crate_type = "lib"]
#![feature(c_unwind)]
// CHECK: define i8 @match1{{.*}}
// CHECK-NEXT: start:
-// CHECK-NEXT: %1 = icmp ugt i8 %0, 1
-// CHECK-NEXT: %2 = zext i8 %0 to i64
-// CHECK-NEXT: %3 = add nsw i64 %2, -1
-// CHECK-NEXT: %_2 = select i1 %1, i64 %3, i64 0
-// CHECK-NEXT: switch i64 %_2, label {{.*}} [
+// CHECK-NEXT: %1 = {{.*}}call i8 @llvm.usub.sat.i8(i8 %0, i8 1)
+// CHECK-NEXT: switch i8 %1, label {{.*}} [
#[no_mangle]
pub fn match1(e: Enum1) -> u8 {
use Enum1::*;
--- /dev/null
+// compile-flags: --crate-type=lib -O -Cdebuginfo=2 -Cno-prepopulate-passes
+// min-llvm-version: 15.0 # this test uses opaque pointer notation
+#![feature(stmt_expr_attributes)]
+
+pub struct S([usize; 8]);
+
+#[no_mangle]
+pub fn outer_function(x: S, y: S) -> usize {
+ (#[inline(always)]|| {
+ let _z = x;
+ y.0[0]
+ })()
+}
+
+// Check that we do not attempt to load from the spilled arg before it is assigned to
+// when generating debuginfo.
+// CHECK-LABEL: @outer_function
+// CHECK: [[spill:%.*]] = alloca %"[closure@{{.*.rs}}:9:23: 9:25]"
+// CHECK-NOT: [[ptr_tmp:%.*]] = getelementptr inbounds %"[closure@{{.*.rs}}:9:23: 9:25]", ptr [[spill]]
+// CHECK-NOT: [[load:%.*]] = load ptr, ptr
+// CHECK: call void @llvm.lifetime.start{{.*}}({{.*}}, ptr [[spill]])
+// CHECK: call void @llvm.memcpy{{.*}}(ptr {{align .*}} [[spill]], ptr {{align .*}} %x
--- /dev/null
+// Checks that naked functions are not instrumented by -Cinstrument-coverage.
+// Regression test for issue #105170.
+//
+// needs-asm-support
+// needs-profiler-support
+// compile-flags: -Cinstrument-coverage
+#![crate_type = "lib"]
+#![feature(naked_functions)]
+use std::arch::asm;
+
+#[naked]
+#[no_mangle]
+pub unsafe extern "C" fn f() {
+ // CHECK: define void @f()
+ // CHECK-NEXT: start:
+ // CHECK-NEXT: call void asm
+ // CHECK-NEXT: unreachable
+ asm!("", options(noreturn));
+}
--- /dev/null
+// Test that __llvm_profile_counter_bias does not get internalized by lto.
+
+// ignore-macos -runtime-counter-relocation not honored on Mach-O
+// compile-flags: -Cprofile-generate -Cllvm-args=-runtime-counter-relocation -Clto=fat
+// needs-profiler-support
+// no-prefer-dynamic
+
+// CHECK: @__llvm_profile_counter_bias = {{.*}}global
+
+pub fn main() {}
include!("aux_mod.rs");
// Here we check that the expansion of the file!() macro is mapped.
-// CHECK: @alloc2 = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }>, align 1
+// CHECK: @alloc2 = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }>
pub static FILE_PATH: &'static str = file!();
fn main() {
// ignore-powerpc
// ignore-powerpc64
// ignore-riscv64 see codegen/riscv-abi
+// ignore-s390x
// ignore-windows
// See repr-transparent.rs
// ignore-riscv64 riscv64 has an i128 type used with test_Vector
// see codegen/riscv-abi for riscv functiona call tests
+// ignore-s390x s390x with default march passes vector types per reference
#![crate_type="lib"]
#![feature(repr_simd, transparent_unions)]
--- /dev/null
+// Verifies that "kcfi" module flag is added.
+//
+// needs-sanitizer-kcfi
+// compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi
+
+#![crate_type="lib"]
+
+pub fn foo() {
+}
+
+// CHECK: !{{[0-9]+}} = !{i32 4, !"kcfi", i32 1}
--- /dev/null
+// Verifies that KCFI type metadata for functions are emitted.
+//
+// revisions: aarch64 x86_64
+// [aarch64] compile-flags: --target aarch64-unknown-none
+// [aarch64] needs-llvm-components: aarch64
+// [x86_64] compile-flags: --target x86_64-unknown-none
+// [x86_64] needs-llvm-components:
+// compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi
+
+#![crate_type="lib"]
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang="sized"]
+trait Sized { }
+#[lang="copy"]
+trait Copy { }
+
+impl Copy for i32 {}
+
+pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
+ // CHECK-LABEL: define{{.*}}foo
+ // FIXME(rcvalle): Change <unknown kind #36> to !kcfi_type when Rust is updated to LLVM 16
+ // CHECK-SAME: {{.*}}!<unknown kind #36> ![[TYPE1:[0-9]+]]
+ // CHECK: call i32 %f(i32 %arg){{.*}}[ "kcfi"(i32 -1666898348) ]
+ f(arg)
+}
+
+pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
+ // CHECK-LABEL: define{{.*}}bar
+ // FIXME(rcvalle): Change <unknown kind #36> to !kcfi_type when Rust is updated to LLVM 16
+ // CHECK-SAME: {{.*}}!<unknown kind #36> ![[TYPE2:[0-9]+]]
+ // CHECK: call i32 %f(i32 %arg1, i32 %arg2){{.*}}[ "kcfi"(i32 -1789026986) ]
+ f(arg1, arg2)
+}
+
+pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
+ // CHECK-LABEL: define{{.*}}baz
+ // FIXME(rcvalle): Change <unknown kind #36> to !kcfi_type when Rust is updated to LLVM 16
+ // CHECK-SAME: {{.*}}!<unknown kind #36> ![[TYPE3:[0-9]+]]
+ // CHECK: call i32 %f(i32 %arg1, i32 %arg2, i32 %arg3){{.*}}[ "kcfi"(i32 1248878270) ]
+ f(arg1, arg2, arg3)
+}
+
+// CHECK: ![[TYPE1]] = !{i32 653723426}
+// CHECK: ![[TYPE2]] = !{i32 412174924}
+// CHECK: ![[TYPE3]] = !{i32 -636668840}
// CHECK: [[FULLY_UNINIT:@[0-9]+]] = private unnamed_addr constant <{ [10 x i8] }> undef
-// CHECK: [[PARTIALLY_UNINIT:@[0-9]+]] = private unnamed_addr constant <{ [4 x i8], [12 x i8] }> <{ [4 x i8] c"\EF\BE\AD\DE", [12 x i8] undef }>, align 4
+// CHECK: [[PARTIALLY_UNINIT:@[0-9]+]] = private unnamed_addr constant <{ [4 x i8], [12 x i8] }> <{ [4 x i8] c"{{\\EF\\BE\\AD\\DE|\\DE\\AD\\BE\\EF}}", [12 x i8] undef }>, align 4
// This shouldn't contain undef, since it contains more chunks
// than the default value of uninit_const_chunk_threshold.
bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/76803_regression.rs:+1:11: +1:12
- switchInt(move _2) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/76803_regression.rs:+1:5: +1:12
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/76803_regression.rs:+1:5: +1:12
}
bb1: {
- _2 = Ne(move _3, const true); // scope 0 at $DIR/bool_compare.rs:+1:8: +1:17
+ _2 = Not(move _3); // scope 0 at $DIR/bool_compare.rs:+1:8: +1:17
StorageDead(_3); // scope 0 at $DIR/bool_compare.rs:+1:16: +1:17
- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/bool_compare.rs:+1:8: +1:17
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/bool_compare.rs:+1:8: +1:17
}
bb1: {
- _2 = Ne(const true, move _3); // scope 0 at $DIR/bool_compare.rs:+1:8: +1:17
+ _2 = Not(move _3); // scope 0 at $DIR/bool_compare.rs:+1:8: +1:17
StorageDead(_3); // scope 0 at $DIR/bool_compare.rs:+1:16: +1:17
- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/bool_compare.rs:+1:8: +1:17
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/bool_compare.rs:+1:8: +1:17
}
bb1: {
- _2 = Eq(move _3, const false); // scope 0 at $DIR/bool_compare.rs:+1:8: +1:18
+ _2 = Not(move _3); // scope 0 at $DIR/bool_compare.rs:+1:8: +1:18
StorageDead(_3); // scope 0 at $DIR/bool_compare.rs:+1:17: +1:18
- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/bool_compare.rs:+1:8: +1:18
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/bool_compare.rs:+1:8: +1:18
}
bb1: {
- _2 = Eq(const false, move _3); // scope 0 at $DIR/bool_compare.rs:+1:8: +1:18
+ _2 = Not(move _3); // scope 0 at $DIR/bool_compare.rs:+1:8: +1:18
StorageDead(_3); // scope 0 at $DIR/bool_compare.rs:+1:17: +1:18
- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/bool_compare.rs:+1:8: +1:18
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/bool_compare.rs:+1:8: +1:18
}
bb1: {
StorageLive(_5); // scope 1 at $DIR/issue_101867.rs:+2:14: +2:15
FakeRead(ForMatchedPlace(None), _1); // scope 1 at $DIR/issue_101867.rs:+2:19: +2:20
_6 = discriminant(_1); // scope 1 at $DIR/issue_101867.rs:+2:19: +2:20
- switchInt(move _6) -> [1_isize: bb4, otherwise: bb3]; // scope 1 at $DIR/issue_101867.rs:+2:9: +2:16
+ switchInt(move _6) -> [1: bb4, otherwise: bb3]; // scope 1 at $DIR/issue_101867.rs:+2:9: +2:16
}
bb1: {
StorageLive(_3); // scope 0 at $DIR/issue_49232.rs:+3:19: +3:23
_3 = const true; // scope 0 at $DIR/issue_49232.rs:+3:19: +3:23
FakeRead(ForMatchedPlace(None), _3); // scope 0 at $DIR/issue_49232.rs:+3:19: +3:23
- switchInt(_3) -> [false: bb3, otherwise: bb4]; // scope 0 at $DIR/issue_49232.rs:+3:13: +3:23
+ switchInt(_3) -> [0: bb3, otherwise: bb4]; // scope 0 at $DIR/issue_49232.rs:+3:13: +3:23
}
bb3: {
_2 = Option::<i32>::Some(const 42_i32); // scope 0 at $DIR/match_false_edges.rs:+1:19: +1:27
FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match_false_edges.rs:+1:19: +1:27
_3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:+1:19: +1:27
- switchInt(move _3) -> [0_isize: bb1, 1_isize: bb2, otherwise: bb4]; // scope 0 at $DIR/match_false_edges.rs:+1:13: +1:27
+ switchInt(move _3) -> [0: bb1, 1: bb2, otherwise: bb4]; // scope 0 at $DIR/match_false_edges.rs:+1:13: +1:27
}
bb1: {
}
bb6: {
- switchInt(move _7) -> [false: bb8, otherwise: bb7]; // scope 0 at $DIR/match_false_edges.rs:+2:20: +2:27
+ switchInt(move _7) -> [0: bb8, otherwise: bb7]; // scope 0 at $DIR/match_false_edges.rs:+2:20: +2:27
}
bb7: {
_2 = Option::<i32>::Some(const 42_i32); // scope 0 at $DIR/match_false_edges.rs:+1:19: +1:27
FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match_false_edges.rs:+1:19: +1:27
_3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:+1:19: +1:27
- switchInt(move _3) -> [0_isize: bb1, 1_isize: bb2, otherwise: bb4]; // scope 0 at $DIR/match_false_edges.rs:+1:13: +1:27
+ switchInt(move _3) -> [0: bb1, 1: bb2, otherwise: bb4]; // scope 0 at $DIR/match_false_edges.rs:+1:13: +1:27
}
bb1: {
}
bb6: {
- switchInt(move _7) -> [false: bb8, otherwise: bb7]; // scope 0 at $DIR/match_false_edges.rs:+2:20: +2:27
+ switchInt(move _7) -> [0: bb8, otherwise: bb7]; // scope 0 at $DIR/match_false_edges.rs:+2:20: +2:27
}
bb7: {
_2 = Option::<i32>::Some(const 1_i32); // scope 0 at $DIR/match_false_edges.rs:+1:19: +1:26
FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match_false_edges.rs:+1:19: +1:26
_4 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:+1:19: +1:26
- switchInt(move _4) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/match_false_edges.rs:+1:13: +1:26
+ switchInt(move _4) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/match_false_edges.rs:+1:13: +1:26
}
bb1: {
bb4: {
_3 = discriminant(_2); // scope 0 at $DIR/match_false_edges.rs:+1:19: +1:26
- switchInt(move _3) -> [1_isize: bb6, otherwise: bb5]; // scope 0 at $DIR/match_false_edges.rs:+1:13: +1:26
+ switchInt(move _3) -> [1: bb6, otherwise: bb5]; // scope 0 at $DIR/match_false_edges.rs:+1:13: +1:26
}
bb5: {
}
bb9: {
- switchInt(move _8) -> [false: bb11, otherwise: bb10]; // scope 0 at $DIR/match_false_edges.rs:+2:21: +2:28
+ switchInt(move _8) -> [0: bb11, otherwise: bb10]; // scope 0 at $DIR/match_false_edges.rs:+2:21: +2:28
}
bb10: {
}
bb15: {
- switchInt(move _12) -> [false: bb17, otherwise: bb16]; // scope 0 at $DIR/match_false_edges.rs:+4:20: +4:29
+ switchInt(move _12) -> [0: bb17, otherwise: bb16]; // scope 0 at $DIR/match_false_edges.rs:+4:20: +4:29
}
bb16: {
bb0: {
FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/simple_match.rs:+1:11: +1:12
- switchInt(_1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simple_match.rs:+1:5: +1:12
+ switchInt(_1) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/simple_match.rs:+1:5: +1:12
}
bb1: {
bb0: {
- StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _3 = discriminant(_1); // scope 0 at $DIR/const_goto.rs:+1:17: +1:20
-- switchInt(move _3) -> [1_isize: bb2, 2_isize: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+- switchInt(move _3) -> [1: bb2, 2: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ _2 = discriminant(_1); // scope 0 at $DIR/const_goto.rs:+1:17: +1:20
-+ switchInt(move _2) -> [1_isize: bb2, 2_isize: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ switchInt(move _2) -> [1: bb2, 2: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
}
bb1: {
- }
-
- bb3: {
-- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+- switchInt(move _2) -> [0: bb5, otherwise: bb4]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
-
- bb4: {
StorageLive(_1); // scope 0 at $DIR/const_goto_const_eval_fail.rs:+1:11: +6:6
StorageLive(_2); // scope 0 at $DIR/const_goto_const_eval_fail.rs:+2:15: +2:16
_2 = const A; // scope 0 at $DIR/const_goto_const_eval_fail.rs:+2:15: +2:16
- switchInt(_2) -> [1_i32: bb2, 2_i32: bb2, 3_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/const_goto_const_eval_fail.rs:+2:9: +2:16
+ switchInt(_2) -> [1: bb2, 2: bb2, 3: bb2, otherwise: bb1]; // scope 0 at $DIR/const_goto_const_eval_fail.rs:+2:9: +2:16
}
bb1: {
bb2: {
_1 = const B; // scope 0 at $DIR/const_goto_const_eval_fail.rs:+3:26: +3:27
- goto -> bb3; // scope 0 at $DIR/const_goto_const_eval_fail.rs:+3:26: +3:27
-+ switchInt(_1) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/const_goto_const_eval_fail.rs:+1:5: +6:6
++ switchInt(_1) -> [0: bb4, otherwise: bb3]; // scope 0 at $DIR/const_goto_const_eval_fail.rs:+1:5: +6:6
}
bb3: {
-- switchInt(_1) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/const_goto_const_eval_fail.rs:+1:5: +6:6
+- switchInt(_1) -> [0: bb5, otherwise: bb4]; // scope 0 at $DIR/const_goto_const_eval_fail.rs:+1:5: +6:6
- }
-
- bb4: {
- StorageLive(_5); // scope 0 at $DIR/const_goto_storage.rs:+2:21: +2:52
- StorageLive(_6); // scope 0 at $DIR/const_goto_storage.rs:+2:24: +2:28
- _6 = const true; // scope 0 at $DIR/const_goto_storage.rs:+2:24: +2:28
-- switchInt(move _6) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/const_goto_storage.rs:+2:24: +2:28
+- switchInt(move _6) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/const_goto_storage.rs:+2:24: +2:28
+ StorageLive(_2); // scope 0 at $DIR/const_goto_storage.rs:+2:24: +2:28
+ _2 = const true; // scope 0 at $DIR/const_goto_storage.rs:+2:24: +2:28
-+ switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/const_goto_storage.rs:+2:24: +2:28
++ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/const_goto_storage.rs:+2:24: +2:28
}
bb1: {
-
- bb3: {
- StorageDead(_6); // scope 0 at $DIR/const_goto_storage.rs:+2:51: +2:52
-- switchInt(move _5) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/const_goto_storage.rs:+2:21: +2:52
+- switchInt(move _5) -> [0: bb5, otherwise: bb4]; // scope 0 at $DIR/const_goto_storage.rs:+2:21: +2:52
- }
-
- bb4: {
-
- bb6: {
- StorageDead(_5); // scope 0 at $DIR/const_goto_storage.rs:+2:75: +2:76
-- switchInt(move _4) -> [false: bb8, otherwise: bb7]; // scope 0 at $DIR/const_goto_storage.rs:+2:18: +2:76
+- switchInt(move _4) -> [0: bb8, otherwise: bb7]; // scope 0 at $DIR/const_goto_storage.rs:+2:18: +2:76
- }
-
- bb7: {
- }
-
- bb9: {
-- switchInt(move _3) -> [false: bb11, otherwise: bb10]; // scope 0 at $DIR/const_goto_storage.rs:+2:15: +6:10
+- switchInt(move _3) -> [0: bb11, otherwise: bb10]; // scope 0 at $DIR/const_goto_storage.rs:+2:15: +6:10
- }
-
- bb10: {
bb0: {
StorageLive(_1); // scope 0 at $DIR/control_flow_simplification.rs:+1:8: +1:21
_1 = const _; // scope 0 at $DIR/control_flow_simplification.rs:+1:8: +1:21
-- switchInt(move _1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/control_flow_simplification.rs:+1:8: +1:21
-+ switchInt(const false) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/control_flow_simplification.rs:+1:8: +1:21
+- switchInt(move _1) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/control_flow_simplification.rs:+1:8: +1:21
++ switchInt(const false) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/control_flow_simplification.rs:+1:8: +1:21
}
bb1: {
((_3 as Some).0: bool) = const true; // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
discriminant(_3) = 1; // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
- _4 = discriminant(_3); // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
-- switchInt(move _4) -> [1_isize: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
+- switchInt(move _4) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
+ _4 = const 1_isize; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
-+ switchInt(const 1_isize) -> [1_isize: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
++ switchInt(const 1_isize) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
}
bb1: {
- switchInt(((_3 as Some).0: bool)) -> [false: bb3, otherwise: bb2]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
+ switchInt(((_3 as Some).0: bool)) -> [0: bb3, otherwise: bb2]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
}
bb2: {
((_3 as Some).0: bool) = const true; // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
discriminant(_3) = 1; // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
- _4 = discriminant(_3); // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
-- switchInt(move _4) -> [1_isize: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
+- switchInt(move _4) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
+ _4 = const 1_isize; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
-+ switchInt(const 1_isize) -> [1_isize: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
++ switchInt(const 1_isize) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
}
bb1: {
- switchInt(((_3 as Some).0: bool)) -> [false: bb3, otherwise: bb2]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
+ switchInt(((_3 as Some).0: bool)) -> [0: bb3, otherwise: bb2]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
}
bb2: {
bb0: {
StorageLive(_1); // scope 0 at $DIR/switch_int.rs:+1:11: +1:12
_1 = const 1_i32; // scope 0 at $DIR/switch_int.rs:+1:11: +1:12
-- switchInt(_1) -> [1_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:+1:5: +1:12
-+ switchInt(const 1_i32) -> [1_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:+1:5: +1:12
+- switchInt(_1) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:+1:5: +1:12
++ switchInt(const 1_i32) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:+1:5: +1:12
}
bb1: {
bb0: {
StorageLive(_1); // scope 0 at $DIR/switch_int.rs:+1:11: +1:12
_1 = const 1_i32; // scope 0 at $DIR/switch_int.rs:+1:11: +1:12
-- switchInt(const 1_i32) -> [1_i32: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:+1:5: +1:12
+- switchInt(const 1_i32) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/switch_int.rs:+1:5: +1:12
+ goto -> bb2; // scope 0 at $DIR/switch_int.rs:+1:5: +1:12
}
bcb1__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb1</td></tr><tr><td align="left" balign="left">Expression(bcb0 + bcb3) at 10:5-11:17<br align="left"/> 11:12-11:17: @2.Call: _2 = bar() -> [return: bb3, unwind: bb6]</td></tr><tr><td align="left" balign="left">bb1: FalseUnwind<br align="left"/>bb2: Call</td></tr><tr><td align="left" balign="left">bb3: SwitchInt</td></tr></table>>];
bcb0__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb0</td></tr><tr><td align="left" balign="left"></td></tr><tr><td align="left" balign="left">Counter(bcb0) at 9:1-9:11<br align="left"/> </td></tr><tr><td align="left" balign="left">bb0: Goto</td></tr></table>>];
bcb3__Cov_0_3 -> bcb1__Cov_0_3 [label=<>];
- bcb1__Cov_0_3 -> bcb3__Cov_0_3 [label=<false>];
+ bcb1__Cov_0_3 -> bcb3__Cov_0_3 [label=<0>];
bcb1__Cov_0_3 -> bcb2__Cov_0_3 [label=<otherwise>];
bcb0__Cov_0_3 -> bcb1__Cov_0_3 [label=<>];
}
discriminant(_1) = 0; // scope 0 at $DIR/enum.rs:+1:13: +1:21
StorageLive(_2); // scope 1 at $DIR/enum.rs:+2:9: +2:10
_3 = discriminant(_1); // scope 1 at $DIR/enum.rs:+2:19: +2:20
- switchInt(move _3) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 1 at $DIR/enum.rs:+2:13: +2:20
+ switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 1 at $DIR/enum.rs:+2:13: +2:20
}
bb1: {
+ _4 = const 1_i32; // scope 1 at $DIR/if.rs:+2:16: +2:17
+ _3 = const true; // scope 1 at $DIR/if.rs:+2:16: +2:22
StorageDead(_4); // scope 1 at $DIR/if.rs:+2:21: +2:22
-- switchInt(move _3) -> [false: bb2, otherwise: bb1]; // scope 1 at $DIR/if.rs:+2:16: +2:22
-+ switchInt(const true) -> [false: bb2, otherwise: bb1]; // scope 1 at $DIR/if.rs:+2:16: +2:22
+- switchInt(move _3) -> [0: bb2, otherwise: bb1]; // scope 1 at $DIR/if.rs:+2:16: +2:22
++ switchInt(const true) -> [0: bb2, otherwise: bb1]; // scope 1 at $DIR/if.rs:+2:16: +2:22
}
bb1: {
+ _9 = const 1_i32; // scope 3 at $DIR/if.rs:+5:16: +5:17
+ _8 = const true; // scope 3 at $DIR/if.rs:+5:16: +5:22
StorageDead(_9); // scope 3 at $DIR/if.rs:+5:21: +5:22
-- switchInt(move _8) -> [false: bb5, otherwise: bb4]; // scope 3 at $DIR/if.rs:+5:16: +5:22
-+ switchInt(const true) -> [false: bb5, otherwise: bb4]; // scope 3 at $DIR/if.rs:+5:16: +5:22
+- switchInt(move _8) -> [0: bb5, otherwise: bb4]; // scope 3 at $DIR/if.rs:+5:16: +5:22
++ switchInt(const true) -> [0: bb5, otherwise: bb4]; // scope 3 at $DIR/if.rs:+5:16: +5:22
}
bb4: {
StorageLive(_1); // scope 0 at $DIR/issue_81605.rs:+1:9: +1:33
StorageLive(_2); // scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
_2 = const true; // scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
-- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
-+ switchInt(const true) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
+- switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
++ switchInt(const true) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
}
bb1: {
}
bb2: {
-- switchInt(move _5) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/cycle.rs:+3:11: +3:17
-+ switchInt(move _4) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/cycle.rs:+3:11: +3:17
+- switchInt(move _5) -> [0: bb4, otherwise: bb3]; // scope 0 at $DIR/cycle.rs:+3:11: +3:17
++ switchInt(move _4) -> [0: bb4, otherwise: bb3]; // scope 0 at $DIR/cycle.rs:+3:11: +3:17
}
bb3: {
bb0: {
StorageLive(_3); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+1:8: +1:9
_3 = _1; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+1:8: +1:9
- switchInt(move _3) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+1:8: +1:9
+ switchInt(move _3) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+1:8: +1:9
}
bb1: {
_7 = Len((*_2)); // scope 0 at $DIR/deduplicate_blocks.rs:+2:9: +2:37
_8 = const 4_usize; // scope 0 at $DIR/deduplicate_blocks.rs:+2:9: +2:37
_9 = Ge(move _7, move _8); // scope 0 at $DIR/deduplicate_blocks.rs:+2:9: +2:37
- switchInt(move _9) -> [false: bb6, otherwise: bb2]; // scope 0 at $DIR/deduplicate_blocks.rs:+2:9: +2:37
+ switchInt(move _9) -> [0: bb6, otherwise: bb2]; // scope 0 at $DIR/deduplicate_blocks.rs:+2:9: +2:37
}
bb2: {
- switchInt((*_2)[0 of 4]) -> [47_u8: bb3, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
+ switchInt((*_2)[0 of 4]) -> [47: bb3, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
}
bb3: {
- switchInt((*_2)[1 of 4]) -> [47_u8: bb4, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
+ switchInt((*_2)[1 of 4]) -> [47: bb4, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
}
bb4: {
- switchInt((*_2)[2 of 4]) -> [47_u8: bb5, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
+ switchInt((*_2)[2 of 4]) -> [47: bb5, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
}
bb5: {
-- switchInt((*_2)[3 of 4]) -> [47_u8: bb11, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
-+ switchInt((*_2)[3 of 4]) -> [47_u8: bb10, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
+- switchInt((*_2)[3 of 4]) -> [47: bb11, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
++ switchInt((*_2)[3 of 4]) -> [47: bb10, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
}
bb6: {
_4 = Len((*_2)); // scope 0 at $DIR/deduplicate_blocks.rs:+3:9: +3:31
_5 = const 3_usize; // scope 0 at $DIR/deduplicate_blocks.rs:+3:9: +3:31
_6 = Ge(move _4, move _5); // scope 0 at $DIR/deduplicate_blocks.rs:+3:9: +3:31
- switchInt(move _6) -> [false: bb10, otherwise: bb7]; // scope 0 at $DIR/deduplicate_blocks.rs:+3:9: +3:31
+ switchInt(move _6) -> [0: bb10, otherwise: bb7]; // scope 0 at $DIR/deduplicate_blocks.rs:+3:9: +3:31
}
bb7: {
- switchInt((*_2)[0 of 3]) -> [47_u8: bb8, otherwise: bb10]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
+ switchInt((*_2)[0 of 3]) -> [47: bb8, otherwise: bb10]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
}
bb8: {
- switchInt((*_2)[1 of 3]) -> [47_u8: bb9, otherwise: bb10]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
+ switchInt((*_2)[1 of 3]) -> [47: bb9, otherwise: bb10]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
}
bb9: {
-- switchInt((*_2)[2 of 3]) -> [47_u8: bb12, 33_u8: bb13, otherwise: bb10]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
-+ switchInt((*_2)[2 of 3]) -> [47_u8: bb11, 33_u8: bb11, otherwise: bb10]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
+- switchInt((*_2)[2 of 3]) -> [47: bb12, 33: bb13, otherwise: bb10]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
++ switchInt((*_2)[2 of 3]) -> [47: bb11, 33: bb11, otherwise: bb10]; // scope 0 at $DIR/deduplicate_blocks.rs:+1:5: +1:23
}
bb10: {
_7 = const false; // scope 0 at $DIR/string.rs:+1:11: +1:12
_7 = const true; // scope 0 at $DIR/string.rs:+1:11: +1:12
_5 = discriminant(_1); // scope 0 at $DIR/string.rs:+1:11: +1:12
- switchInt(move _5) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/string.rs:+1:5: +1:12
+ switchInt(move _5) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/string.rs:+1:5: +1:12
}
bb1: {
}
bb4: {
- switchInt(move _4) -> [false: bb1, otherwise: bb5]; // scope 0 at $DIR/string.rs:+2:14: +2:17
+ switchInt(move _4) -> [0: bb1, otherwise: bb5]; // scope 0 at $DIR/string.rs:+2:14: +2:17
}
bb5: {
}
bb9: {
- switchInt(_7) -> [false: bb7, otherwise: bb8]; // scope 0 at $DIR/string.rs:+5:1: +5:2
+ switchInt(_7) -> [0: bb7, otherwise: bb8]; // scope 0 at $DIR/string.rs:+5:1: +5:2
}
}
bb3: {
StorageDead(_8); // scope 1 at $DIR/derefer_complex_case.rs:+1:25: +1:26
_10 = discriminant(_7); // scope 1 at $DIR/derefer_complex_case.rs:+1:17: +1:26
- switchInt(move _10) -> [0_isize: bb6, 1_isize: bb4, otherwise: bb5]; // scope 1 at $DIR/derefer_complex_case.rs:+1:17: +1:26
+ switchInt(move _10) -> [0: bb6, 1: bb4, otherwise: bb5]; // scope 1 at $DIR/derefer_complex_case.rs:+1:17: +1:26
}
bb4: {
_6 = &_7; // scope 2 at $DIR/derefer_terminator_test.rs:+3:18: +3:21
_5 = &_6; // scope 2 at $DIR/derefer_terminator_test.rs:+3:17: +3:21
_4 = &_5; // scope 2 at $DIR/derefer_terminator_test.rs:+3:15: +3:22
-- switchInt((*(*(*(*_4))))) -> [false: bb3, otherwise: bb4]; // scope 2 at $DIR/derefer_terminator_test.rs:+3:5: +3:22
+- switchInt((*(*(*(*_4))))) -> [0: bb3, otherwise: bb4]; // scope 2 at $DIR/derefer_terminator_test.rs:+3:5: +3:22
+ _10 = deref_copy (*_4); // scope 2 at $DIR/derefer_terminator_test.rs:+3:5: +3:22
+ _11 = deref_copy (*_10); // scope 2 at $DIR/derefer_terminator_test.rs:+3:5: +3:22
+ _12 = deref_copy (*_11); // scope 2 at $DIR/derefer_terminator_test.rs:+3:5: +3:22
-+ switchInt((*_12)) -> [false: bb3, otherwise: bb4]; // scope 2 at $DIR/derefer_terminator_test.rs:+3:5: +3:22
++ switchInt((*_12)) -> [0: bb3, otherwise: bb4]; // scope 2 at $DIR/derefer_terminator_test.rs:+3:5: +3:22
}
bb3: {
}
bb2: {
- switchInt(move _3) -> [false: bb4, otherwise: bb3]; // scope 1 at $DIR/branch.rs:+3:16: +3:22
+ switchInt(move _3) -> [0: bb4, otherwise: bb3]; // scope 1 at $DIR/branch.rs:+3:16: +3:22
}
bb3: {
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
_7 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
-- switchInt(move _7) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+- switchInt(move _7) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ StorageLive(_10); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ _10 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ StorageLive(_11); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ _11 = Ne(_7, move _10); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ StorageDead(_10); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
-+ switchInt(move _11) -> [false: bb4, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
++ switchInt(move _11) -> [0: bb4, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
}
bb1: {
bb2: {
- _6 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
-- switchInt(move _6) -> [1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+- switchInt(move _6) -> [1: bb3, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
- }
-
- bb3: {
+
+ bb4: {
+ StorageDead(_11); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
-+ switchInt(_7) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
++ switchInt(_7) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
}
}
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
_8 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
-- switchInt(move _8) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+- switchInt(move _8) -> [0: bb1, 1: bb3, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ StorageLive(_11); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ _11 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ StorageLive(_12); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ _12 = Ne(_8, move _11); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ StorageDead(_11); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
-+ switchInt(move _12) -> [false: bb5, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
++ switchInt(move _12) -> [0: bb5, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
}
bb1: {
- _6 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
-- switchInt(move _6) -> [0_isize: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+- switchInt(move _6) -> [0: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
- }
-
- bb2: {
- bb3: {
- _7 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
-- switchInt(move _7) -> [1_isize: bb4, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+- switchInt(move _7) -> [1: bb4, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
- }
-
- bb4: {
+
+ bb5: {
+ StorageDead(_12); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
-+ switchInt(_8) -> [0_isize: bb3, 1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
++ switchInt(_8) -> [0: bb3, 1: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
}
}
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
_7 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
-- switchInt(move _7) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+- switchInt(move _7) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ StorageLive(_10); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ _10 = discriminant((_3.1: std::option::Option<bool>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ StorageLive(_11); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ _11 = Ne(_7, move _10); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+ StorageDead(_10); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
-+ switchInt(move _11) -> [false: bb4, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
++ switchInt(move _11) -> [0: bb4, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
}
bb1: {
bb2: {
- _6 = discriminant((_3.1: std::option::Option<bool>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
-- switchInt(move _6) -> [1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
+- switchInt(move _6) -> [1: bb3, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
- }
-
- bb3: {
+
+ bb4: {
+ StorageDead(_11); // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
-+ switchInt(_7) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
++ switchInt(_7) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:+1:5: +1:17
}
}
StorageDead(_6); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:19: +1:20
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:19: +1:20
_10 = discriminant((_4.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:11: +1:20
-- switchInt(move _10) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
+- switchInt(move _10) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
+ StorageLive(_14); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
+ _14 = discriminant((_4.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
+ StorageLive(_15); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
+ _15 = Ne(_10, move _14); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
+ StorageDead(_14); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
-+ switchInt(move _15) -> [false: bb5, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
++ switchInt(move _15) -> [0: bb5, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
}
bb1: {
bb2: {
- _9 = discriminant((_4.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:11: +1:20
-- switchInt(move _9) -> [1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
+- switchInt(move _9) -> [1: bb3, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
- }
-
- bb3: {
_8 = discriminant((_4.2: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:11: +1:20
-- switchInt(move _8) -> [1_isize: bb4, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
-+ switchInt(move _8) -> [1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
+- switchInt(move _8) -> [1: bb4, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
++ switchInt(move _8) -> [1: bb3, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
}
- bb4: {
+
+ bb5: {
+ StorageDead(_15); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
-+ switchInt(_10) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
++ switchInt(_10) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:5: +1:20
}
}
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:23: +5:24
_34 = deref_copy (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
_11 = discriminant((*_34)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
+ switchInt(move _11) -> [0: bb1, 1: bb3, 2: bb4, 3: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
}
bb1: {
_35 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
_7 = discriminant((*_35)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _7) -> [0_isize: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
+ switchInt(move _7) -> [0: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
}
bb2: {
bb3: {
_36 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
_8 = discriminant((*_36)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
+ switchInt(move _8) -> [1: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
}
bb4: {
_37 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
_9 = discriminant((*_37)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
+ switchInt(move _9) -> [2: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
}
bb5: {
_38 = deref_copy (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
_10 = discriminant((*_38)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
+ switchInt(move _10) -> [3: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:8: +5:24
}
bb6: {
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:16: +1:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:16: +1:17
_8 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:11: +1:17
- switchInt(move _8) -> [0_isize: bb1, 1_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:5: +1:17
+ switchInt(move _8) -> [0: bb1, 1: bb4, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:5: +1:17
}
bb1: {
_6 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:11: +1:17
- switchInt(move _6) -> [0_isize: bb2, 1_isize: bb7, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:5: +1:17
+ switchInt(move _6) -> [0: bb2, 1: bb7, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:5: +1:17
}
bb2: {
bb4: {
_7 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:11: +1:17
- switchInt(move _7) -> [0_isize: bb6, 1_isize: bb5, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:5: +1:17
+ switchInt(move _7) -> [0: bb6, 1: bb5, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:5: +1:17
}
bb5: {
bb0: {
_3 = discriminant(_1); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:+1:11: +1:12
- switchInt(move _3) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:+1:5: +1:12
+ switchInt(move _3) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:+1:5: +1:12
}
bb1: {
bb2: {
_4 = discriminant((*_2)); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:+3:26: +3:28
- switchInt(move _4) -> [1_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:+3:20: +3:28
+ switchInt(move _4) -> [1: bb4, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:+3:20: +3:28
}
bb3: {
bb0: {
_3 = discriminant((*_1)); // scope 1 at $DIR/early_otherwise_branch_soundness.rs:+1:12: +1:31
- switchInt(move _3) -> [1_isize: bb1, otherwise: bb3]; // scope 1 at $DIR/early_otherwise_branch_soundness.rs:+1:12: +1:31
+ switchInt(move _3) -> [1: bb1, otherwise: bb3]; // scope 1 at $DIR/early_otherwise_branch_soundness.rs:+1:12: +1:31
}
bb1: {
_4 = deref_copy (((*_1) as Some).0: &E<'_>); // scope 1 at $DIR/early_otherwise_branch_soundness.rs:+1:12: +1:31
_2 = discriminant((*_4)); // scope 1 at $DIR/early_otherwise_branch_soundness.rs:+1:12: +1:31
- switchInt(move _2) -> [1_isize: bb2, otherwise: bb3]; // scope 1 at $DIR/early_otherwise_branch_soundness.rs:+1:12: +1:31
+ switchInt(move _2) -> [1: bb2, otherwise: bb3]; // scope 1 at $DIR/early_otherwise_branch_soundness.rs:+1:12: +1:31
}
bb2: {
- _2 = Eq(move _3, const true); // scope 0 at $DIR/equal_true.rs:+1:8: +1:17
+ _2 = move _3; // scope 0 at $DIR/equal_true.rs:+1:8: +1:17
StorageDead(_3); // scope 0 at $DIR/equal_true.rs:+1:16: +1:17
- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/equal_true.rs:+1:8: +1:17
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/equal_true.rs:+1:8: +1:17
}
bb1: {
bb0: {
FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/exponential_or.rs:+1:11: +1:12
- switchInt((_1.0: u32)) -> [1_u32: bb2, 4_u32: bb2, otherwise: bb1]; // scope 0 at $DIR/exponential_or.rs:+2:15: +2:20
+ switchInt((_1.0: u32)) -> [1: bb2, 4: bb2, otherwise: bb1]; // scope 0 at $DIR/exponential_or.rs:+2:15: +2:20
}
bb1: {
bb2: {
_2 = discriminant((_1.2: std::option::Option<i32>)); // scope 0 at $DIR/exponential_or.rs:+2:37: +2:55
- switchInt(move _2) -> [0_isize: bb4, 1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/exponential_or.rs:+2:37: +2:55
+ switchInt(move _2) -> [0: bb4, 1: bb3, otherwise: bb1]; // scope 0 at $DIR/exponential_or.rs:+2:37: +2:55
}
bb3: {
- switchInt((((_1.2: std::option::Option<i32>) as Some).0: i32)) -> [1_i32: bb4, 8_i32: bb4, otherwise: bb1]; // scope 0 at $DIR/exponential_or.rs:+2:37: +2:55
+ switchInt((((_1.2: std::option::Option<i32>) as Some).0: i32)) -> [1: bb4, 8: bb4, otherwise: bb1]; // scope 0 at $DIR/exponential_or.rs:+2:37: +2:55
}
bb4: {
_5 = Le(const 6_u32, (_1.3: u32)); // scope 0 at $DIR/exponential_or.rs:+2:62: +2:67
- switchInt(move _5) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/exponential_or.rs:+2:62: +2:67
+ switchInt(move _5) -> [0: bb6, otherwise: bb5]; // scope 0 at $DIR/exponential_or.rs:+2:62: +2:67
}
bb5: {
_6 = Le((_1.3: u32), const 9_u32); // scope 0 at $DIR/exponential_or.rs:+2:62: +2:67
- switchInt(move _6) -> [false: bb6, otherwise: bb8]; // scope 0 at $DIR/exponential_or.rs:+2:62: +2:67
+ switchInt(move _6) -> [0: bb6, otherwise: bb8]; // scope 0 at $DIR/exponential_or.rs:+2:62: +2:67
}
bb6: {
_3 = Le(const 13_u32, (_1.3: u32)); // scope 0 at $DIR/exponential_or.rs:+2:70: +2:77
- switchInt(move _3) -> [false: bb1, otherwise: bb7]; // scope 0 at $DIR/exponential_or.rs:+2:70: +2:77
+ switchInt(move _3) -> [0: bb1, otherwise: bb7]; // scope 0 at $DIR/exponential_or.rs:+2:70: +2:77
}
bb7: {
_4 = Le((_1.3: u32), const 16_u32); // scope 0 at $DIR/exponential_or.rs:+2:70: +2:77
- switchInt(move _4) -> [false: bb1, otherwise: bb8]; // scope 0 at $DIR/exponential_or.rs:+2:70: +2:77
+ switchInt(move _4) -> [0: bb1, otherwise: bb8]; // scope 0 at $DIR/exponential_or.rs:+2:70: +2:77
}
bb8: {
bb1: {
StorageDead(_5); // scope 0 at $DIR/funky_arms.rs:+4:36: +4:37
StorageLive(_6); // scope 1 at $DIR/funky_arms.rs:+8:9: +8:13
- switchInt(_4) -> [false: bb3, otherwise: bb2]; // scope 1 at $DIR/funky_arms.rs:+8:16: +8:32
+ switchInt(_4) -> [0: bb3, otherwise: bb2]; // scope 1 at $DIR/funky_arms.rs:+8:16: +8:32
}
bb2: {
bb5: {
StorageDead(_8); // scope 3 at $DIR/funky_arms.rs:+13:44: +13:45
_9 = discriminant(_7); // scope 3 at $DIR/funky_arms.rs:+13:12: +13:27
- switchInt(move _9) -> [1_isize: bb6, otherwise: bb8]; // scope 3 at $DIR/funky_arms.rs:+13:12: +13:27
+ switchInt(move _9) -> [1: bb6, otherwise: bb8]; // scope 3 at $DIR/funky_arms.rs:+13:12: +13:27
}
bb6: {
bb0: {
_8 = discriminant((*_1)); // scope 0 at $DIR/generator_drop_cleanup.rs:+0:15: +3:6
- switchInt(move _8) -> [0_u32: bb7, 3_u32: bb10, otherwise: bb11]; // scope 0 at $DIR/generator_drop_cleanup.rs:+0:15: +3:6
+ switchInt(move _8) -> [0: bb7, 3: bb10, otherwise: bb11]; // scope 0 at $DIR/generator_drop_cleanup.rs:+0:15: +3:6
}
bb1: {
bb0: {
_11 = discriminant((*(_1.0: &mut [generator@$DIR/generator_tiny.rs:19:16: 19:24]))); // scope 0 at $DIR/generator_tiny.rs:+0:16: +6:6
- switchInt(move _11) -> [0_u32: bb1, 3_u32: bb5, otherwise: bb6]; // scope 0 at $DIR/generator_tiny.rs:+0:16: +6:6
+ switchInt(move _11) -> [0: bb1, 3: bb5, otherwise: bb6]; // scope 0 at $DIR/generator_tiny.rs:+0:16: +6:6
}
bb1: {
bb0: {
StorageLive(_2); // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:9
_2 = _1; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:9
- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:9
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:9
}
bb1: {
_3 = _1; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:9
_2 = Eq(move _3, const -42f32); // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:18
StorageDead(_3); // scope 0 at $DIR/if_condition_int.rs:+1:17: +1:18
- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:18
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:18
}
bb1: {
_3 = _1; // scope 0 at $DIR/if_condition_int.rs:+1:13: +1:14
- _2 = Eq(move _3, const 17_i8); // scope 0 at $DIR/if_condition_int.rs:+1:13: +1:20
- StorageDead(_3); // scope 0 at $DIR/if_condition_int.rs:+1:19: +1:20
-- switchInt(_2) -> [false: bb2, otherwise: bb1]; // scope 1 at $DIR/if_condition_int.rs:+2:5: +2:12
+- switchInt(_2) -> [0: bb2, otherwise: bb1]; // scope 1 at $DIR/if_condition_int.rs:+2:5: +2:12
+ _2 = Eq(_3, const 17_i8); // scope 0 at $DIR/if_condition_int.rs:+1:13: +1:20
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:19: +1:20
-+ switchInt(move _3) -> [17_i8: bb1, otherwise: bb2]; // scope 1 at $DIR/if_condition_int.rs:+2:5: +2:12
++ switchInt(move _3) -> [17: bb1, otherwise: bb2]; // scope 1 at $DIR/if_condition_int.rs:+2:5: +2:12
}
bb1: {
_3 = _1; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:9
- _2 = Eq(move _3, const 'x'); // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
- StorageDead(_3); // scope 0 at $DIR/if_condition_int.rs:+1:15: +1:16
-- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
+- switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:15: +1:16
-+ switchInt(move _3) -> ['x': bb1, otherwise: bb2]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
++ switchInt(move _3) -> [120: bb1, otherwise: bb2]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
}
bb1: {
_3 = _1; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:9
- _2 = Eq(move _3, const 42_i8); // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
- StorageDead(_3); // scope 0 at $DIR/if_condition_int.rs:+1:14: +1:15
-- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
+- switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:14: +1:15
-+ switchInt(move _3) -> [42_i8: bb1, otherwise: bb2]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
++ switchInt(move _3) -> [42: bb1, otherwise: bb2]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
}
bb1: {
_3 = _1; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:9
- _2 = Eq(move _3, const 42_u32); // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
- StorageDead(_3); // scope 0 at $DIR/if_condition_int.rs:+1:14: +1:15
-- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
+- switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:14: +1:15
-+ switchInt(move _3) -> [42_u32: bb1, otherwise: bb2]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
++ switchInt(move _3) -> [42: bb1, otherwise: bb2]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
}
bb1: {
_5 = _1; // scope 0 at $DIR/if_condition_int.rs:+3:15: +3:16
- _4 = Ne(move _5, const 21_u32); // scope 0 at $DIR/if_condition_int.rs:+3:15: +3:22
- StorageDead(_5); // scope 0 at $DIR/if_condition_int.rs:+3:21: +3:22
-- switchInt(move _4) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/if_condition_int.rs:+3:15: +3:22
+- switchInt(move _4) -> [0: bb4, otherwise: bb3]; // scope 0 at $DIR/if_condition_int.rs:+3:15: +3:22
+ nop; // scope 0 at $DIR/if_condition_int.rs:+3:15: +3:22
+ nop; // scope 0 at $DIR/if_condition_int.rs:+3:21: +3:22
-+ switchInt(move _5) -> [21_u32: bb4, otherwise: bb3]; // scope 0 at $DIR/if_condition_int.rs:+3:15: +3:22
++ switchInt(move _5) -> [21: bb4, otherwise: bb3]; // scope 0 at $DIR/if_condition_int.rs:+3:15: +3:22
}
bb3: {
_3 = _1; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:9
- _2 = Eq(move _3, const -42_i32); // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
- StorageDead(_3); // scope 0 at $DIR/if_condition_int.rs:+1:15: +1:16
-- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
+- switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:15: +1:16
-+ switchInt(move _3) -> [-42_i32: bb1, otherwise: bb2]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
++ switchInt(move _3) -> [4294967254: bb1, otherwise: bb2]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:16
}
bb1: {
_3 = _1; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:9
- _2 = Eq(move _3, const 42_u32); // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
- StorageDead(_3); // scope 0 at $DIR/if_condition_int.rs:+1:14: +1:15
-- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
+- switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
+ nop; // scope 0 at $DIR/if_condition_int.rs:+1:14: +1:15
-+ switchInt(move _3) -> [42_u32: bb1, otherwise: bb2]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
++ switchInt(move _3) -> [42: bb1, otherwise: bb2]; // scope 0 at $DIR/if_condition_int.rs:+1:8: +1:15
}
bb1: {
+ let _3: (); // in scope 1 at $DIR/cycle.rs:6:5: 6:8
+ let mut _4: &fn() {main}; // in scope 1 at $DIR/cycle.rs:6:5: 6:6
+ let mut _5: (); // in scope 1 at $DIR/cycle.rs:6:5: 6:8
-+ scope 2 (inlined <fn() {main} as Fn<()>>::call - shim(fn() {main})) { // at $DIR/cycle.rs:6:5: 6:8
-+ }
+ }
bb0: {
+ StorageLive(_4); // scope 1 at $DIR/cycle.rs:6:5: 6:6
+ _4 = &_2; // scope 1 at $DIR/cycle.rs:6:5: 6:6
+ StorageLive(_5); // scope 1 at $DIR/cycle.rs:6:5: 6:8
-+ _3 = move (*_4)() -> [return: bb4, unwind: bb2]; // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL
++ _3 = <fn() {main} as Fn<()>>::call(move _4, move _5) -> [return: bb2, unwind: bb3]; // scope 1 at $DIR/cycle.rs:6:5: 6:8
++ // mir::Constant
++ // + span: $DIR/cycle.rs:6:5: 6:6
++ // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a fn() {main}, ()) -> <fn() {main} as FnOnce<()>>::Output {<fn() {main} as Fn<()>>::call}, val: Value(<ZST>) }
}
bb1: {
return; // scope 0 at $DIR/cycle.rs:+2:2: +2:2
+ }
+
-+ bb2 (cleanup): {
-+ drop(_2) -> bb3; // scope 1 at $DIR/cycle.rs:7:1: 7:2
++ bb2: {
++ StorageDead(_5); // scope 1 at $DIR/cycle.rs:6:7: 6:8
++ StorageDead(_4); // scope 1 at $DIR/cycle.rs:6:7: 6:8
++ StorageDead(_3); // scope 1 at $DIR/cycle.rs:6:8: 6:9
++ drop(_2) -> bb1; // scope 1 at $DIR/cycle.rs:7:1: 7:2
+ }
+
+ bb3 (cleanup): {
-+ resume; // scope 1 at $DIR/cycle.rs:5:1: 7:2
++ drop(_2) -> bb4; // scope 1 at $DIR/cycle.rs:7:1: 7:2
+ }
+
-+ bb4: {
-+ StorageDead(_5); // scope 1 at $DIR/cycle.rs:6:7: 6:8
-+ StorageDead(_4); // scope 1 at $DIR/cycle.rs:6:7: 6:8
-+ StorageDead(_3); // scope 1 at $DIR/cycle.rs:6:8: 6:9
-+ drop(_2) -> bb1; // scope 1 at $DIR/cycle.rs:7:1: 7:2
++ bb4 (cleanup): {
++ resume; // scope 1 at $DIR/cycle.rs:5:1: 7:2
}
}
+ let _3: (); // in scope 1 at $DIR/cycle.rs:6:5: 6:8
+ let mut _4: &fn() {g}; // in scope 1 at $DIR/cycle.rs:6:5: 6:6
+ let mut _5: (); // in scope 1 at $DIR/cycle.rs:6:5: 6:8
-+ scope 2 (inlined <fn() {g} as Fn<()>>::call - shim(fn() {g})) { // at $DIR/cycle.rs:6:5: 6:8
-+ scope 3 (inlined g) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL
-+ let mut _6: fn() {main}; // in scope 3 at $DIR/cycle.rs:12:5: 12:12
-+ scope 4 (inlined f::<fn() {main}>) { // at $DIR/cycle.rs:12:5: 12:12
-+ debug g => _6; // in scope 4 at $DIR/cycle.rs:5:6: 5:7
-+ let _7: (); // in scope 4 at $DIR/cycle.rs:6:5: 6:8
-+ let mut _8: &fn() {main}; // in scope 4 at $DIR/cycle.rs:6:5: 6:6
-+ scope 5 (inlined <fn() {main} as Fn<()>>::call - shim(fn() {main})) { // at $DIR/cycle.rs:6:5: 6:8
-+ }
-+ }
-+ }
-+ }
+ }
bb0: {
+ StorageLive(_4); // scope 1 at $DIR/cycle.rs:6:5: 6:6
+ _4 = &_2; // scope 1 at $DIR/cycle.rs:6:5: 6:6
+ StorageLive(_5); // scope 1 at $DIR/cycle.rs:6:5: 6:8
-+ StorageLive(_6); // scope 3 at $DIR/cycle.rs:12:5: 12:12
-+ StorageLive(_7); // scope 4 at $DIR/cycle.rs:6:5: 6:8
-+ StorageLive(_8); // scope 4 at $DIR/cycle.rs:6:5: 6:6
-+ _8 = &_6; // scope 4 at $DIR/cycle.rs:6:5: 6:6
-+ _7 = move (*_8)() -> [return: bb4, unwind: bb2]; // scope 5 at $SRC_DIR/core/src/ops/function.rs:LL:COL
++ _3 = <fn() {g} as Fn<()>>::call(move _4, move _5) -> [return: bb2, unwind: bb3]; // scope 1 at $DIR/cycle.rs:6:5: 6:8
++ // mir::Constant
++ // + span: $DIR/cycle.rs:6:5: 6:6
++ // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a fn() {g}, ()) -> <fn() {g} as FnOnce<()>>::Output {<fn() {g} as Fn<()>>::call}, val: Value(<ZST>) }
}
bb1: {
return; // scope 0 at $DIR/cycle.rs:+2:2: +2:2
+ }
+
-+ bb2 (cleanup): {
-+ drop(_2) -> bb3; // scope 1 at $DIR/cycle.rs:7:1: 7:2
++ bb2: {
++ StorageDead(_5); // scope 1 at $DIR/cycle.rs:6:7: 6:8
++ StorageDead(_4); // scope 1 at $DIR/cycle.rs:6:7: 6:8
++ StorageDead(_3); // scope 1 at $DIR/cycle.rs:6:8: 6:9
++ drop(_2) -> bb1; // scope 1 at $DIR/cycle.rs:7:1: 7:2
+ }
+
+ bb3 (cleanup): {
-+ resume; // scope 1 at $DIR/cycle.rs:5:1: 7:2
++ drop(_2) -> bb4; // scope 1 at $DIR/cycle.rs:7:1: 7:2
+ }
+
-+ bb4: {
-+ StorageDead(_8); // scope 4 at $DIR/cycle.rs:6:7: 6:8
-+ StorageDead(_7); // scope 4 at $DIR/cycle.rs:6:8: 6:9
-+ StorageDead(_6); // scope 3 at $DIR/cycle.rs:12:5: 12:12
-+ StorageDead(_5); // scope 1 at $DIR/cycle.rs:6:7: 6:8
-+ StorageDead(_4); // scope 1 at $DIR/cycle.rs:6:7: 6:8
-+ StorageDead(_3); // scope 1 at $DIR/cycle.rs:6:8: 6:9
-+ drop(_2) -> bb1; // scope 1 at $DIR/cycle.rs:7:1: 7:2
++ bb4 (cleanup): {
++ resume; // scope 1 at $DIR/cycle.rs:5:1: 7:2
}
}
--- /dev/null
+- // MIR for `main` before Inline
++ // MIR for `main` after Inline
+
+ fn main() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/exponential_runtime.rs:+0:11: +0:11
+ let _1: (); // in scope 0 at $DIR/exponential_runtime.rs:+1:5: +1:22
++ scope 1 (inlined <() as G>::call) { // at $DIR/exponential_runtime.rs:86:5: 86:22
++ let _2: (); // in scope 1 at $DIR/exponential_runtime.rs:73:9: 73:25
++ let _3: (); // in scope 1 at $DIR/exponential_runtime.rs:74:9: 74:25
++ let _4: (); // in scope 1 at $DIR/exponential_runtime.rs:75:9: 75:25
++ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/exponential_runtime.rs:+1:5: +1:22
+- _1 = <() as G>::call() -> bb1; // scope 0 at $DIR/exponential_runtime.rs:+1:5: +1:22
++ StorageLive(_2); // scope 1 at $DIR/exponential_runtime.rs:73:9: 73:25
++ _2 = <() as F>::call() -> bb1; // scope 1 at $DIR/exponential_runtime.rs:73:9: 73:25
+ // mir::Constant
+- // + span: $DIR/exponential_runtime.rs:86:5: 86:20
+- // + literal: Const { ty: fn() {<() as G>::call}, val: Value(<ZST>) }
++ // + span: $DIR/exponential_runtime.rs:73:9: 73:23
++ // + literal: Const { ty: fn() {<() as F>::call}, val: Value(<ZST>) }
+ }
+
+ bb1: {
++ StorageDead(_2); // scope 1 at $DIR/exponential_runtime.rs:73:25: 73:26
++ StorageLive(_3); // scope 1 at $DIR/exponential_runtime.rs:74:9: 74:25
++ _3 = <() as F>::call() -> bb2; // scope 1 at $DIR/exponential_runtime.rs:74:9: 74:25
++ // mir::Constant
++ // + span: $DIR/exponential_runtime.rs:74:9: 74:23
++ // + literal: Const { ty: fn() {<() as F>::call}, val: Value(<ZST>) }
++ }
++
++ bb2: {
++ StorageDead(_3); // scope 1 at $DIR/exponential_runtime.rs:74:25: 74:26
++ StorageLive(_4); // scope 1 at $DIR/exponential_runtime.rs:75:9: 75:25
++ _4 = <() as F>::call() -> bb3; // scope 1 at $DIR/exponential_runtime.rs:75:9: 75:25
++ // mir::Constant
++ // + span: $DIR/exponential_runtime.rs:75:9: 75:23
++ // + literal: Const { ty: fn() {<() as F>::call}, val: Value(<ZST>) }
++ }
++
++ bb3: {
++ StorageDead(_4); // scope 1 at $DIR/exponential_runtime.rs:75:25: 75:26
+ StorageDead(_1); // scope 0 at $DIR/exponential_runtime.rs:+1:22: +1:23
+ _0 = const (); // scope 0 at $DIR/exponential_runtime.rs:+0:11: +2:2
+ return; // scope 0 at $DIR/exponential_runtime.rs:+2:2: +2:2
+ }
+ }
+
--- /dev/null
+// Checks that code with exponential runtime does not have exponential behavior in inlining.
+
+trait A {
+ fn call();
+}
+
+trait B {
+ fn call();
+}
+impl<T: A> B for T {
+ #[inline]
+ fn call() {
+ <T as A>::call();
+ <T as A>::call();
+ <T as A>::call();
+ }
+}
+
+trait C {
+ fn call();
+}
+impl<T: B> C for T {
+ #[inline]
+ fn call() {
+ <T as B>::call();
+ <T as B>::call();
+ <T as B>::call();
+ }
+}
+
+trait D {
+ fn call();
+}
+impl<T: C> D for T {
+ #[inline]
+ fn call() {
+ <T as C>::call();
+ <T as C>::call();
+ <T as C>::call();
+ }
+}
+
+trait E {
+ fn call();
+}
+impl<T: D> E for T {
+ #[inline]
+ fn call() {
+ <T as D>::call();
+ <T as D>::call();
+ <T as D>::call();
+ }
+}
+
+trait F {
+ fn call();
+}
+impl<T: E> F for T {
+ #[inline]
+ fn call() {
+ <T as E>::call();
+ <T as E>::call();
+ <T as E>::call();
+ }
+}
+
+trait G {
+ fn call();
+}
+impl<T: F> G for T {
+ #[inline]
+ fn call() {
+ <T as F>::call();
+ <T as F>::call();
+ <T as F>::call();
+ }
+}
+
+impl A for () {
+ #[inline(never)]
+ fn call() {}
+}
+
+// EMIT_MIR exponential_runtime.main.Inline.diff
+fn main() {
+ <() as G>::call();
+}
let mut _0: (); // return place in scope 0 at $DIR/inline_cycle.rs:+0:10: +0:10
let _1: (); // in scope 0 at $DIR/inline_cycle.rs:+1:5: +1:24
+ scope 1 (inlined <C as Call>::call) { // at $DIR/inline_cycle.rs:14:5: 14:24
-+ scope 2 (inlined <A<C> as Call>::call) { // at $DIR/inline_cycle.rs:43:9: 43:23
-+ scope 3 (inlined <B<C> as Call>::call) { // at $DIR/inline_cycle.rs:28:9: 28:31
-+ }
-+ }
+ }
bb0: {
StorageLive(_1); // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:24
- _1 = <C as Call>::call() -> bb1; // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:24
-+ _1 = <C as Call>::call() -> bb1; // scope 3 at $DIR/inline_cycle.rs:36:9: 36:28
++ _1 = <A<C> as Call>::call() -> bb1; // scope 1 at $DIR/inline_cycle.rs:43:9: 43:23
// mir::Constant
- // + span: $DIR/inline_cycle.rs:14:5: 14:22
-+ // + span: $DIR/inline_cycle.rs:36:9: 36:26
- // + literal: Const { ty: fn() {<C as Call>::call}, val: Value(<ZST>) }
+- // + literal: Const { ty: fn() {<C as Call>::call}, val: Value(<ZST>) }
++ // + span: $DIR/inline_cycle.rs:43:9: 43:21
++ // + literal: Const { ty: fn() {<A<C> as Call>::call}, val: Value(<ZST>) }
}
bb1: {
+ debug f => _2; // in scope 1 at $DIR/inline_cycle.rs:53:22: 53:23
+ let _3: (); // in scope 1 at $DIR/inline_cycle.rs:54:5: 54:8
+ let mut _4: (); // in scope 1 at $DIR/inline_cycle.rs:54:5: 54:8
-+ scope 2 (inlined <fn() {f} as FnOnce<()>>::call_once - shim(fn() {f})) { // at $DIR/inline_cycle.rs:54:5: 54:8
-+ scope 3 (inlined f) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL
-+ let _5: (); // in scope 3 at $DIR/inline_cycle.rs:59:5: 59:12
-+ }
-+ }
+ }
bb0: {
+ _2 = f; // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:12
// mir::Constant
- // + span: $DIR/inline_cycle.rs:49:5: 49:9
-+ // + span: $DIR/inline_cycle.rs:49:10: 49:11
-+ // + literal: Const { ty: fn() {f}, val: Value(<ZST>) }
+- // + literal: Const { ty: fn(fn() {f}) {call::<fn() {f}>}, val: Value(<ZST>) }
+- // mir::Constant
+ // + span: $DIR/inline_cycle.rs:49:10: 49:11
+ // + literal: Const { ty: fn() {f}, val: Value(<ZST>) }
+ StorageLive(_3); // scope 1 at $DIR/inline_cycle.rs:54:5: 54:8
+ StorageLive(_4); // scope 1 at $DIR/inline_cycle.rs:54:5: 54:8
-+ StorageLive(_5); // scope 3 at $DIR/inline_cycle.rs:59:5: 59:12
-+ _5 = call::<fn() {f}>(f) -> bb1; // scope 3 at $DIR/inline_cycle.rs:59:5: 59:12
++ _3 = <fn() {f} as FnOnce<()>>::call_once(move _2, move _4) -> bb1; // scope 1 at $DIR/inline_cycle.rs:54:5: 54:8
+ // mir::Constant
-+ // + span: $DIR/inline_cycle.rs:59:5: 59:9
- // + literal: Const { ty: fn(fn() {f}) {call::<fn() {f}>}, val: Value(<ZST>) }
- // mir::Constant
-- // + span: $DIR/inline_cycle.rs:49:10: 49:11
-+ // + span: $DIR/inline_cycle.rs:59:10: 59:11
- // + literal: Const { ty: fn() {f}, val: Value(<ZST>) }
++ // + span: $DIR/inline_cycle.rs:54:5: 54:6
++ // + literal: Const { ty: extern "rust-call" fn(fn() {f}, ()) -> <fn() {f} as FnOnce<()>>::Output {<fn() {f} as FnOnce<()>>::call_once}, val: Value(<ZST>) }
}
bb1: {
-+ StorageDead(_5); // scope 3 at $DIR/inline_cycle.rs:59:12: 59:13
+ StorageDead(_4); // scope 1 at $DIR/inline_cycle.rs:54:7: 54:8
+ StorageDead(_3); // scope 1 at $DIR/inline_cycle.rs:54:8: 54:9
+ StorageDead(_2); // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:12
let _1: (); // in scope 0 at $DIR/inline_cycle_generic.rs:+1:5: +1:24
+ scope 1 (inlined <C as Call>::call) { // at $DIR/inline_cycle_generic.rs:9:5: 9:24
+ scope 2 (inlined <B<A> as Call>::call) { // at $DIR/inline_cycle_generic.rs:38:9: 38:31
-+ scope 3 (inlined <A as Call>::call) { // at $DIR/inline_cycle_generic.rs:31:9: 31:28
-+ scope 4 (inlined <B<C> as Call>::call) { // at $DIR/inline_cycle_generic.rs:23:9: 23:31
-+ }
-+ }
+ }
+ }
bb0: {
StorageLive(_1); // scope 0 at $DIR/inline_cycle_generic.rs:+1:5: +1:24
- _1 = <C as Call>::call() -> bb1; // scope 0 at $DIR/inline_cycle_generic.rs:+1:5: +1:24
-+ _1 = <C as Call>::call() -> bb1; // scope 4 at $DIR/inline_cycle_generic.rs:31:9: 31:28
++ _1 = <A as Call>::call() -> bb1; // scope 2 at $DIR/inline_cycle_generic.rs:31:9: 31:28
// mir::Constant
- // + span: $DIR/inline_cycle_generic.rs:9:5: 9:22
+- // + literal: Const { ty: fn() {<C as Call>::call}, val: Value(<ZST>) }
+ // + span: $DIR/inline_cycle_generic.rs:31:9: 31:26
- // + literal: Const { ty: fn() {<C as Call>::call}, val: Value(<ZST>) }
++ // + literal: Const { ty: fn() {<A as Call>::call}, val: Value(<ZST>) }
}
bb1: {
_3 = _1; // scope 0 at $DIR/inline_diverging.rs:+1:8: +1:9
_2 = Gt(move _3, const 0_i32); // scope 0 at $DIR/inline_diverging.rs:+1:8: +1:13
StorageDead(_3); // scope 0 at $DIR/inline_diverging.rs:+1:12: +1:13
- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/inline_diverging.rs:+1:8: +1:13
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/inline_diverging.rs:+1:8: +1:13
}
bb1: {
+ scope 3 {
+ debug b => _9; // in scope 3 at $DIR/inline_diverging.rs:28:9: 28:10
+ }
-+ scope 6 (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) { // at $DIR/inline_diverging.rs:28:13: 28:16
-+ scope 7 (inlined sleep) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL
-+ }
-+ }
-+ }
-+ scope 4 (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) { // at $DIR/inline_diverging.rs:27:13: 27:16
-+ scope 5 (inlined sleep) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL
-+ }
+ }
+ }
+ StorageLive(_4); // scope 1 at $DIR/inline_diverging.rs:27:13: 27:14
+ _4 = &_2; // scope 1 at $DIR/inline_diverging.rs:27:13: 27:14
+ StorageLive(_5); // scope 1 at $DIR/inline_diverging.rs:27:13: 27:16
-+ goto -> bb1; // scope 5 at $DIR/inline_diverging.rs:39:5: 39:12
++ _3 = <fn() -> ! {sleep} as Fn<()>>::call(move _4, move _5) -> [return: bb1, unwind: bb5]; // scope 1 at $DIR/inline_diverging.rs:27:13: 27:16
++ // mir::Constant
++ // + span: $DIR/inline_diverging.rs:27:13: 27:14
++ // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a fn() -> ! {sleep}, ()) -> <fn() -> ! {sleep} as FnOnce<()>>::Output {<fn() -> ! {sleep} as Fn<()>>::call}, val: Value(<ZST>) }
+ }
+
+ bb1: {
-+ goto -> bb1; // scope 5 at $DIR/inline_diverging.rs:39:5: 39:12
++ StorageDead(_5); // scope 1 at $DIR/inline_diverging.rs:27:15: 27:16
++ StorageDead(_4); // scope 1 at $DIR/inline_diverging.rs:27:15: 27:16
++ StorageLive(_6); // scope 2 at $DIR/inline_diverging.rs:28:13: 28:14
++ _6 = &_2; // scope 2 at $DIR/inline_diverging.rs:28:13: 28:14
++ StorageLive(_7); // scope 2 at $DIR/inline_diverging.rs:28:13: 28:16
++ _9 = <fn() -> ! {sleep} as Fn<()>>::call(move _6, move _7) -> [return: bb2, unwind: bb4]; // scope 2 at $DIR/inline_diverging.rs:28:13: 28:16
++ // mir::Constant
++ // + span: $DIR/inline_diverging.rs:28:13: 28:14
++ // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a fn() -> ! {sleep}, ()) -> <fn() -> ! {sleep} as FnOnce<()>>::Output {<fn() -> ! {sleep} as Fn<()>>::call}, val: Value(<ZST>) }
++ }
++
++ bb2: {
++ StorageDead(_7); // scope 2 at $DIR/inline_diverging.rs:28:15: 28:16
++ StorageDead(_6); // scope 2 at $DIR/inline_diverging.rs:28:15: 28:16
++ StorageLive(_8); // scope 3 at $DIR/inline_diverging.rs:29:6: 29:7
++ _8 = move _3; // scope 3 at $DIR/inline_diverging.rs:29:6: 29:7
++ Deinit(_1); // scope 3 at $DIR/inline_diverging.rs:29:5: 29:11
++ (_1.0: !) = move _8; // scope 3 at $DIR/inline_diverging.rs:29:5: 29:11
++ (_1.1: !) = move _9; // scope 3 at $DIR/inline_diverging.rs:29:5: 29:11
++ StorageDead(_8); // scope 3 at $DIR/inline_diverging.rs:29:10: 29:11
++ StorageDead(_3); // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2
++ drop(_2) -> bb3; // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2
++ }
++
++ bb3: {
++ unreachable; // scope 0 at $DIR/inline_diverging.rs:30:2: 30:2
++ }
++
++ bb4 (cleanup): {
++ drop(_3) -> bb5; // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2
++ }
++
++ bb5 (cleanup): {
++ drop(_2) -> bb6; // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2
++ }
++
++ bb6 (cleanup): {
++ resume; // scope 1 at $DIR/inline_diverging.rs:26:1: 30:2
}
}
+ _7 = const false; // scope 0 at $DIR/inline_generator.rs:+1:14: +1:46
+ _10 = deref_copy (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]); // scope 6 at $DIR/inline_generator.rs:15:5: 15:41
+ _9 = discriminant((*_10)); // scope 6 at $DIR/inline_generator.rs:15:5: 15:41
-+ switchInt(move _9) -> [0_u32: bb3, 1_u32: bb8, 3_u32: bb7, otherwise: bb9]; // scope 6 at $DIR/inline_generator.rs:15:5: 15:41
++ switchInt(move _9) -> [0: bb3, 1: bb8, 3: bb7, otherwise: bb9]; // scope 6 at $DIR/inline_generator.rs:15:5: 15:41
}
- bb3: {
+
+ bb3: {
+ StorageLive(_8); // scope 6 at $DIR/inline_generator.rs:15:17: 15:39
-+ switchInt(move _7) -> [false: bb5, otherwise: bb4]; // scope 6 at $DIR/inline_generator.rs:15:20: 15:21
++ switchInt(move _7) -> [0: bb5, otherwise: bb4]; // scope 6 at $DIR/inline_generator.rs:15:20: 15:21
+ }
+
+ bb4: {
// + literal: Const { ty: &i32, val: Unevaluated(bar, [], Some(promoted[1])) }
Retag(_10); // scope 1 at $DIR/inline_retag.rs:+2:7: +2:9
_4 = &(*_10); // scope 1 at $DIR/inline_retag.rs:+2:7: +2:9
- Retag(_4); // scope 1 at $DIR/inline_retag.rs:+2:7: +2:9
_3 = &(*_4); // scope 1 at $DIR/inline_retag.rs:+2:7: +2:9
- Retag(_3); // scope 1 at $DIR/inline_retag.rs:+2:7: +2:9
StorageLive(_6); // scope 1 at $DIR/inline_retag.rs:+2:11: +2:14
StorageLive(_7); // scope 1 at $DIR/inline_retag.rs:+2:11: +2:14
_9 = const _; // scope 1 at $DIR/inline_retag.rs:+2:11: +2:14
// + literal: Const { ty: &i32, val: Unevaluated(bar, [], Some(promoted[0])) }
Retag(_9); // scope 1 at $DIR/inline_retag.rs:+2:11: +2:14
_7 = &(*_9); // scope 1 at $DIR/inline_retag.rs:+2:11: +2:14
- Retag(_7); // scope 1 at $DIR/inline_retag.rs:+2:11: +2:14
_6 = &(*_7); // scope 1 at $DIR/inline_retag.rs:+2:11: +2:14
- Retag(_6); // scope 1 at $DIR/inline_retag.rs:+2:11: +2:14
Retag(_3); // scope 2 at $DIR/inline_retag.rs:16:8: 16:9
Retag(_6); // scope 2 at $DIR/inline_retag.rs:16:17: 16:18
StorageLive(_11); // scope 2 at $DIR/inline_retag.rs:17:5: 17:7
+ StorageLive(_6); // scope 2 at $DIR/inline_shims.rs:+2:14: +2:40
+ StorageLive(_7); // scope 2 at $DIR/inline_shims.rs:+2:14: +2:40
+ _6 = discriminant((*_5)); // scope 3 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
-+ switchInt(move _6) -> [0_isize: bb2, otherwise: bb3]; // scope 3 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
++ switchInt(move _6) -> [0: bb2, otherwise: bb3]; // scope 3 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
}
bb2: {
}
bb3: {
- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at /the/src/instrument_coverage.rs:+2:12: +2:17
+ switchInt(move _2) -> [0: bb5, otherwise: bb4]; // scope 0 at /the/src/instrument_coverage.rs:+2:12: +2:17
}
bb4: {
StorageLive(_3); // scope 1 at $DIR/issue_38669.rs:+3:9: +5:10
StorageLive(_4); // scope 1 at $DIR/issue_38669.rs:+3:12: +3:24
_4 = _1; // scope 1 at $DIR/issue_38669.rs:+3:12: +3:24
- switchInt(move _4) -> [false: bb4, otherwise: bb3]; // scope 1 at $DIR/issue_38669.rs:+3:12: +3:24
+ switchInt(move _4) -> [0: bb4, otherwise: bb3]; // scope 1 at $DIR/issue_38669.rs:+3:12: +3:24
}
bb3: {
}
bb8 (cleanup): {
- switchInt(_5) -> [false: bb6, otherwise: bb7]; // scope 0 at $DIR/issue_41110.rs:+1:27: +1:28
+ switchInt(_5) -> [0: bb6, otherwise: bb7]; // scope 0 at $DIR/issue_41110.rs:+1:27: +1:28
}
}
}
bb14 (cleanup): {
- switchInt(_6) -> [false: bb10, otherwise: bb13]; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2
+ switchInt(_6) -> [0: bb10, otherwise: bb13]; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2
}
}
}
bb1: {
- switchInt(move _2) -> [false: bb7, otherwise: bb2]; // scope 1 at $DIR/issue_41888.rs:+2:8: +2:14
+ switchInt(move _2) -> [0: bb7, otherwise: bb2]; // scope 1 at $DIR/issue_41888.rs:+2:8: +2:14
}
bb2: {
bb4: {
StorageDead(_3); // scope 1 at $DIR/issue_41888.rs:+3:19: +3:20
_5 = discriminant(_1); // scope 2 at $DIR/issue_41888.rs:+4:16: +4:24
- switchInt(move _5) -> [0_isize: bb5, otherwise: bb6]; // scope 2 at $DIR/issue_41888.rs:+4:16: +4:24
+ switchInt(move _5) -> [0: bb5, otherwise: bb6]; // scope 2 at $DIR/issue_41888.rs:+4:16: +4:24
}
bb5: {
bb19: {
_10 = discriminant(_1); // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2
- switchInt(move _10) -> [0_isize: bb15, otherwise: bb17]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2
+ switchInt(move _10) -> [0: bb15, otherwise: bb17]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2
}
bb20: {
- switchInt(_7) -> [false: bb15, otherwise: bb19]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2
+ switchInt(_7) -> [0: bb15, otherwise: bb19]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2
}
bb21 (cleanup): {
_11 = discriminant(_1); // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2
- switchInt(move _11) -> [0_isize: bb16, otherwise: bb18]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2
+ switchInt(move _11) -> [0: bb16, otherwise: bb18]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2
}
bb22 (cleanup): {
- switchInt(_7) -> [false: bb12, otherwise: bb21]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2
+ switchInt(_7) -> [0: bb12, otherwise: bb21]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2
}
}
bb2: {
StorageDead(_7); // scope 0 at $DIR/issue_62289.rs:+1:19: +1:20
_8 = discriminant(_6); // scope 0 at $DIR/issue_62289.rs:+1:15: +1:20
- switchInt(move _8) -> [0_isize: bb3, 1_isize: bb5, otherwise: bb4]; // scope 0 at $DIR/issue_62289.rs:+1:15: +1:20
+ switchInt(move _8) -> [0: bb3, 1: bb5, otherwise: bb4]; // scope 0 at $DIR/issue_62289.rs:+1:15: +1:20
}
bb3: {
StorageDead(_17); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_15 = Not(move _16); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageDead(_16); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- switchInt(move _15) -> [false: bb5, otherwise: bb4]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ switchInt(move _15) -> [0: bb5, otherwise: bb4]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
}
bb4: {
StorageDead(_12); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_10 = Not(move _11); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageDead(_11); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- switchInt(move _10) -> [false: bb4, otherwise: bb3]; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ switchInt(move _10) -> [0: bb4, otherwise: bb3]; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
}
bb3: {
StorageDead(_33); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_31 = Not(move _32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageDead(_32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- switchInt(move _31) -> [false: bb13, otherwise: bb12]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ switchInt(move _31) -> [0: bb13, otherwise: bb12]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
}
bb12: {
bb2: {
_7 = discriminant(_2); // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL
- switchInt(move _7) -> [0_isize: bb6, 1_isize: bb8, otherwise: bb7]; // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL
+ switchInt(move _7) -> [0: bb6, 1: bb8, otherwise: bb7]; // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL
}
bb3: {
StorageDead(_4); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL
StorageDead(_5); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL
StorageDead(_3); // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23
- switchInt(move _9) -> [1_isize: bb1, otherwise: bb3]; // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23
+ switchInt(move _9) -> [1: bb1, otherwise: bb3]; // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23
}
bb6: {
bb1: {
StorageDead(_3); // scope 2 at $DIR/issue_75439.rs:+2:52: +2:53
- switchInt(_2[0 of 4]) -> [0_u32: bb2, otherwise: bb8]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30
+ switchInt(_2[0 of 4]) -> [0: bb2, otherwise: bb8]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30
}
bb2: {
- switchInt(_2[1 of 4]) -> [0_u32: bb3, otherwise: bb8]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30
+ switchInt(_2[1 of 4]) -> [0: bb3, otherwise: bb8]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30
}
bb3: {
- switchInt(_2[2 of 4]) -> [0_u32: bb5, 4294901760_u32: bb6, otherwise: bb8]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30
+ switchInt(_2[2 of 4]) -> [0: bb5, 4294901760: bb6, otherwise: bb8]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30
}
bb4: {
StorageLive(_1); // scope 0 at $DIR/loop_test.rs:+4:5: +6:6
StorageLive(_2); // scope 0 at $DIR/loop_test.rs:+4:8: +4:12
_2 = const true; // scope 0 at $DIR/loop_test.rs:+4:8: +4:12
- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/loop_test.rs:+4:8: +4:12
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/loop_test.rs:+4:8: +4:12
}
bb1: {
_3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
StorageDead(_5); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- switchInt(move _3) -> [false: bb4, otherwise: bb2]; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
+ switchInt(move _3) -> [0: bb4, otherwise: bb2]; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
}
bb2: {
_3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
StorageDead(_5); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:26: +1:27
- switchInt(move _3) -> [false: bb4, otherwise: bb2]; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
+ switchInt(move _3) -> [0: bb4, otherwise: bb2]; // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
}
bb2: {
_3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
StorageDead(_5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
StorageDead(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
- switchInt(move _3) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
+ switchInt(move _3) -> [0: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
}
bb1: {
_3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
StorageDead(_5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
StorageDead(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
- switchInt(move _3) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
+ switchInt(move _3) -> [0: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
}
bb1: {
_3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_slice_len.rs:+1:8: +1:27
StorageDead(_5); // scope 0 at $DIR/lower_slice_len.rs:+1:26: +1:27
StorageDead(_4); // scope 0 at $DIR/lower_slice_len.rs:+1:26: +1:27
- switchInt(move _3) -> [false: bb4, otherwise: bb2]; // scope 0 at $DIR/lower_slice_len.rs:+1:8: +1:27
+ switchInt(move _3) -> [0: bb4, otherwise: bb2]; // scope 0 at $DIR/lower_slice_len.rs:+1:8: +1:27
}
bb2: {
bb0: {
- FakeRead(ForMatchedPlace(None), _2); // scope 0 at $DIR/match_arm_scopes.rs:+1:11: +1:16
-- switchInt((_2.0: bool)) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
-+ switchInt((_2.0: bool)) -> [false: bb5, otherwise: bb1]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
+- switchInt((_2.0: bool)) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
++ switchInt((_2.0: bool)) -> [0: bb5, otherwise: bb1]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
}
bb1: {
- falseEdge -> [real: bb8, imaginary: bb3]; // scope 0 at $DIR/match_arm_scopes.rs:+2:9: +2:22
-+ switchInt((_2.1: bool)) -> [false: bb10, otherwise: bb2]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
++ switchInt((_2.1: bool)) -> [0: bb10, otherwise: bb2]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
}
bb2: {
-- switchInt((_2.1: bool)) -> [false: bb3, otherwise: bb4]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
-+ switchInt((_2.0: bool)) -> [false: bb3, otherwise: bb17]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
+- switchInt((_2.1: bool)) -> [0: bb3, otherwise: bb4]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
++ switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb17]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
}
bb3: {
- }
-
- bb4: {
-- switchInt((_2.0: bool)) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
+- switchInt((_2.0: bool)) -> [0: bb6, otherwise: bb5]; // scope 0 at $DIR/match_arm_scopes.rs:+1:5: +1:16
- }
-
- bb5: {
StorageLive(_9); // scope 0 at $DIR/match_arm_scopes.rs:+2:42: +2:73
StorageLive(_10); // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
_10 = _1; // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
-- switchInt(move _10) -> [false: bb10, otherwise: bb9]; // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
-+ switchInt(move _10) -> [false: bb7, otherwise: bb6]; // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
+- switchInt(move _10) -> [0: bb10, otherwise: bb9]; // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
++ switchInt(move _10) -> [0: bb7, otherwise: bb6]; // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
}
- bb9: {
- bb10: {
+ bb7: {
_9 = (*_6); // scope 0 at $DIR/match_arm_scopes.rs:+2:70: +2:71
-- switchInt(move _9) -> [false: bb12, otherwise: bb11]; // scope 0 at $DIR/match_arm_scopes.rs:+2:42: +2:73
-+ switchInt(move _9) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match_arm_scopes.rs:+2:42: +2:73
+- switchInt(move _9) -> [0: bb12, otherwise: bb11]; // scope 0 at $DIR/match_arm_scopes.rs:+2:42: +2:73
++ switchInt(move _9) -> [0: bb9, otherwise: bb8]; // scope 0 at $DIR/match_arm_scopes.rs:+2:42: +2:73
}
- bb11: {
StorageLive(_12); // scope 0 at $DIR/match_arm_scopes.rs:+2:42: +2:73
StorageLive(_13); // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
_13 = _1; // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
-- switchInt(move _13) -> [false: bb15, otherwise: bb14]; // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
-+ switchInt(move _13) -> [false: bb12, otherwise: bb11]; // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
+- switchInt(move _13) -> [0: bb15, otherwise: bb14]; // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
++ switchInt(move _13) -> [0: bb12, otherwise: bb11]; // scope 0 at $DIR/match_arm_scopes.rs:+2:45: +2:49
}
- bb14: {
- bb15: {
+ bb12: {
_12 = (*_6); // scope 0 at $DIR/match_arm_scopes.rs:+2:70: +2:71
-- switchInt(move _12) -> [false: bb17, otherwise: bb16]; // scope 0 at $DIR/match_arm_scopes.rs:+2:42: +2:73
-+ switchInt(move _12) -> [false: bb14, otherwise: bb13]; // scope 0 at $DIR/match_arm_scopes.rs:+2:42: +2:73
+- switchInt(move _12) -> [0: bb17, otherwise: bb16]; // scope 0 at $DIR/match_arm_scopes.rs:+2:42: +2:73
++ switchInt(move _12) -> [0: bb14, otherwise: bb13]; // scope 0 at $DIR/match_arm_scopes.rs:+2:42: +2:73
}
- bb16: {
StorageLive(_3); // scope 2 at $DIR/match_test.rs:+6:5: +11:6
FakeRead(ForMatchedPlace(None), _1); // scope 2 at $DIR/match_test.rs:+6:11: +6:12
_6 = Le(const 0_i32, _1); // scope 2 at $DIR/match_test.rs:+7:9: +7:14
- switchInt(move _6) -> [false: bb4, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:+7:9: +7:14
+ switchInt(move _6) -> [0: bb4, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:+7:9: +7:14
}
bb1: {
_7 = Lt(_1, const 10_i32); // scope 2 at $DIR/match_test.rs:+7:9: +7:14
- switchInt(move _7) -> [false: bb4, otherwise: bb2]; // scope 2 at $DIR/match_test.rs:+7:9: +7:14
+ switchInt(move _7) -> [0: bb4, otherwise: bb2]; // scope 2 at $DIR/match_test.rs:+7:9: +7:14
}
bb2: {
bb4: {
_4 = Le(const 10_i32, _1); // scope 2 at $DIR/match_test.rs:+8:9: +8:16
- switchInt(move _4) -> [false: bb7, otherwise: bb5]; // scope 2 at $DIR/match_test.rs:+8:9: +8:16
+ switchInt(move _4) -> [0: bb7, otherwise: bb5]; // scope 2 at $DIR/match_test.rs:+8:9: +8:16
}
bb5: {
_5 = Le(_1, const 20_i32); // scope 2 at $DIR/match_test.rs:+8:9: +8:16
- switchInt(move _5) -> [false: bb7, otherwise: bb6]; // scope 2 at $DIR/match_test.rs:+8:9: +8:16
+ switchInt(move _5) -> [0: bb7, otherwise: bb6]; // scope 2 at $DIR/match_test.rs:+8:9: +8:16
}
bb6: {
}
bb7: {
- switchInt(_1) -> [-1_i32: bb8, otherwise: bb3]; // scope 2 at $DIR/match_test.rs:+6:5: +6:12
+ switchInt(_1) -> [4294967295: bb8, otherwise: bb3]; // scope 2 at $DIR/match_test.rs:+6:5: +6:12
}
bb8: {
_8 = &shallow _1; // scope 2 at $DIR/match_test.rs:+6:11: +6:12
StorageLive(_9); // scope 2 at $DIR/match_test.rs:+7:18: +7:19
_9 = _2; // scope 2 at $DIR/match_test.rs:+7:18: +7:19
- switchInt(move _9) -> [false: bb11, otherwise: bb10]; // scope 2 at $DIR/match_test.rs:+7:18: +7:19
+ switchInt(move _9) -> [0: bb11, otherwise: bb10]; // scope 2 at $DIR/match_test.rs:+7:18: +7:19
}
bb10: {
StorageLive(_4); // scope 2 at $DIR/matches_reduce_branches.rs:+3:9: +3:10
StorageLive(_5); // scope 3 at $DIR/matches_reduce_branches.rs:+4:9: +4:10
StorageLive(_6); // scope 4 at $DIR/matches_reduce_branches.rs:+6:5: +21:6
-- switchInt(_1) -> [7_i32: bb2, otherwise: bb1]; // scope 4 at $DIR/matches_reduce_branches.rs:+6:5: +6:12
+- switchInt(_1) -> [7: bb2, otherwise: bb1]; // scope 4 at $DIR/matches_reduce_branches.rs:+6:5: +6:12
- }
-
- bb1: {
bb0: {
StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_3 = discriminant(_1); // scope 0 at $DIR/matches_reduce_branches.rs:+1:17: +1:20
-- switchInt(move _3) -> [0_isize: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+- switchInt(move _3) -> [0: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ StorageLive(_4); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ _4 = move _3; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ _2 = Eq(_4, const 0_isize); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ StorageDead(_4); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-+ switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
++ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
}
bb1: {
- }
-
- bb3: {
-- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+- switchInt(move _2) -> [0: bb5, otherwise: bb4]; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
-
- bb4: {
StorageLive(_5); // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
StorageLive(_6); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
_6 = const true; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
-- switchInt(move _6) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
+- switchInt(move _6) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
- }
-
- bb1: {
+ _5 = Ne(_7, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+2:45: +2:50
+ StorageDead(_7); // scope 0 at $DIR/matches_reduce_branches.rs:+2:24: +2:28
StorageDead(_6); // scope 0 at $DIR/matches_reduce_branches.rs:+2:51: +2:52
-- switchInt(move _5) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
+- switchInt(move _5) -> [0: bb5, otherwise: bb4]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
- }
-
- bb4: {
+ _4 = Ne(_8, const false); // scope 0 at $DIR/matches_reduce_branches.rs:+2:69: +2:74
+ StorageDead(_8); // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
StorageDead(_5); // scope 0 at $DIR/matches_reduce_branches.rs:+2:75: +2:76
-- switchInt(move _4) -> [false: bb8, otherwise: bb7]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
+- switchInt(move _4) -> [0: bb8, otherwise: bb7]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
- }
-
- bb7: {
- }
-
- bb9: {
-- switchInt(move _3) -> [false: bb11, otherwise: bb10]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
+- switchInt(move _3) -> [0: bb11, otherwise: bb10]; // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
- }
-
- bb10: {
bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/matches_u8.rs:+1:11: +1:12
- switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/matches_u8.rs:+1:5: +1:12
+ switchInt(move _2) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 0 at $DIR/matches_u8.rs:+1:5: +1:12
}
bb1: {
bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/matches_u8.rs:+1:11: +1:12
- switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/matches_u8.rs:+1:5: +1:12
+ switchInt(move _2) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 0 at $DIR/matches_u8.rs:+1:5: +1:12
}
bb1: {
FakeRead(ForLet(None), _6); // bb1[4]: scope 2 at $DIR/region_subtyping_basic.rs:+3:9: +3:10
StorageLive(_7); // bb1[5]: scope 3 at $DIR/region_subtyping_basic.rs:+4:8: +4:12
_7 = const ConstValue(Scalar(0x01): bool); // bb1[6]: scope 3 at $DIR/region_subtyping_basic.rs:+4:8: +4:12
- switchInt(move _7) -> [ConstValue(Scalar(0x00): bool): bb4, otherwise: bb2]; // bb1[7]: scope 3 at $DIR/region_subtyping_basic.rs:+4:8: +4:12
+ switchInt(move _7) -> [0: bb4, otherwise: bb2]; // bb1[7]: scope 3 at $DIR/region_subtyping_basic.rs:+4:8: +4:12
}
bb2: {
FakeRead(ForLet(None), _6); // bb1[4]: scope 2 at $DIR/region_subtyping_basic.rs:+3:9: +3:10
StorageLive(_7); // bb1[5]: scope 3 at $DIR/region_subtyping_basic.rs:+4:8: +4:12
_7 = const ConstValue(Scalar(0x01): bool); // bb1[6]: scope 3 at $DIR/region_subtyping_basic.rs:+4:8: +4:12
- switchInt(move _7) -> [ConstValue(Scalar(0x00): bool): bb4, otherwise: bb2]; // bb1[7]: scope 3 at $DIR/region_subtyping_basic.rs:+4:8: +4:12
+ switchInt(move _7) -> [0: bb4, otherwise: bb2]; // bb1[7]: scope 3 at $DIR/region_subtyping_basic.rs:+4:8: +4:12
}
bb2: {
bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/no_drop_for_inactive_variant.rs:+1:11: +1:14
- switchInt(move _2) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/no_drop_for_inactive_variant.rs:+1:5: +1:14
+ switchInt(move _2) -> [0: bb1, 1: bb3, otherwise: bb2]; // scope 0 at $DIR/no_drop_for_inactive_variant.rs:+1:5: +1:14
}
bb1: {
- _2 = Ne(move _3, const false); // scope 0 at $DIR/not_equal_false.rs:+1:8: +1:18
+ _2 = move _3; // scope 0 at $DIR/not_equal_false.rs:+1:8: +1:18
StorageDead(_3); // scope 0 at $DIR/not_equal_false.rs:+1:17: +1:18
- switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/not_equal_false.rs:+1:8: +1:18
+ switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/not_equal_false.rs:+1:8: +1:18
}
bb1: {
- FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
_3 = discriminant(_1); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
- switchInt(move _3) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:+1:5: +1:12
+ switchInt(move _3) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:+1:5: +1:12
}
bb1: {
}
bb2: {
- switchInt((*(*((_1 as Some).0: &&i32)))) -> [0_i32: bb3, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:+1:5: +1:12
+ switchInt((*(*((_1 as Some).0: &&i32)))) -> [0: bb3, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:+1:5: +1:12
}
bb3: {
+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
StorageLive(_8); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
_8 = _2; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
- switchInt(move _8) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+ switchInt(move _8) -> [0: bb6, otherwise: bb5]; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
}
bb5: {
bb3: {
- StorageDead(_8); // scope 2 at $DIR/remove_storage_markers.rs:+2:18: +2:19
_10 = discriminant(_7); // scope 2 at $DIR/remove_storage_markers.rs:+2:14: +2:19
- switchInt(move _10) -> [0_isize: bb6, 1_isize: bb4, otherwise: bb5]; // scope 2 at $DIR/remove_storage_markers.rs:+2:14: +2:19
+ switchInt(move _10) -> [0: bb6, 1: bb4, otherwise: bb5]; // scope 2 at $DIR/remove_storage_markers.rs:+2:14: +2:19
}
bb4: {
--- /dev/null
+// MIR for `get_union` after PreCodegen
+
+fn get_union() -> Foo {
+ let mut _0: Foo; // return place in scope 0 at $DIR/remove_zsts.rs:+0:19: +0:22
+
+ bb0: {
+ Deinit(_0); // scope 0 at $DIR/remove_zsts.rs:+1:5: +1:18
+ return; // scope 0 at $DIR/remove_zsts.rs:+2:2: +2:2
+ }
+}
--- /dev/null
+- // MIR for `get_union` before RemoveZsts
++ // MIR for `get_union` after RemoveZsts
+
+ fn get_union() -> Foo {
+ let mut _0: Foo; // return place in scope 0 at $DIR/remove_zsts.rs:+0:19: +0:22
+ let mut _1: (); // in scope 0 at $DIR/remove_zsts.rs:+1:14: +1:16
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/remove_zsts.rs:+1:14: +1:16
+- Deinit(_1); // scope 0 at $DIR/remove_zsts.rs:+1:14: +1:16
++ nop; // scope 0 at $DIR/remove_zsts.rs:+1:14: +1:16
+ Deinit(_0); // scope 0 at $DIR/remove_zsts.rs:+1:5: +1:18
+- (_0.0: ()) = move _1; // scope 0 at $DIR/remove_zsts.rs:+1:5: +1:18
++ nop; // scope 0 at $DIR/remove_zsts.rs:+1:5: +1:18
+ StorageDead(_1); // scope 0 at $DIR/remove_zsts.rs:+1:17: +1:18
+ return; // scope 0 at $DIR/remove_zsts.rs:+2:2: +2:2
+ }
+ }
+
--- /dev/null
+union Foo {
+ x: (),
+ y: u64,
+}
+
+// EMIT_MIR remove_zsts.get_union.RemoveZsts.diff
+// EMIT_MIR remove_zsts.get_union.PreCodegen.after.mir
+fn get_union() -> Foo {
+ Foo { x: () }
+}
+
+fn main() {
+ get_union();
+}
+++ /dev/null
-// MIR for `get_union` after RemoveZsts
-
-fn get_union() -> Foo {
- let mut _0: Foo; // return place in scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:+0:19: +0:22
- let mut _1: (); // in scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:+1:14: +1:16
-
- bb0: {
- StorageLive(_1); // scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:+1:14: +1:16
- nop; // scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:+1:14: +1:16
- Deinit(_0); // scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:+1:5: +1:18
- (_0.0: ()) = move _1; // scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:+1:5: +1:18
- StorageDead(_1); // scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:+1:17: +1:18
- return; // scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:+2:2: +2:2
- }
-}
+++ /dev/null
-// unit-test: RemoveZsts
-
-// Ensure RemoveZsts doesn't remove ZST assignments to union fields,
-// which causes problems in Miri.
-
-union Foo {
- x: (),
- y: u64,
-}
-
-// EMIT_MIR remove_zsts_dont_touch_unions.get_union.RemoveZsts.after.mir
-fn get_union() -> Foo {
- Foo { x: () }
-}
-
-
-fn main() {
- get_union();
-}
StorageLive(_3); // scope 1 at $DIR/retag.rs:+2:13: +2:19
StorageLive(_4); // scope 1 at $DIR/retag.rs:+2:13: +2:19
_4 = &mut _1; // scope 1 at $DIR/retag.rs:+2:13: +2:19
- Retag(_4); // scope 1 at $DIR/retag.rs:+2:13: +2:19
_3 = &raw mut (*_4); // scope 1 at $DIR/retag.rs:+2:13: +2:19
- Retag([raw] _3); // scope 1 at $DIR/retag.rs:+2:13: +2:19
_2 = move _3 as *mut usize (Pointer(ArrayToPointer)); // scope 1 at $DIR/retag.rs:+2:13: +2:33
StorageDead(_3); // scope 1 at $DIR/retag.rs:+2:32: +2:33
StorageDead(_4); // scope 1 at $DIR/retag.rs:+2:33: +2:34
StorageLive(_10); // scope 4 at $DIR/retag.rs:+6:13: +6:15
StorageLive(_11); // scope 4 at $DIR/retag.rs:+6:13: +6:15
_11 = &_8; // scope 4 at $DIR/retag.rs:+6:13: +6:15
- Retag(_11); // scope 4 at $DIR/retag.rs:+6:13: +6:15
_10 = &raw const (*_11); // scope 4 at $DIR/retag.rs:+6:13: +6:15
- Retag([raw] _10); // scope 4 at $DIR/retag.rs:+6:13: +6:15
_9 = move _10 as *const usize (Pointer(ArrayToPointer)); // scope 4 at $DIR/retag.rs:+6:13: +6:31
StorageDead(_10); // scope 4 at $DIR/retag.rs:+6:30: +6:31
StorageDead(_11); // scope 4 at $DIR/retag.rs:+6:31: +6:32
StorageDead(_17); // scope 6 at $DIR/retag.rs:+7:33: +7:34
_15 = (*_16); // scope 6 at $DIR/retag.rs:+7:25: +7:34
_14 = &_15; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Retag(_14); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_35 = const _; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
// mir::Constant
// + literal: Const { ty: &usize, val: Unevaluated(array_casts, [], Some(promoted[0])) }
Retag(_35); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_18 = &(*_35); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Retag(_18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
Deinit(_13); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
(_13.0: &usize) = move _14; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
(_13.1: &usize) = move _18; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageDead(_24); // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_22 = Not(move _23); // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageDead(_23); // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- switchInt(move _22) -> [false: bb4, otherwise: bb3]; // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ switchInt(move _22) -> [0: bb4, otherwise: bb3]; // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
}
bb3: {
StorageLive(_30); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_31); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_31 = &(*_20); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Retag(_31); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_30 = &(*_31); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Retag(_30); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_32); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_33); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_33 = &(*_21); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Retag(_33); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_32 = &(*_33); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Retag(_32); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_34); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
Deinit(_34); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
discriminant(_34) = 0; // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
let mut _3: (); // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
bb0: {
- Retag([raw] _1); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
_2 = &mut (*_1); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
_3 = <Test as Drop>::drop(move _2) -> bb1; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
// mir::Constant
_3 = _2; // scope 0 at $DIR/retag.rs:+1:18: +1:19
Retag(_3); // scope 0 at $DIR/retag.rs:+1:18: +1:19
_0 = &(*_2); // scope 1 at $DIR/retag.rs:+2:9: +2:10
- Retag(_0); // scope 1 at $DIR/retag.rs:+2:9: +2:10
StorageDead(_3); // scope 0 at $DIR/retag.rs:+3:5: +3:6
return; // scope 0 at $DIR/retag.rs:+3:6: +3:6
}
Deinit(_5); // scope 1 at $DIR/retag.rs:+3:17: +3:24
(_5.0: i32) = const 0_i32; // scope 1 at $DIR/retag.rs:+3:17: +3:24
_4 = &_5; // scope 1 at $DIR/retag.rs:+3:17: +3:36
- Retag(_4); // scope 1 at $DIR/retag.rs:+3:17: +3:36
StorageLive(_6); // scope 1 at $DIR/retag.rs:+3:29: +3:35
StorageLive(_7); // scope 1 at $DIR/retag.rs:+3:29: +3:35
_7 = &mut _1; // scope 1 at $DIR/retag.rs:+3:29: +3:35
- Retag(_7); // scope 1 at $DIR/retag.rs:+3:29: +3:35
_6 = &mut (*_7); // scope 1 at $DIR/retag.rs:+3:29: +3:35
- Retag([2phase] _6); // scope 1 at $DIR/retag.rs:+3:29: +3:35
_3 = Test::foo(move _4, move _6) -> [return: bb1, unwind: bb8]; // scope 1 at $DIR/retag.rs:+3:17: +3:36
// mir::Constant
// + span: $DIR/retag.rs:33:25: 33:28
_9 = move _3; // scope 2 at $DIR/retag.rs:+4:19: +4:20
Retag(_9); // scope 2 at $DIR/retag.rs:+4:19: +4:20
_8 = &mut (*_9); // scope 2 at $DIR/retag.rs:+4:19: +4:20
- Retag(_8); // scope 2 at $DIR/retag.rs:+4:19: +4:20
StorageDead(_9); // scope 2 at $DIR/retag.rs:+4:22: +4:23
StorageLive(_10); // scope 3 at $DIR/retag.rs:+5:13: +5:14
_10 = move _8; // scope 3 at $DIR/retag.rs:+5:17: +5:18
StorageLive(_11); // scope 4 at $DIR/retag.rs:+7:13: +7:15
StorageLive(_12); // scope 4 at $DIR/retag.rs:+7:18: +7:29
_12 = &raw mut (*_10); // scope 4 at $DIR/retag.rs:+7:18: +7:19
- Retag([raw] _12); // scope 4 at $DIR/retag.rs:+7:18: +7:19
_11 = _12; // scope 4 at $DIR/retag.rs:+7:18: +7:29
StorageDead(_12); // scope 4 at $DIR/retag.rs:+7:29: +7:30
_2 = const (); // scope 1 at $DIR/retag.rs:+2:5: +8:6
StorageLive(_17); // scope 6 at $DIR/retag.rs:+15:16: +15:18
StorageLive(_18); // scope 6 at $DIR/retag.rs:+15:16: +15:18
_18 = &_1; // scope 6 at $DIR/retag.rs:+15:16: +15:18
- Retag(_18); // scope 6 at $DIR/retag.rs:+15:16: +15:18
_17 = &(*_18); // scope 6 at $DIR/retag.rs:+15:16: +15:18
- Retag(_17); // scope 6 at $DIR/retag.rs:+15:16: +15:18
_15 = move _16(move _17) -> bb3; // scope 6 at $DIR/retag.rs:+15:14: +15:19
}
Deinit(_21); // scope 7 at $DIR/retag.rs:+18:5: +18:12
(_21.0: i32) = const 0_i32; // scope 7 at $DIR/retag.rs:+18:5: +18:12
_20 = &_21; // scope 7 at $DIR/retag.rs:+18:5: +18:24
- Retag(_20); // scope 7 at $DIR/retag.rs:+18:5: +18:24
StorageLive(_22); // scope 7 at $DIR/retag.rs:+18:21: +18:23
StorageLive(_23); // scope 7 at $DIR/retag.rs:+18:21: +18:23
_28 = const _; // scope 7 at $DIR/retag.rs:+18:21: +18:23
// + literal: Const { ty: &i32, val: Unevaluated(main, [], Some(promoted[0])) }
Retag(_28); // scope 7 at $DIR/retag.rs:+18:21: +18:23
_23 = &(*_28); // scope 7 at $DIR/retag.rs:+18:21: +18:23
- Retag(_23); // scope 7 at $DIR/retag.rs:+18:21: +18:23
_22 = &(*_23); // scope 7 at $DIR/retag.rs:+18:21: +18:23
- Retag(_22); // scope 7 at $DIR/retag.rs:+18:21: +18:23
_19 = Test::foo_shr(move _20, move _22) -> [return: bb4, unwind: bb7]; // scope 7 at $DIR/retag.rs:+18:5: +18:24
// mir::Constant
// + span: $DIR/retag.rs:48:13: 48:20
StorageLive(_25); // scope 7 at $DIR/retag.rs:+21:9: +21:11
StorageLive(_26); // scope 7 at $DIR/retag.rs:+21:14: +21:28
_26 = &raw const (*_15); // scope 7 at $DIR/retag.rs:+21:14: +21:16
- Retag([raw] _26); // scope 7 at $DIR/retag.rs:+21:14: +21:16
_25 = _26; // scope 7 at $DIR/retag.rs:+21:14: +21:28
StorageDead(_26); // scope 7 at $DIR/retag.rs:+21:28: +21:29
StorageLive(_27); // scope 8 at $DIR/retag.rs:+23:5: +23:18
Retag([fn entry] _2); // scope 0 at $DIR/retag.rs:+0:23: +0:24
StorageLive(_3); // scope 0 at $DIR/retag.rs:+1:9: +1:10
_3 = &mut (*_2); // scope 0 at $DIR/retag.rs:+1:9: +1:10
- Retag(_3); // scope 0 at $DIR/retag.rs:+1:9: +1:10
_0 = &mut (*_3); // scope 0 at $DIR/retag.rs:+1:9: +1:10
- Retag(_0); // scope 0 at $DIR/retag.rs:+1:9: +1:10
StorageDead(_3); // scope 0 at $DIR/retag.rs:+2:5: +2:6
return; // scope 0 at $DIR/retag.rs:+2:6: +2:6
}
let mut _18: i32; // in scope 8 at $SRC_DIR/core/src/result.rs:LL:COL
scope 9 {
debug e => _16; // in scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
- scope 10 (inlined <i32 as From<i32>>::from) { // at $SRC_DIR/core/src/result.rs:LL:COL
- debug t => _18; // in scope 10 at $SRC_DIR/core/src/convert/mod.rs:LL:COL
- }
}
}
}
StorageLive(_4); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:9
_4 = _1; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:9
_10 = discriminant(_4); // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
-- switchInt(move _10) -> [0_isize: bb7, 1_isize: bb5, otherwise: bb6]; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
-+ switchInt(move _10) -> [0_isize: bb6, 1_isize: bb4, otherwise: bb5]; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
+- switchInt(move _10) -> [0: bb7, 1: bb5, otherwise: bb6]; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
++ switchInt(move _10) -> [0: bb6, 1: bb4, otherwise: bb5]; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
}
bb1: {
- StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
- _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
-- switchInt(move _5) -> [0_isize: bb2, 1_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
+- switchInt(move _5) -> [0: bb2, 1: bb4, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
- }
-
- bb2: {
StorageLive(_17); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
StorageLive(_18); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
_18 = move _16; // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
- _17 = move _18; // scope 10 at $SRC_DIR/core/src/convert/mod.rs:LL:COL
- StorageDead(_18); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
- Deinit(_0); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
- ((_0 as Err).0: i32) = move _17; // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
- discriminant(_0) = 1; // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
- StorageDead(_17); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
- StorageDead(_16); // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL
- StorageDead(_8); // scope 2 at $DIR/separate_const_switch.rs:+1:9: +1:10
- StorageDead(_6); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
- StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:+1:10: +1:11
- StorageDead(_3); // scope 0 at $DIR/separate_const_switch.rs:+2:1: +2:2
- return; // scope 0 at $DIR/separate_const_switch.rs:+2:2: +2:2
+- _17 = <i32 as From<i32>>::from(move _18) -> bb8; // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
++ _17 = <i32 as From<i32>>::from(move _18) -> bb7; // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
+ // mir::Constant
+ // + span: $SRC_DIR/core/src/result.rs:LL:COL
+ // + literal: Const { ty: fn(i32) -> i32 {<i32 as From<i32>>::from}, val: Value(<ZST>) }
}
- bb5: {
- goto -> bb1; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
+ StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
+ _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
-+ switchInt(move _5) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
++ switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
}
- bb6: {
- goto -> bb1; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
+ StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
+ _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
-+ switchInt(move _5) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
++ switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
+ }
+
+- bb8: {
++ bb7: {
+ StorageDead(_18); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
+ Deinit(_0); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
+ ((_0 as Err).0: i32) = move _17; // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
+ discriminant(_0) = 1; // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
+ StorageDead(_17); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
+ StorageDead(_16); // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL
+ StorageDead(_8); // scope 2 at $DIR/separate_const_switch.rs:+1:9: +1:10
+ StorageDead(_6); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
+ StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:+1:10: +1:11
+ StorageDead(_3); // scope 0 at $DIR/separate_const_switch.rs:+2:1: +2:2
+ return; // scope 0 at $DIR/separate_const_switch.rs:+2:2: +2:2
}
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/separate_const_switch.rs:+5:11: +10:6
_3 = discriminant(_1); // scope 0 at $DIR/separate_const_switch.rs:+6:15: +6:16
- switchInt(move _3) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:+6:9: +6:16
+ switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:+6:9: +6:16
}
bb1: {
StorageDead(_6); // scope 0 at $DIR/separate_const_switch.rs:+8:43: +8:44
- goto -> bb4; // scope 0 at $DIR/separate_const_switch.rs:+8:43: +8:44
+ _8 = discriminant(_2); // scope 0 at $DIR/separate_const_switch.rs:+5:11: +10:6
-+ switchInt(move _8) -> [0_isize: bb6, 1_isize: bb4, otherwise: bb5]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
++ switchInt(move _8) -> [0: bb6, 1: bb4, otherwise: bb5]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
}
bb2: {
-
- bb4: {
_8 = discriminant(_2); // scope 0 at $DIR/separate_const_switch.rs:+5:11: +10:6
-- switchInt(move _8) -> [0_isize: bb7, 1_isize: bb5, otherwise: bb6]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
-+ switchInt(move _8) -> [0_isize: bb6, 1_isize: bb4, otherwise: bb5]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
+- switchInt(move _8) -> [0: bb7, 1: bb5, otherwise: bb6]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
++ switchInt(move _8) -> [0: bb6, 1: bb4, otherwise: bb5]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
}
- bb5: {
}
- bb3: {
-- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:+2:12: +2:17
+- switchInt(move _2) -> [0: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:+2:12: +2:17
+ bb2: {
-+ switchInt(move _2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:+2:12: +2:17
++ switchInt(move _2) -> [0: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:+2:12: +2:17
}
- bb4: {
}
bb3: {
- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:+2:12: +2:17
+ switchInt(move _2) -> [0: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:+2:12: +2:17
}
bb4: {
bb0: {
StorageLive(_1); // scope 0 at $DIR/simplify_if.rs:+1:8: +1:13
_1 = const false; // scope 0 at $DIR/simplify_if.rs:+1:8: +1:13
-- switchInt(const false) -> [false: bb3, otherwise: bb1]; // scope 0 at $DIR/simplify_if.rs:+1:8: +1:13
+- switchInt(const false) -> [0: bb3, otherwise: bb1]; // scope 0 at $DIR/simplify_if.rs:+1:8: +1:13
+ goto -> bb3; // scope 0 at $DIR/simplify_if.rs:+1:8: +1:13
}
StorageDead(_3); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:68: +1:69
StorageDead(_2); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:68: +1:69
_5 = discriminant((_1.0: std::option::Option<u8>)); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:12: +1:27
- switchInt(move _5) -> [1_isize: bb1, otherwise: bb3]; // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:12: +1:27
+ switchInt(move _5) -> [1: bb1, otherwise: bb3]; // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:12: +1:27
}
bb1: {
_4 = discriminant((_1.1: std::option::Option<T>)); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:12: +1:27
- switchInt(move _4) -> [0_isize: bb2, otherwise: bb3]; // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:12: +1:27
+ switchInt(move _4) -> [0: bb2, otherwise: bb3]; // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:12: +1:27
}
bb2: {
- _5 = const false; // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+1:11: +1:12
- _5 = const true; // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+1:11: +1:12
_2 = discriminant(_1); // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+1:11: +1:12
- switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+1:5: +1:12
+ switchInt(move _2) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+1:5: +1:12
}
bb1: {
- _1 = _2; // scope 1 at $DIR/simplify_match.rs:+1:28: +1:29
+ _1 = const false; // scope 1 at $DIR/simplify_match.rs:+1:28: +1:29
StorageDead(_2); // scope 0 at $DIR/simplify_match.rs:+1:30: +1:31
-- switchInt(_1) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:+1:5: +1:31
-+ switchInt(const false) -> [false: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:+1:5: +1:31
+- switchInt(_1) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:+1:5: +1:31
++ switchInt(const false) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:+1:5: +1:31
}
bb1: {
bb4 (cleanup): {
_6 = Eq(_4, _3); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
- switchInt(move _6) -> [false: bb3, otherwise: bb2]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
+ switchInt(move _6) -> [0: bb3, otherwise: bb2]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
}
bb5: {
bb6: {
_8 = Eq(_4, _3); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
- switchInt(move _8) -> [false: bb5, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
+ switchInt(move _8) -> [0: bb5, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
}
bb7: {
bb10 (cleanup): {
_12 = Eq(_9, _10); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
- switchInt(move _12) -> [false: bb9, otherwise: bb2]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
+ switchInt(move _12) -> [0: bb9, otherwise: bb2]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
}
bb11: {
bb12: {
_14 = Eq(_9, _10); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
- switchInt(move _14) -> [false: bb11, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
+ switchInt(move _14) -> [0: bb11, otherwise: bb1]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
}
bb13: {
bb15: {
_2 = SizeOf(std::string::String); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
_3 = Len((*_1)); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
- switchInt(move _2) -> [0_usize: bb8, otherwise: bb14]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
+ switchInt(move _2) -> [0: bb8, otherwise: bb14]; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
}
}
discriminant(_2) = 1; // scope 1 at $DIR/sroa.rs:+1:22: +1:29
StorageDead(_3); // scope 1 at $DIR/sroa.rs:+1:28: +1:29
_4 = discriminant(_2); // scope 1 at $DIR/sroa.rs:+1:12: +1:19
- switchInt(move _4) -> [1_isize: bb1, otherwise: bb2]; // scope 1 at $DIR/sroa.rs:+1:12: +1:19
+ switchInt(move _4) -> [1: bb1, otherwise: bb2]; // scope 1 at $DIR/sroa.rs:+1:12: +1:19
}
bb1: {
bb0: {
StorageLive(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
_3 = discriminant(_1); // scope 0 at $DIR/try_identity_e2e.rs:+3:19: +3:20
- switchInt(move _3) -> [0_isize: bb2, 1_isize: bb1, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:20
+ switchInt(move _3) -> [0: bb2, 1: bb1, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:20
}
bb1: {
((_2 as Break).0: E) = move _5; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
discriminant(_2) = 1; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
_6 = discriminant(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
- switchInt(move _6) -> [0_isize: bb5, 1_isize: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
+ switchInt(move _6) -> [0: bb5, 1: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
}
bb2: {
((_2 as Continue).0: T) = move _4; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
discriminant(_2) = 0; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
_6 = discriminant(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
- switchInt(move _6) -> [0_isize: bb5, 1_isize: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
+ switchInt(move _6) -> [0: bb5, 1: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
}
bb3: {
bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +2:16
- switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +2:16
+ switchInt(move _2) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +2:16
}
bb1: {
Deinit(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
discriminant(_2) = 2; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
_3 = discriminant(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
- switchInt(move _3) -> [2_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +1:19
+ switchInt(move _3) -> [2: bb1, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +1:19
}
bb1: {
Deinit(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
discriminant(_7) = 0; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
_8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
- switchInt(move _8) -> [4_isize: bb5, 5_isize: bb3, otherwise: bb4]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +7:19
+ switchInt(move _8) -> [4: bb5, 5: bb3, otherwise: bb4]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +7:19
}
bb2: {
Deinit(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
discriminant(_2) = 2; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
_3 = discriminant(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
-- switchInt(move _3) -> [0_isize: bb3, 1_isize: bb4, 2_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +1:19
-+ switchInt(move _3) -> [2_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +1:19
+- switchInt(move _3) -> [0: bb3, 1: bb4, 2: bb1, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +1:19
++ switchInt(move _3) -> [2: bb1, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +1:19
}
bb1: {
Deinit(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
discriminant(_7) = 0; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
_8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
- switchInt(move _8) -> [4_isize: bb8, 5_isize: bb6, otherwise: bb7]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +7:19
+ switchInt(move _8) -> [4: bb8, 5: bb6, otherwise: bb7]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +7:19
}
bb6: {
StorageLive(_4); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:11: +3:22
_4 = &(_1.1: Test1); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:11: +3:22
_5 = discriminant((*_4)); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:11: +3:22
- switchInt(move _5) -> [2_isize: bb3, 3_isize: bb1, otherwise: bb2]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:5: +3:22
+ switchInt(move _5) -> [2: bb3, 3: bb1, otherwise: bb2]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:5: +3:22
}
bb1: {
StorageDead(_3); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+8:6: +8:7
StorageLive(_9); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+10:5: +15:6
_10 = discriminant((_1.1: Test1)); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+10:11: +10:21
- switchInt(move _10) -> [2_isize: bb7, 3_isize: bb5, otherwise: bb6]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+10:5: +10:21
+ switchInt(move _10) -> [2: bb7, 3: bb5, otherwise: bb6]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+10:5: +10:21
}
bb5: {
StorageLive(_4); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:11: +3:22
_4 = &(_1.1: Test1); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:11: +3:22
_5 = discriminant((*_4)); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:11: +3:22
-- switchInt(move _5) -> [0_isize: bb3, 1_isize: bb4, 2_isize: bb5, 3_isize: bb1, otherwise: bb2]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:5: +3:22
-+ switchInt(move _5) -> [2_isize: bb5, 3_isize: bb1, otherwise: bb2]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:5: +3:22
+- switchInt(move _5) -> [0: bb3, 1: bb4, 2: bb5, 3: bb1, otherwise: bb2]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:5: +3:22
++ switchInt(move _5) -> [2: bb5, 3: bb1, otherwise: bb2]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:5: +3:22
}
bb1: {
StorageDead(_3); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+8:6: +8:7
StorageLive(_9); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+10:5: +15:6
_10 = discriminant((_1.1: Test1)); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+10:11: +10:21
-- switchInt(move _10) -> [0_isize: bb9, 1_isize: bb10, 2_isize: bb11, 3_isize: bb7, otherwise: bb8]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+10:5: +10:21
-+ switchInt(move _10) -> [2_isize: bb11, 3_isize: bb7, otherwise: bb8]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+10:5: +10:21
+- switchInt(move _10) -> [0: bb9, 1: bb10, 2: bb11, 3: bb7, otherwise: bb8]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+10:5: +10:21
++ switchInt(move _10) -> [2: bb11, 3: bb7, otherwise: bb8]; // scope 1 at $DIR/uninhabited_enum_branching2.rs:+10:5: +10:21
}
bb7: {
bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:+1:11: +1:12
-- switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:+1:5: +1:12
-+ switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb5]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:+1:5: +1:12
+- switchInt(move _2) -> [1: bb3, 2: bb2, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:+1:5: +1:12
++ switchInt(move _2) -> [1: bb3, 2: bb2, otherwise: bb5]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:+1:5: +1:12
}
bb1: {
bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:+1:11: +1:12
-- switchInt(move _2) -> [0_isize: bb2, 1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:+1:5: +1:12
-+ switchInt(move _2) -> [1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:+1:5: +1:12
+- switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:+1:5: +1:12
++ switchInt(move _2) -> [1: bb3, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:+1:5: +1:12
}
bb1: {
bb1: {
_2 = discriminant(_1); // scope 1 at $DIR/unreachable.rs:+1:12: +1:20
-- switchInt(move _2) -> [1_isize: bb2, otherwise: bb6]; // scope 1 at $DIR/unreachable.rs:+1:12: +1:20
-+ switchInt(move _2) -> [1_isize: bb2, otherwise: bb3]; // scope 1 at $DIR/unreachable.rs:+1:12: +1:20
+- switchInt(move _2) -> [1: bb2, otherwise: bb6]; // scope 1 at $DIR/unreachable.rs:+1:12: +1:20
++ switchInt(move _2) -> [1: bb2, otherwise: bb3]; // scope 1 at $DIR/unreachable.rs:+1:12: +1:20
}
bb2: {
- StorageLive(_5); // scope 2 at $DIR/unreachable.rs:+4:9: +8:10
- StorageLive(_6); // scope 2 at $DIR/unreachable.rs:+4:12: +4:16
- _6 = const true; // scope 2 at $DIR/unreachable.rs:+4:12: +4:16
-- switchInt(move _6) -> [false: bb4, otherwise: bb3]; // scope 2 at $DIR/unreachable.rs:+4:12: +4:16
+- switchInt(move _6) -> [0: bb4, otherwise: bb3]; // scope 2 at $DIR/unreachable.rs:+4:12: +4:16
+ unreachable; // scope 2 at $DIR/unreachable.rs:+4:12: +4:16
}
bb1: {
_3 = discriminant(_2); // scope 2 at $DIR/unreachable_diverging.rs:+2:12: +2:22
- switchInt(move _3) -> [1_isize: bb2, otherwise: bb6]; // scope 2 at $DIR/unreachable_diverging.rs:+2:12: +2:22
+ switchInt(move _3) -> [1: bb2, otherwise: bb6]; // scope 2 at $DIR/unreachable_diverging.rs:+2:12: +2:22
}
bb2: {
StorageLive(_5); // scope 2 at $DIR/unreachable_diverging.rs:+3:9: +5:10
StorageLive(_6); // scope 2 at $DIR/unreachable_diverging.rs:+3:12: +3:13
_6 = _1; // scope 2 at $DIR/unreachable_diverging.rs:+3:12: +3:13
- switchInt(move _6) -> [false: bb4, otherwise: bb3]; // scope 2 at $DIR/unreachable_diverging.rs:+3:12: +3:13
+ switchInt(move _6) -> [0: bb4, otherwise: bb3]; // scope 2 at $DIR/unreachable_diverging.rs:+3:12: +3:13
}
bb3: {
Deinit(_3); // scope 2 at $DIR/while_let_loops.rs:+2:28: +2:32
discriminant(_3) = 0; // scope 2 at $DIR/while_let_loops.rs:+2:28: +2:32
- _4 = discriminant(_3); // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
-- switchInt(move _4) -> [1_isize: bb1, otherwise: bb3]; // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
+- switchInt(move _4) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
+ _4 = const 0_isize; // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
-+ switchInt(const 0_isize) -> [1_isize: bb1, otherwise: bb3]; // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
++ switchInt(const 0_isize) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
}
bb1: {
- switchInt(((_3 as Some).0: u32)) -> [0_u32: bb2, otherwise: bb3]; // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
+ switchInt(((_3 as Some).0: u32)) -> [0: bb2, otherwise: bb3]; // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
}
bb2: {
bb2: {
StorageDead(_3); // scope 0 at $DIR/while_storage.rs:+1:21: +1:22
- switchInt(move _2) -> [false: bb7, otherwise: bb3]; // scope 0 at $DIR/while_storage.rs:+1:11: +1:22
+ switchInt(move _2) -> [0: bb7, otherwise: bb3]; // scope 0 at $DIR/while_storage.rs:+1:11: +1:22
}
bb3: {
bb4: {
StorageDead(_5); // scope 0 at $DIR/while_storage.rs:+2:22: +2:23
- switchInt(move _4) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/while_storage.rs:+2:12: +2:23
+ switchInt(move _4) -> [0: bb6, otherwise: bb5]; // scope 0 at $DIR/while_storage.rs:+2:12: +2:23
}
bb5: {
if crate_type != CrateType::Rlib {
sess.fatal(&format!("Crate type is {:?}", crate_type));
}
- let output_name = out_filename(sess, crate_type, &outputs, &*crate_name.as_str());
+ let output_name = out_filename(sess, crate_type, &outputs, crate_name);
let mut out_file = ::std::fs::File::create(output_name).unwrap();
write!(out_file, "This has been \"compiled\" successfully.").unwrap();
}
rm -f expected_*
endif
-include clear_expected_if_blessed
+-include clear_expected_if_blessed
%: $(SOURCEDIR)/lib/%.rs
# Compile the test library with coverage instrumentation
# ignore-msvc
# needs-rust-lld
+# ignore-s390x lld does not yet support s390x as target
all:
RUSTC_LOG=rustc_codegen_ssa::back::link=info $(RUSTC) -Z gcc-ld=lld -C link-args=-Wl,-v main.rs 2> $(TMPDIR)/output.txt
$(CGREP) -e "^LLD [0-9]+\.[0-9]+\.[0-9]+" < $(TMPDIR)/output.txt
--- /dev/null
+# only-macos
+#
+# Check that a set deployment target actually makes it to the linker.
+# This is important since its a compatibility hazard. The linker will
+# generate load commands differently based on what minimum OS it can assume.
+
+include ../../run-make-fulldeps/tools.mk
+
+ifeq ($(strip $(shell uname -m)),arm64)
+ GREP_PATTERN = "minos 11.0"
+else
+ GREP_PATTERN = "version 10.9"
+endif
+
+OUT_FILE=$(TMPDIR)/with_deployment_target.dylib
+all:
+ env MACOSX_DEPLOYMENT_TARGET=10.9 $(RUSTC) with_deployment_target.rs -o $(OUT_FILE)
+# XXX: The check is for either the x86_64 minimum OR the aarch64 minimum (M1 starts at macOS 11).
+# They also use different load commands, so we let that change with each too. The aarch64 check
+# isn't as robust as the x86 one, but testing both seems unneeded.
+ vtool -show-build $(OUT_FILE) | $(CGREP) -e $(GREP_PATTERN)
--- /dev/null
+#![crate_type = "cdylib"]
+
+#[allow(dead_code)]
+fn something_and_nothing() {}
+++ /dev/null
-goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
-assert: ("#functions")
-goto: "./struct.Foo.html"
-assert: ("div.item-decl")
compare-elements-css: (".impl-items .docblock table th", ".top-doc .docblock table th", ["border"])
compare-elements-css: (".impl-items .docblock table td", ".top-doc .docblock table td", ["border"])
+
+define-function: (
+ "check-colors",
+ (theme, border_color, zebra_stripe_color),
+ [
+ ("local-storage", {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|}),
+ ("reload"),
+ ("assert-css", (".top-doc .docblock table tbody tr:nth-child(1)", {
+ "background-color": "rgba(0, 0, 0, 0)",
+ })),
+ ("assert-css", (".top-doc .docblock table tbody tr:nth-child(2)", {
+ "background-color": |zebra_stripe_color|,
+ })),
+ ("assert-css", (".top-doc .docblock table tbody tr:nth-child(3)", {
+ "background-color": "rgba(0, 0, 0, 0)",
+ })),
+ ("assert-css", (".top-doc .docblock table tbody tr:nth-child(4)", {
+ "background-color": |zebra_stripe_color|,
+ })),
+ ("assert-css", (".top-doc .docblock table td", {
+ "border-style": "solid",
+ "border-width": "1px",
+ "border-color": |border_color|,
+ })),
+ ("assert-css", (".top-doc .docblock table th", {
+ "border-style": "solid",
+ "border-width": "1px",
+ "border-color": |border_color|,
+ })),
+ ]
+)
+
+call-function: ("check-colors", {
+ "theme": "dark",
+ "border_color": "rgb(224, 224, 224)",
+ "zebra_stripe_color": "rgb(42, 42, 42)",
+})
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "border_color": "rgb(92, 103, 115)",
+ "zebra_stripe_color": "rgb(25, 31, 38)",
+})
+call-function: ("check-colors", {
+ "theme": "light",
+ "border_color": "rgb(224, 224, 224)",
+ "zebra_stripe_color": "rgb(245, 245, 245)",
+})
assert-css: (".variants > .variant", {"margin": "0px 0px 12px"})
assert-css: (".variants > .docblock", {"margin": "0px 0px 32px 24px"})
+
+assert-css: (
+ "details.non-exhaustive > summary",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+)
+// This test ensures that the margins on methods are coherent inside an impl block.
goto: "file://" + |DOC_PATH| + "/test_docs/trait_members/struct.HasTrait.html#impl-TraitMembers-for-HasTrait"
assert-count: ("#trait-implementations-list > .rustdoc-toggle", 1)
goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
+
+// The next/prev buttons vertically scroll the code viewport between examples
+store-property: (initialScrollTop, ".scraped-example-list > .scraped-example pre", "scrollTop")
+focus: ".scraped-example-list > .scraped-example .next"
+press-key: "Enter"
+assert-property-false: (".scraped-example-list > .scraped-example pre", {
+ "scrollTop": |initialScrollTop|
+})
+focus: ".scraped-example-list > .scraped-example .prev"
+press-key: "Enter"
+assert-property: (".scraped-example-list > .scraped-example pre", {
+ "scrollTop": |initialScrollTop|
+})
+
+// The expand button increases the scrollHeight of the minimized code viewport
store-property: (smallOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight")
assert-property-false: (".scraped-example-list > .scraped-example pre", {
"scrollHeight": |smallOffsetHeight|
--- /dev/null
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
+
+store-value: (font, '"Fira Sans", Arial, NanumBarunGothic, sans-serif')
+
+wait-for-css: (".scraped-example-title", {"font-family": |font|})
+wait-for-css: (".more-examples-toggle summary", {"font-family": |font|})
+wait-for-css: (".more-examples-toggle .hide-more", {"font-family": |font|})
+wait-for-css: (".example-links a", {"font-family": |font|})
--- /dev/null
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
+
+// Clicking "More examples..." will open additional examples
+assert-attribute-false: (".more-examples-toggle", {"open": ""})
+click: ".more-examples-toggle"
+assert-attribute: (".more-examples-toggle", {"open": ""})
+
+// Toggling all docs will close additional examples
+click: "#toggle-all-docs"
+assert-attribute-false: (".more-examples-toggle", {"open": ""})
+
+// After re-opening the docs, the additional examples should stay closed
+click: "#toggle-all-docs"
+assert-attribute-false: (".more-examples-toggle", {"open": ""})
--- /dev/null
+fn main() {
+ scrape_examples::test_many();
+}
--- /dev/null
+fn main() {
+ scrape_examples::test_many();
+}
--- /dev/null
+fn main() {
+ scrape_examples::test_many();
+}
--- /dev/null
+fn main() {
+ scrape_examples::test_many();
+}
--- /dev/null
+fn main() {
+ scrape_examples::test_many();
+}
--- /dev/null
+fn main() {
+ scrape_examples::test_many();
+}
--- /dev/null
+fn main() {
+ scrape_examples::test_many();
+}
println!("hello world!");
println!("hello world!");
}
+ scrape_examples::test();
}
/// test();
/// ```
pub fn test() {}
+
+pub fn test_many() {}
///
/// # title!
#[doc(alias = "ThisIsAnAlias")]
+#[non_exhaustive]
pub enum WhoLetTheDogOut {
/// Woof!
Woof,
/// | header1 | header2 |
/// |--------------------------|--------------------------|
/// | Lorem Ipsum, Lorem Ipsum | Lorem Ipsum, Lorem Ipsum |
+ /// | Lorem Ipsum, Lorem Ipsum | Lorem Ipsum, Lorem Ipsum |
+ /// | Lorem Ipsum, Lorem Ipsum | Lorem Ipsum, Lorem Ipsum |
+ /// | Lorem Ipsum, Lorem Ipsum | Lorem Ipsum, Lorem Ipsum |
pub struct DocBlockTable {}
impl DocBlockTableTrait for DocBlockTable {
--- /dev/null
+// All stability badges should have rounded corners and colored backgrounds.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+show-text: true
+define-function: (
+ "check-badge",
+ (theme, background, color),
+ [
+ ("local-storage", {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|}),
+ ("goto", "file://" + |DOC_PATH| + "/test_docs/index.html"),
+ ("assert", (".docblock .stab")),
+ ("assert", (".item-table .stab")),
+ ("assert-css", (".stab", {
+ "border-radius": "3px",
+ "color": |color|,
+ "background-color": |background|,
+ })),
+ ("goto", "file://" + |DOC_PATH| + "/test_docs/fn.replaced_function.html"),
+ ("assert", (".item-info .stab")),
+ ("assert-css", (".stab", {
+ "border-radius": "3px",
+ "color": |color|,
+ "background-color": |background|,
+ })),
+ ]
+)
+
+call-function: ("check-badge", {
+ "theme": "ayu",
+ "color": "rgb(197, 197, 197)",
+ "background": "rgb(49, 69, 89)",
+})
+call-function: ("check-badge", {
+ "theme": "dark",
+ "color": "rgb(221, 221, 221)",
+ "background": "rgb(49, 69, 89)",
+})
+call-function: ("check-badge", {
+ "theme": "light",
+ "color": "rgb(0, 0, 0)",
+ "background": "rgb(255, 245, 214)",
+})
// This is now collapsed so there shouldn't be the "open" attribute on details.
assert-attribute-false: ("#main-content > details.top-doc", {"open": ""})
assert-text: ("#toggle-all-docs", "[+]")
+assert-css: (
+ "#main-content > details.top-doc > summary",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+)
click: "#toggle-all-docs"
// Not collapsed anymore so the "open" attribute should be back.
wait-for-attribute: ("#main-content > details.top-doc", {"open": ""})
click: "#toggle-all-docs"
wait-for-text: ("#toggle-all-docs", "[−]")
assert-attribute: ("details.rustdoc-toggle", {"open": ""}, ALL)
+
+// Checking the toggles style.
+show-text: true
+define-function: (
+ "check-color",
+ (theme, filter),
+ [
+ // Setting the theme.
+ ("local-storage", {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}),
+ // We reload the page so the local storage settings are being used.
+ ("reload"),
+
+ ("assert-css", ("details.rustdoc-toggle > summary::before", {
+ "opacity": "0.5",
+ "filter": |filter|,
+ }, ALL)),
+ ("move-cursor-to", "details.rustdoc-toggle summary"),
+ ("assert-css", ("details.rustdoc-toggle > summary:hover::before", {
+ "opacity": "1",
+ "filter": |filter|,
+ })),
+ // moving the cursor somewhere else to not mess with next function calls.
+ ("move-cursor-to", ".search-input"),
+ ]
+)
+
+call-function: ("check-color", {"theme": "ayu", "filter": "invert(1)"})
+call-function: ("check-color", {"theme": "dark", "filter": "invert(1)"})
+call-function: ("check-color", {"theme": "light", "filter": "none"})
--- /dev/null
+pub trait Trait {
+ /// [`Enum::Variant`]
+ fn method() {}
+}
+
+pub enum Enum {
+ Variant,
+}
--- /dev/null
+// Regression test for <https://github.com/rust-lang/rust/issues/105025>
+// aux-build: enum_variant_in_trait_method.rs
+
+extern crate enum_variant_in_trait_method;
+
+pub struct Local;
+
+/// local impl
+impl enum_variant_in_trait_method::Trait for Local {}
+
+// @!has "$.index[*][?(@.name == 'Trait')]"
+// @!has "$.index[*][?(@.name == 'method')]"
+// @count "$.index[*][?(@.docs == 'local impl')].inner.items[*]" 0
--- /dev/null
+/// The Docs
+pub trait HasDocs {}
--- /dev/null
+// Regression test for <https://github.com/rust-lang/rust/issues/105022>
+// aux-build: trait_with_docs.rs
+
+extern crate trait_with_docs;
+
+pub struct Local;
+
+impl trait_with_docs::HasDocs for Local {}
+
+// @!has "$.index[*][?(@.name == 'HasDocs')]"
#![no_std]
pub fn drop_default<T: core::default::Default>(_x: T) {}
-// FIXME(adotinthevoid): Theses shouldn't be here
-// @has "$.index[*][?(@.name=='Debug')]"
-
-// Debug may have several items. All we depend on here the that `fmt` is first. See
-// https://github.com/rust-lang/rust/pull/104525#issuecomment-1331087852 for why we
-// can't use [*].
-
-// @set Debug_fmt = "$.index[*][?(@.name=='Debug')].inner.items[0]"
-// @has "$.index[*][?(@.name=='fmt')].id" $Debug_fmt
+// @!has "$.index[*][?(@.name=='Debug')]"
+// @!has "$.index[*][?(@.name=='Default')]"
--- /dev/null
+// Just check we don't get an ICE for `N`.
+
+use std::cell::Cell;
+use std::mem;
+
+pub struct S {
+ s: Cell<usize>
+}
+
+pub const N: usize = 0 - (mem::size_of::<S>() != 400) as usize;
+//~^ ERROR evaluation of constant value failed
--- /dev/null
+error[E0080]: evaluation of constant value failed
+ --> $DIR/const-evalutation-ice.rs:10:22
+ |
+LL | pub const N: usize = 0 - (mem::size_of::<S>() != 400) as usize;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
--> $DIR/doc-cfg.rs:3:7
|
LL | #[doc(cfg(), cfg(foo, bar))]
- | ^^^^^
+ | ^^^^^ help: expected syntax is: `cfg(/* predicate */)`
error: multiple `cfg` predicates are specified
--> $DIR/doc-cfg.rs:3:23
--> $DIR/doc-cfg.rs:7:7
|
LL | #[doc(cfg())]
- | ^^^^^
+ | ^^^^^ help: expected syntax is: `cfg(/* predicate */)`
error: multiple `cfg` predicates are specified
--> $DIR/doc-cfg.rs:8:16
strip-priv-imports - strips all private import statements (`use`, `extern crate`) from a crate
propagate-doc-cfg - propagates `#[doc(cfg(...))]` to child items
collect-intra-doc-links - resolves intra-doc links
-check-code-block-syntax - validates syntax inside Rust code blocks
collect-trait-impls - retrieves trait impls for items in the crate
calculate-doc-coverage - counts the number of items with and without documentation
-check-invalid-html-tags - detects invalid HTML tags in doc comments
- check-bare-urls - detects URLs that are not hyperlinks
+ run-lints - runs some of rustdoc's lints
Default passes for rustdoc:
collect-trait-impls
strip-private (when not --document-private-items)
strip-priv-imports (when --document-private-items)
collect-intra-doc-links
-check-code-block-syntax
-check-invalid-html-tags
propagate-doc-cfg
- check-bare-urls
+ run-lints
Passes run with `--show-coverage`:
strip-hidden (when not --document-hidden-items)
--- /dev/null
+// This test ensures that it's not crashing rustdoc.
+
+pub struct Foo<'a, 'b, T> {
+ field1: dyn Bar<'a, 'b,>,
+ //~^ ERROR
+ //~^^ ERROR
+}
+
+pub trait Bar<'x, 's, U>
+ where U: 'x,
+ Self:'x,
+ Self:'s
+{}
--- /dev/null
+error[E0107]: this trait takes 1 generic argument but 0 generic arguments were supplied
+ --> $DIR/unable-fulfill-trait.rs:4:17
+ |
+LL | field1: dyn Bar<'a, 'b,>,
+ | ^^^ expected 1 generic argument
+ |
+note: trait defined here, with 1 generic parameter: `U`
+ --> $DIR/unable-fulfill-trait.rs:9:11
+ |
+LL | pub trait Bar<'x, 's, U>
+ | ^^^ -
+help: add missing generic argument
+ |
+LL | field1: dyn Bar<'a, 'b, U,>,
+ | +++
+
+error[E0227]: ambiguous lifetime bound, explicit lifetime bound required
+ --> $DIR/unable-fulfill-trait.rs:4:13
+ |
+LL | field1: dyn Bar<'a, 'b,>,
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0107, E0227.
+For more information about an error, try `rustc --explain E0107`.
-Z location-detail=val -- what location details should be tracked when using caller_location, either `none`, or a comma separated list of location details, for which valid options are `file`, `line`, and `column` (default: `file,line,column`)
-Z ls=val -- list the symbols defined by a library crate (default: no)
-Z macro-backtrace=val -- show macro backtraces (default: no)
+ -Z maximal-hir-to-mir-coverage=val -- save as much information as possible about the correspondence between MIR and HIR as source scopes (default: no)
-Z merge-functions=val -- control the operation of the MergeFunctions LLVM pass, taking the same values as the target option of the same name
-Z meta-stats=val -- gather metadata statistics (default: no)
-Z mir-emit-retag=val -- emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 (default: no)
-Z no-analysis=val -- parse and expand the source, but run no analysis
-Z no-codegen=val -- run all passes except codegen; no output
-Z no-generate-arange-section=val -- omit DWARF address ranges that give faster lookups
- -Z no-interleave-lints=val -- execute lints separately; allows benchmarking individual lints
-Z no-leak-check=val -- disable the 'leak check' for subtyping; unsound, but useful for tests
-Z no-link=val -- compile without linking
-Z no-parallel-llvm=val -- run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)
--- /dev/null
+#![crate_name = "foo"]
+
+pub trait Eq {}
+pub trait Eq2 {}
+
+// Checking that "where predicates" and "generics params" are merged.
+// @has 'foo/trait.T.html'
+// @has - "//*[@id='tymethod.f']/h4" "fn f<'a, 'b, 'c, T>()where Self: Eq, T: Eq + 'a, 'c: 'b + 'a,"
+pub trait T {
+ fn f<'a, 'b, 'c: 'a, T: Eq + 'a>()
+ where Self: Eq, Self: Eq, T: Eq, 'c: 'b;
+}
+
+// Checking that a duplicated "where predicate" is removed.
+// @has 'foo/trait.T2.html'
+// @has - "//*[@id='tymethod.f']/h4" "fn f<T>()where Self: Eq + Eq2, T: Eq2 + Eq,"
+pub trait T2 {
+ fn f<T: Eq>()
+ where Self: Eq, Self: Eq2, T: Eq2;
+}
+++ /dev/null
-// Just check we don't get an ICE for `N`.
-
-use std::cell::Cell;
-use std::mem;
-
-pub struct S {
- s: Cell<usize>
-}
-
-pub const N: usize = 0 - (mem::size_of::<S>() != 4) as usize;
pub struct Foo<T> { field: T }
// @has impl_parts/struct.Foo.html '//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
-// "impl<T: Clone> !AnAutoTrait for Foo<T>where T: Sync,"
+// "impl<T> !AnAutoTrait for Foo<T>where T: Sync + Clone,"
// @has impl_parts/trait.AnAutoTrait.html '//*[@id="implementors-list"]//h3[@class="code-header"]' \
-// "impl<T: Clone> !AnAutoTrait for Foo<T>where T: Sync,"
+// "impl<T> !AnAutoTrait for Foo<T>where T: Sync + Clone,"
impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync {}
// @has - '//section[@id="method.foo"]/h4[@class="code-header"]/a[@class="trait"]' 'Clone'
// @!has - '//section[@id="method.foo"]/h4[@class="code-header"]/span[@class="where"]' '~const'
// @has - '//section[@id="method.foo"]/h4[@class="code-header"]/span[@class="where fmt-newline"]' ': Clone'
- pub const fn foo<B: ~const Clone + ~const Destruct>()
+ pub const fn foo<B, C: ~const Clone + ~const Destruct>()
where
B: ~const Clone + ~const Destruct,
{
-<div class="item-decl"><pre class="rust enum"><code>pub enum Cow<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>>,</span>{
+<div class="item-decl"><pre class="rust enum"><code>pub enum Cow<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B),
Whatever(<a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>),
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust struct"><code>pub struct Struct<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>>,</span>{
+<div class="item-decl"><pre class="rust struct"><code>pub struct Struct<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B,
pub b: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>,
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust union"><code>pub union Union<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>>,</span>{
+<div class="item-decl"><pre class="rust union"><code>pub union Union<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
/* private fields */
}</code></pre></div>
\ No newline at end of file
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> <crate attribute>:1:1
- |
-LL | plugin(lint_plugin_test)
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
warning: item is named 'lintme'
--> $DIR/lint-plugin-cmdline-load.rs:8:1
|
|
= note: `#[warn(test_lint)]` on by default
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> <crate attribute>:1:1
+ |
+LL | plugin(lint_plugin_test)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
warning: 2 warnings emitted
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/lint-plugin-deny-attr.rs:5:1
- |
-LL | #![plugin(lint_plugin_test)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
error: item is named 'lintme'
--> $DIR/lint-plugin-deny-attr.rs:9:1
|
LL | #![deny(test_lint)]
| ^^^^^^^^^
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> $DIR/lint-plugin-deny-attr.rs:5:1
+ |
+LL | #![plugin(lint_plugin_test)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
error: aborting due to previous error; 1 warning emitted
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/lint-plugin-deny-cmdline.rs:6:1
- |
-LL | #![plugin(lint_plugin_test)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
error: item is named 'lintme'
--> $DIR/lint-plugin-deny-cmdline.rs:9:1
|
|
= note: requested on the command line with `-D test-lint`
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> $DIR/lint-plugin-deny-cmdline.rs:6:1
+ |
+LL | #![plugin(lint_plugin_test)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
error: aborting due to previous error; 1 warning emitted
#[allow(test_lint)]
//~^ ERROR allow(test_lint) incompatible
//~| ERROR allow(test_lint) incompatible
-//~| ERROR allow(test_lint) incompatible
pub fn main() {
lintme();
}
LL | #[allow(test_lint)]
| ^^^^^^^^^ overruled by previous forbid
-error[E0453]: allow(test_lint) incompatible with previous forbid
- --> $DIR/lint-plugin-forbid-attrs.rs:11:9
- |
-LL | #![forbid(test_lint)]
- | --------- `forbid` level set here
-...
-LL | #[allow(test_lint)]
- | ^^^^^^^^^ overruled by previous forbid
-
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/lint-plugin-forbid-attrs.rs:5:1
- |
-LL | #![plugin(lint_plugin_test)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
error: item is named 'lintme'
--> $DIR/lint-plugin-forbid-attrs.rs:9:1
|
LL | #[allow(test_lint)]
| ^^^^^^^^^ overruled by previous forbid
-error: aborting due to 4 previous errors; 1 warning emitted
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> $DIR/lint-plugin-forbid-attrs.rs:5:1
+ |
+LL | #![plugin(lint_plugin_test)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
+error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0453`.
#[allow(test_lint)] //~ ERROR allow(test_lint) incompatible
//~| ERROR allow(test_lint) incompatible
- //~| ERROR allow(test_lint)
+
pub fn main() {
lintme();
}
|
= note: `forbid` lint level was set on command line
-error[E0453]: allow(test_lint) incompatible with previous forbid
- --> $DIR/lint-plugin-forbid-cmdline.rs:10:9
- |
-LL | #[allow(test_lint)]
- | ^^^^^^^^^ overruled by previous forbid
- |
- = note: `forbid` lint level was set on command line
-
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/lint-plugin-forbid-cmdline.rs:6:1
- |
-LL | #![plugin(lint_plugin_test)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
error: item is named 'lintme'
--> $DIR/lint-plugin-forbid-cmdline.rs:8:1
|
|
= note: `forbid` lint level was set on command line
-error: aborting due to 4 previous errors; 1 warning emitted
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> $DIR/lint-plugin-forbid-cmdline.rs:6:1
+ |
+LL | #![plugin(lint_plugin_test)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
+error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0453`.
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/lint-plugin.rs:5:1
- |
-LL | #![plugin(lint_plugin_test)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
warning: item is named 'lintme'
--> $DIR/lint-plugin.rs:8:1
|
|
= note: `#[warn(test_lint)]` on by default
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> $DIR/lint-plugin.rs:5:1
+ |
+LL | #![plugin(lint_plugin_test)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
warning: 2 warnings emitted
|
= note: requested on the command line with `-A test_lint`
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/lint-tool-cmdline-allow.rs:7:1
- |
-LL | #![plugin(lint_tool_test)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
-warning: lint name `test_lint` is deprecated and does not have an effect anymore. Use: clippy::test_lint
- |
- = note: requested on the command line with `-A test_lint`
-
warning: item is named 'lintme'
--> $DIR/lint-tool-cmdline-allow.rs:9:1
|
|
= note: `#[warn(clippy::test_lint)]` on by default
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> $DIR/lint-tool-cmdline-allow.rs:7:1
+ |
+LL | #![plugin(lint_tool_test)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
warning: lint name `test_lint` is deprecated and does not have an effect anymore. Use: clippy::test_lint
|
= note: requested on the command line with `-A test_lint`
-warning: 6 warnings emitted
+warning: 5 warnings emitted
//~^ WARNING lint name `test_lint` is deprecated and may not have an effect in the future
//~| WARNING lint name `test_lint` is deprecated and may not have an effect in the future
//~| WARNING lint name `test_lint` is deprecated and may not have an effect in the future
-//~| WARNING lint name `test_lint` is deprecated and may not have an effect in the future
#![deny(clippy_group)]
//~^ WARNING lint name `clippy_group` is deprecated and may not have an effect in the future
//~| WARNING lint name `clippy_group` is deprecated and may not have an effect in the future
//~| WARNING lint name `clippy_group` is deprecated and may not have an effect in the future
-//~| WARNING lint name `clippy_group` is deprecated and may not have an effect in the future
fn lintme() { } //~ ERROR item is named 'lintme'
//~^ WARNING lint name `test_group` is deprecated and may not have an effect in the future
//~| WARNING lint name `test_group` is deprecated and may not have an effect in the future
//~| WARNING lint name `test_group` is deprecated and may not have an effect in the future
-//~| WARNING lint name `test_group` is deprecated and may not have an effect in the future
#[deny(this_lint_does_not_exist)] //~ WARNING unknown lint: `this_lint_does_not_exist`
fn hello() {
fn lintmetoo() { }
= note: `#[warn(renamed_and_removed_lints)]` on by default
warning: lint name `clippy_group` is deprecated and may not have an effect in the future.
- --> $DIR/lint-tool-test.rs:14:9
+ --> $DIR/lint-tool-test.rs:13:9
|
LL | #![deny(clippy_group)]
| ^^^^^^^^^^^^ help: change it to: `clippy::group`
warning: lint name `test_group` is deprecated and may not have an effect in the future.
- --> $DIR/lint-tool-test.rs:31:9
+ --> $DIR/lint-tool-test.rs:29:9
|
LL | #[allow(test_group)]
| ^^^^^^^^^^ help: change it to: `clippy::test_group`
| ^^^^^^^^^ help: change it to: `clippy::test_lint`
warning: lint name `clippy_group` is deprecated and may not have an effect in the future.
- --> $DIR/lint-tool-test.rs:14:9
- |
-LL | #![deny(clippy_group)]
- | ^^^^^^^^^^^^ help: change it to: `clippy::group`
-
-warning: lint name `test_group` is deprecated and may not have an effect in the future.
- --> $DIR/lint-tool-test.rs:31:9
- |
-LL | #[allow(test_group)]
- | ^^^^^^^^^^ help: change it to: `clippy::test_group`
-
-warning: unknown lint: `this_lint_does_not_exist`
- --> $DIR/lint-tool-test.rs:36:8
- |
-LL | #[deny(this_lint_does_not_exist)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `#[warn(unknown_lints)]` on by default
-
-warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
- --> $DIR/lint-tool-test.rs:6:1
- |
-LL | #![plugin(lint_tool_test)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
- |
- = note: `#[warn(deprecated)]` on by default
-
-warning: lint name `test_lint` is deprecated and may not have an effect in the future.
- --> $DIR/lint-tool-test.rs:9:23
- |
-LL | #![cfg_attr(foo, warn(test_lint))]
- | ^^^^^^^^^ help: change it to: `clippy::test_lint`
-
-warning: lint name `clippy_group` is deprecated and may not have an effect in the future.
- --> $DIR/lint-tool-test.rs:14:9
+ --> $DIR/lint-tool-test.rs:13:9
|
LL | #![deny(clippy_group)]
| ^^^^^^^^^^^^ help: change it to: `clippy::group`
error: item is named 'lintme'
- --> $DIR/lint-tool-test.rs:20:1
+ --> $DIR/lint-tool-test.rs:18:1
|
LL | fn lintme() { }
| ^^^^^^^^^^^^^^^
|
note: the lint level is defined here
- --> $DIR/lint-tool-test.rs:14:9
+ --> $DIR/lint-tool-test.rs:13:9
|
LL | #![deny(clippy_group)]
| ^^^^^^^^^^^^
= note: `#[deny(clippy::test_lint)]` implied by `#[deny(clippy::group)]`
error: item is named 'lintmetoo'
- --> $DIR/lint-tool-test.rs:28:5
+ --> $DIR/lint-tool-test.rs:26:5
|
LL | fn lintmetoo() { }
| ^^^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::test_group)]` implied by `#[deny(clippy::group)]`
warning: lint name `test_group` is deprecated and may not have an effect in the future.
- --> $DIR/lint-tool-test.rs:31:9
+ --> $DIR/lint-tool-test.rs:29:9
|
LL | #[allow(test_group)]
| ^^^^^^^^^^ help: change it to: `clippy::test_group`
+warning: unknown lint: `this_lint_does_not_exist`
+ --> $DIR/lint-tool-test.rs:33:8
+ |
+LL | #[deny(this_lint_does_not_exist)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(unknown_lints)]` on by default
+
+warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
+ --> $DIR/lint-tool-test.rs:6:1
+ |
+LL | #![plugin(lint_tool_test)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
+ |
+ = note: `#[warn(deprecated)]` on by default
+
warning: lint name `test_lint` is deprecated and may not have an effect in the future.
--> $DIR/lint-tool-test.rs:9:23
|
| ^^^^^^^^^ help: change it to: `clippy::test_lint`
warning: lint name `clippy_group` is deprecated and may not have an effect in the future.
- --> $DIR/lint-tool-test.rs:14:9
+ --> $DIR/lint-tool-test.rs:13:9
|
LL | #![deny(clippy_group)]
| ^^^^^^^^^^^^ help: change it to: `clippy::group`
warning: lint name `test_group` is deprecated and may not have an effect in the future.
- --> $DIR/lint-tool-test.rs:31:9
+ --> $DIR/lint-tool-test.rs:29:9
|
LL | #[allow(test_group)]
| ^^^^^^^^^^ help: change it to: `clippy::test_group`
-error: aborting due to 2 previous errors; 14 warnings emitted
+error: aborting due to 2 previous errors; 11 warnings emitted
fn_decl: decl.clone(),
body: e,
fn_decl_span: DUMMY_SP,
+ fn_arg_span: DUMMY_SP,
})))
});
}
--- /dev/null
+struct ArpIPv4<'a> {
+ s: &'a u8
+}
+
+impl<'a> ArpIPv4<'a> {
+ const LENGTH: usize = 20;
+
+ pub fn to_buffer() -> [u8; Self::LENGTH] {
+ //~^ ERROR: generic `Self` types are currently not permitted in anonymous constants
+ unimplemented!()
+ }
+}
+
+fn main() {}
--- /dev/null
+error: generic `Self` types are currently not permitted in anonymous constants
+ --> $DIR/issue-47814.rs:8:32
+ |
+LL | pub fn to_buffer() -> [u8; Self::LENGTH] {
+ | ^^^^
+ |
+note: not a concrete type
+ --> $DIR/issue-47814.rs:5:10
+ |
+LL | impl<'a> ArpIPv4<'a> {
+ | ^^^^^^^^^^^
+
+error: aborting due to previous error
+
+#![feature(type_ascription)]
+
fn e() {
- p:a<p:p<e=6>>
- //~^ ERROR comparison operators
+ type_ascribe!(p, a<p:p<e=6>>);
+ //~^ ERROR cannot find type `a` in this scope
//~| ERROR cannot find value
//~| ERROR associated const equality
- //~| ERROR associated const equality
+ //~| ERROR cannot find trait `p` in this scope
//~| ERROR associated type bounds
}
-error: comparison operators cannot be chained
- --> $DIR/issue-93835.rs:2:8
- |
-LL | fn e() {
- | - while parsing this struct
-LL | p:a<p:p<e=6>>
- | ^ ^
- |
- = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
- = help: or use `(...)` if you meant to specify fn arguments
-
error[E0425]: cannot find value `p` in this scope
- --> $DIR/issue-93835.rs:2:5
- |
-LL | p:a<p:p<e=6>>
- | ^ not found in this scope
- |
-help: you might have meant to write a `struct` literal
- |
-LL ~ fn e() { SomeStruct {
-LL | p:a<p:p<e=6>>
- ...
-LL |
-LL ~ }}
+ --> $DIR/issue-93835.rs:4:19
|
-help: maybe you meant to write a path separator here
- |
-LL | p::a<p:p<e=6>>
- | ~~
-help: maybe you meant to write an assignment here
- |
-LL | let p:a<p:p<e=6>>
- | ~~~~~
+LL | type_ascribe!(p, a<p:p<e=6>>);
+ | ^ not found in this scope
-error[E0658]: associated const equality is incomplete
- --> $DIR/issue-93835.rs:2:13
+error[E0412]: cannot find type `a` in this scope
+ --> $DIR/issue-93835.rs:4:22
|
-LL | p:a<p:p<e=6>>
- | ^^^
+LL | type_ascribe!(p, a<p:p<e=6>>);
+ | ^ not found in this scope
+
+error[E0405]: cannot find trait `p` in this scope
+ --> $DIR/issue-93835.rs:4:26
|
- = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
- = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
+LL | type_ascribe!(p, a<p:p<e=6>>);
+ | ^ not found in this scope
error[E0658]: associated const equality is incomplete
- --> $DIR/issue-93835.rs:2:13
+ --> $DIR/issue-93835.rs:4:28
|
-LL | p:a<p:p<e=6>>
- | ^^^
+LL | type_ascribe!(p, a<p:p<e=6>>);
+ | ^^^
|
= note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
= help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
error[E0658]: associated type bounds are unstable
- --> $DIR/issue-93835.rs:2:9
+ --> $DIR/issue-93835.rs:4:24
|
-LL | p:a<p:p<e=6>>
- | ^^^^^^^^
+LL | type_ascribe!(p, a<p:p<e=6>>);
+ | ^^^^^^^^
|
= note: see issue #52662 <https://github.com/rust-lang/rust/issues/52662> for more information
= help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
error: aborting due to 5 previous errors
-Some errors have detailed explanations: E0425, E0658.
-For more information about an error, try `rustc --explain E0425`.
+Some errors have detailed explanations: E0405, E0412, E0425, E0658.
+For more information about an error, try `rustc --explain E0405`.
--- /dev/null
+// check-pass
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct Foo;
+
+impl Foo {
+ type Bar<T> = u8;
+}
+
+fn main() {
+ let a: Foo::Bar<()>;
+}
--- /dev/null
+// check-pass
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct S<T>(T);
+
+impl<T: O> S<T> {
+ type P = <T as O>::P;
+}
+
+trait O {
+ type P;
+}
+
+impl O for i32 {
+ type P = String;
+}
+
+fn main() {
+ let _: S<i32>::P = String::new();
+}
--- /dev/null
+// check-pass
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct S;
+
+impl S {
+ type P<T: O> = <T as O>::P;
+}
+
+trait O {
+ type P;
+}
+
+impl O for i32 {
+ type P = String;
+}
+
+fn main() {
+ let _: S::P<i32> = String::new();
+}
--- /dev/null
+// check-pass
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct S<T>(T);
+
+impl<T> S<T> {
+ type P = T;
+}
+
+fn main() {
+ type A = S<()>::P;
+ let _: A = ();
+}
LL | | panic!()
LL | | }
| |_^
- = note: required because it captures the following types: `ResumeTy`, `Option<bool>`, `impl Future<Output = !>`, `()`
+ = note: required because it captures the following types: `&mut Context<'_>`, `Option<bool>`, `impl Future<Output = !>`, `()`
note: required because it's used within this `async fn` body
--> $DIR/async-await-let-else.rs:21:32
|
--- /dev/null
+// compile-flags: -Zdrop-tracking
+// edition: 2021
+
+fn main() {}
+
+async fn foo() {
+ None { value: (), ..Default::default() }.await;
+ //~^ ERROR `Option<_>` is not a future
+ //~| ERROR variant `Option<_>::None` has no field named `value`
+}
--- /dev/null
+error[E0559]: variant `Option<_>::None` has no field named `value`
+ --> $DIR/drop-track-bad-field-in-fru.rs:7:12
+ |
+LL | None { value: (), ..Default::default() }.await;
+ | ^^^^^ `Option<_>::None` does not have this field
+
+error[E0277]: `Option<_>` is not a future
+ --> $DIR/drop-track-bad-field-in-fru.rs:7:45
+ |
+LL | None { value: (), ..Default::default() }.await;
+ | ^^^^^^
+ | |
+ | `Option<_>` is not a future
+ | help: remove the `.await`
+ |
+ = help: the trait `Future` is not implemented for `Option<_>`
+ = note: Option<_> must be a future or must implement `IntoFuture` to be awaited
+ = note: required for `Option<_>` to implement `IntoFuture`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0277, E0559.
+For more information about an error, try `rustc --explain E0277`.
--- /dev/null
+// edition:2018
+#![feature(generators, generator_trait)]
+
+use std::future::Future;
+use std::ops::Generator;
+
+async fn async_fn() {}
+fn returns_async_block() -> impl Future<Output = ()> {
+ async {}
+}
+fn returns_generator() -> impl Generator<(), Yield = (), Return = ()> {
+ || {
+ let _: () = yield ();
+ }
+}
+
+fn takes_future(_f: impl Future<Output = ()>) {}
+fn takes_generator<ResumeTy>(_g: impl Generator<ResumeTy, Yield = (), Return = ()>) {}
+
+fn main() {
+ // okay:
+ takes_future(async_fn());
+ takes_future(returns_async_block());
+ takes_future(async {});
+ takes_generator(returns_generator());
+ takes_generator(|| {
+ let _: () = yield ();
+ });
+
+ // async futures are not generators:
+ takes_generator(async_fn());
+ //~^ ERROR the trait bound
+ takes_generator(returns_async_block());
+ //~^ ERROR the trait bound
+ takes_generator(async {});
+ //~^ ERROR the trait bound
+
+ // generators are not futures:
+ takes_future(returns_generator());
+ //~^ ERROR is not a future
+ takes_future(|ctx| {
+ //~^ ERROR is not a future
+ ctx = yield ();
+ });
+}
--- /dev/null
+error[E0277]: the trait bound `impl Future<Output = ()>: Generator<_>` is not satisfied
+ --> $DIR/generator-not-future.rs:31:21
+ |
+LL | takes_generator(async_fn());
+ | --------------- ^^^^^^^^^^ the trait `Generator<_>` is not implemented for `impl Future<Output = ()>`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `takes_generator`
+ --> $DIR/generator-not-future.rs:18:39
+ |
+LL | fn takes_generator<ResumeTy>(_g: impl Generator<ResumeTy, Yield = (), Return = ()>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_generator`
+
+error[E0277]: the trait bound `impl Future<Output = ()>: Generator<_>` is not satisfied
+ --> $DIR/generator-not-future.rs:33:21
+ |
+LL | takes_generator(returns_async_block());
+ | --------------- ^^^^^^^^^^^^^^^^^^^^^ the trait `Generator<_>` is not implemented for `impl Future<Output = ()>`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `takes_generator`
+ --> $DIR/generator-not-future.rs:18:39
+ |
+LL | fn takes_generator<ResumeTy>(_g: impl Generator<ResumeTy, Yield = (), Return = ()>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_generator`
+
+error[E0277]: the trait bound `[async block@$DIR/generator-not-future.rs:35:21: 35:29]: Generator<_>` is not satisfied
+ --> $DIR/generator-not-future.rs:35:21
+ |
+LL | takes_generator(async {});
+ | --------------- ^^^^^^^^ the trait `Generator<_>` is not implemented for `[async block@$DIR/generator-not-future.rs:35:21: 35:29]`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `takes_generator`
+ --> $DIR/generator-not-future.rs:18:39
+ |
+LL | fn takes_generator<ResumeTy>(_g: impl Generator<ResumeTy, Yield = (), Return = ()>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_generator`
+
+error[E0277]: `impl Generator<Yield = (), Return = ()>` is not a future
+ --> $DIR/generator-not-future.rs:39:18
+ |
+LL | takes_future(returns_generator());
+ | ------------ ^^^^^^^^^^^^^^^^^^^ `impl Generator<Yield = (), Return = ()>` is not a future
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Future` is not implemented for `impl Generator<Yield = (), Return = ()>`
+ = note: impl Generator<Yield = (), Return = ()> must be a future or must implement `IntoFuture` to be awaited
+note: required by a bound in `takes_future`
+ --> $DIR/generator-not-future.rs:17:26
+ |
+LL | fn takes_future(_f: impl Future<Output = ()>) {}
+ | ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future`
+
+error[E0277]: `[generator@$DIR/generator-not-future.rs:41:18: 41:23]` is not a future
+ --> $DIR/generator-not-future.rs:41:18
+ |
+LL | takes_future(|ctx| {
+ | _____------------_^
+ | | |
+ | | required by a bound introduced by this call
+LL | |
+LL | | ctx = yield ();
+LL | | });
+ | |_____^ `[generator@$DIR/generator-not-future.rs:41:18: 41:23]` is not a future
+ |
+ = help: the trait `Future` is not implemented for `[generator@$DIR/generator-not-future.rs:41:18: 41:23]`
+ = note: [generator@$DIR/generator-not-future.rs:41:18: 41:23] must be a future or must implement `IntoFuture` to be awaited
+note: required by a bound in `takes_future`
+ --> $DIR/generator-not-future.rs:17:26
+ |
+LL | fn takes_future(_f: impl Future<Output = ()>) {}
+ | ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
LL | async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
| ^^^^^^^
|
-note: the parameter type `U` must be valid for the anonymous lifetime as defined here...
+note: the parameter type `U` must be valid for the anonymous lifetime defined here...
--> $DIR/async-generics-and-bounds.rs:12:18
|
LL | async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
- | ^
+ | ^^^^^
note: ...so that the reference type `&(T, U)` does not outlive the data it points at
--> $DIR/async-generics-and-bounds.rs:12:28
|
LL | async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
| ^^^^^^^
|
-note: the parameter type `T` must be valid for the anonymous lifetime as defined here...
+note: the parameter type `T` must be valid for the anonymous lifetime defined here...
--> $DIR/async-generics-and-bounds.rs:12:18
|
LL | async fn foo(&self) -> &(T, U) where T: Debug + Sized, U: Hash;
- | ^
+ | ^^^^^
note: ...so that the reference type `&(T, U)` does not outlive the data it points at
--> $DIR/async-generics-and-bounds.rs:12:28
|
LL | async fn foo(&self) -> &(T, U);
| ^^^^^^^
|
-note: the parameter type `U` must be valid for the anonymous lifetime as defined here...
+note: the parameter type `U` must be valid for the anonymous lifetime defined here...
--> $DIR/async-generics.rs:9:18
|
LL | async fn foo(&self) -> &(T, U);
- | ^
+ | ^^^^^
note: ...so that the reference type `&(T, U)` does not outlive the data it points at
--> $DIR/async-generics.rs:9:28
|
LL | async fn foo(&self) -> &(T, U);
| ^^^^^^^
|
-note: the parameter type `T` must be valid for the anonymous lifetime as defined here...
+note: the parameter type `T` must be valid for the anonymous lifetime defined here...
--> $DIR/async-generics.rs:9:18
|
LL | async fn foo(&self) -> &(T, U);
- | ^
+ | ^^^^^
note: ...so that the reference type `&(T, U)` does not outlive the data it points at
--> $DIR/async-generics.rs:9:28
|
-// check-fail
-// known-bug: #102682
+// check-pass
// edition: 2021
#![feature(async_fn_in_trait)]
+++ /dev/null
-error[E0309]: the parameter type `Self` may not live long enough
- --> $DIR/async-lifetimes-and-bounds.rs:11:43
- |
-LL | async fn foo(&'a self, key: &'b T) -> (&'a Self, &'b T) where T: Debug + Sized;
- | ^^^^^^^^^^^^^^^^^
- |
- = help: consider adding an explicit lifetime bound `Self: 'a`...
- = note: ...so that the reference type `&'a Self` does not outlive the data it points at
-
-error[E0309]: the parameter type `T` may not live long enough
- --> $DIR/async-lifetimes-and-bounds.rs:11:43
- |
-LL | async fn foo(&'a self, key: &'b T) -> (&'a Self, &'b T) where T: Debug + Sized;
- | ^^^^^^^^^^^^^^^^^ ...so that the reference type `&'b T` does not outlive the data it points at
- |
-help: consider adding an explicit lifetime bound...
- |
-LL | trait MyTrait<'a, 'b, T: 'b> {
- | ++++
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0309`.
-// check-fail
-// known-bug: #102682
+// check-pass
// edition: 2021
#![feature(async_fn_in_trait)]
+++ /dev/null
-error[E0309]: the parameter type `Self` may not live long enough
- --> $DIR/async-lifetimes.rs:9:43
- |
-LL | async fn foo(&'a self, key: &'b T) -> (&'a Self, &'b T);
- | ^^^^^^^^^^^^^^^^^
- |
- = help: consider adding an explicit lifetime bound `Self: 'a`...
- = note: ...so that the reference type `&'a Self` does not outlive the data it points at
-
-error[E0309]: the parameter type `T` may not live long enough
- --> $DIR/async-lifetimes.rs:9:43
- |
-LL | async fn foo(&'a self, key: &'b T) -> (&'a Self, &'b T);
- | ^^^^^^^^^^^^^^^^^ ...so that the reference type `&'b T` does not outlive the data it points at
- |
-help: consider adding an explicit lifetime bound...
- |
-LL | trait MyTrait<'a, 'b, T: 'b> {
- | ++++
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0309`.
--- /dev/null
+// check-pass
+// edition: 2021
+
+#![feature(async_fn_in_trait)]
+#![allow(incomplete_features)]
+
+trait TcpStack {
+ type Connection<'a>: Sized where Self: 'a;
+ fn connect<'a>(&'a self) -> Self::Connection<'a>;
+ async fn async_connect<'a>(&'a self) -> Self::Connection<'a>;
+}
+
+fn main() {}
--- /dev/null
+// edition:2021
+
+#![feature(async_fn_in_trait)]
+//~^ WARN the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
+
+trait MyTrait {
+ async fn foo<'a>(&self);
+ async fn bar(&self);
+}
+
+impl MyTrait for i32 {
+ async fn foo(&self) {}
+ //~^ ERROR lifetime parameters or bounds on method `foo` do not match the trait declaration
+
+ async fn bar(&self) {
+ self.foo();
+ }
+}
+
+fn main() {}
--- /dev/null
+warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/lifetime-mismatch.rs:3:12
+ |
+LL | #![feature(async_fn_in_trait)]
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
+ --> $DIR/lifetime-mismatch.rs:12:17
+ |
+LL | async fn foo<'a>(&self);
+ | ---- lifetimes in impl do not match this method in trait
+...
+LL | async fn foo(&self) {}
+ | ^ lifetimes do not match method in trait
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0195`.
--- /dev/null
+// check-pass
+// edition: 2021
+
+#![feature(async_fn_in_trait)]
+#![feature(return_position_impl_trait_in_trait)]
+#![allow(incomplete_features)]
+
+use std::future::Future;
+use std::marker::PhantomData;
+
+trait Lockable<K, V> {
+ async fn lock_all_entries(&self) -> impl Future<Output = Guard<'_>>;
+}
+
+struct Guard<'a>(PhantomData<&'a ()>);
+
+fn main() {}
--- /dev/null
+// edition: 2021
+
+#![feature(async_fn_in_trait)]
+//~^ WARN the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
+
+trait A {
+ async fn e() {
+ Ok(())
+ //~^ ERROR mismatched types
+ //~| HELP consider using a semicolon here
+ }
+}
+
+fn main() {}
--- /dev/null
+warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/return-type-suggestion.rs:3:12
+ |
+LL | #![feature(async_fn_in_trait)]
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0308]: mismatched types
+ --> $DIR/return-type-suggestion.rs:8:9
+ |
+LL | Ok(())
+ | ^^^^^^- help: consider using a semicolon here: `;`
+ | |
+ | expected `()`, found enum `Result`
+ |
+ = note: expected unit type `()`
+ found enum `Result<(), _>`
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0308`.
|
LL | fn make_non_send_future2() -> impl Future<Output = Arc<RefCell<i32>>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: required because it captures the following types: `ResumeTy`, `impl Future<Output = Arc<RefCell<i32>>>`, `()`, `Ready<i32>`
+ = note: required because it captures the following types: `&mut Context<'_>`, `impl Future<Output = Arc<RefCell<i32>>>`, `()`, `Ready<i32>`
note: required because it's used within this `async` block
--> $DIR/issue-68112.rs:60:20
|
|
LL | fn make_non_send_future2() -> impl Future<Output = Arc<RefCell<i32>>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: required because it captures the following types: `ResumeTy`, `impl Future<Output = Arc<RefCell<i32>>>`, `()`, `i32`, `Ready<i32>`
+ = note: required because it captures the following types: `&mut Context<'_>`, `impl Future<Output = Arc<RefCell<i32>>>`, `()`, `i32`, `Ready<i32>`
note: required because it's used within this `async` block
--> $DIR/issue-68112.rs:60:20
|
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
+ = note: requirement occurs because of a mutable reference to `Context<'_>`
+ = note: mutable references are invariant over their type parameter
+ = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: aborting due to previous error
| ___________________________________________________________________^
LL | | }
| |_^
- = note: required because it captures the following types: `ResumeTy`, `impl Future<Output = ()>`, `()`
+ = note: required because it captures the following types: `&mut Context<'_>`, `impl Future<Output = ()>`, `()`
note: required because it's used within this `async` block
--> $DIR/issue-70935-complex-spans.rs:16:5
|
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `NotSend`
= note: required because it appears within the type `(NotSend,)`
- = note: required because it captures the following types: `ResumeTy`, `(NotSend,)`, `()`, `impl Future<Output = ()>`
+ = note: required because it captures the following types: `&mut Context<'_>`, `(NotSend,)`, `()`, `impl Future<Output = ()>`
note: required because it's used within this `async fn` body
--> $DIR/partial-drop-partial-reinit.rs:31:16
|
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `NotSend`
= note: required because it appears within the type `(NotSend,)`
- = note: required because it captures the following types: `ResumeTy`, `(NotSend,)`, `impl Future<Output = ()>`, `()`
+ = note: required because it captures the following types: `&mut Context<'_>`, `(NotSend,)`, `impl Future<Output = ()>`, `()`
note: required because it's used within this `async fn` body
--> $DIR/partial-drop-partial-reinit.rs:31:16
|
--- /dev/null
+// check-pass
+// edition:2021
+
+#[track_caller]
+fn f() {
+ let _ = async {};
+}
+
+fn main() {
+ f();
+}
bar_track_caller().await
}
+struct Foo;
+
+impl Foo {
+ #[track_caller]
+ async fn bar_assoc() {
+ panic!();
+ }
+}
+
+async fn foo_assoc() {
+ Foo::bar_assoc().await
+}
+
fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 {
let loc = Arc::new(Mutex::new(None));
fn main() {
assert_eq!(panicked_at(|| block_on(foo())), 41);
assert_eq!(panicked_at(|| block_on(foo_track_caller())), 54);
+ assert_eq!(panicked_at(|| block_on(foo_assoc())), 67);
}
--- /dev/null
+#[w = { extern crate alloc; }]
+//~^ ERROR unexpected expression: `{
+//~| ERROR cannot find attribute `w` in this scope
+fn f() {}
+
+fn main() {}
--- /dev/null
+error: unexpected expression: `{
+ extern crate alloc;
+ }`
+ --> $DIR/unused-item-in-attr.rs:1:7
+ |
+LL | #[w = { extern crate alloc; }]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: cannot find attribute `w` in this scope
+ --> $DIR/unused-item-in-attr.rs:1:3
+ |
+LL | #[w = { extern crate alloc; }]
+ | ^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+#![feature(auto_traits)]
+
+auto trait Trait1<'a> {}
+//~^ ERROR auto traits cannot have generic parameters
+
+fn f<'a>(x: &dyn Trait1<'a>)
+{}
+
+fn main() {
+ f(&1);
+}
--- /dev/null
+error[E0567]: auto traits cannot have generic parameters
+ --> $DIR/bad-generics-on-dyn.rs:3:18
+ |
+LL | auto trait Trait1<'a> {}
+ | ------^^^^ help: remove the parameters
+ | |
+ | auto trait cannot have generic parameters
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0567`.
|
= help: the trait `Mul<f32>` is not implemented for `i32`
= help: the following other types implement trait `Mul<Rhs>`:
- <&'a f32 as Mul<f32>>
- <&'a f64 as Mul<f64>>
- <&'a i128 as Mul<i128>>
- <&'a i16 as Mul<i16>>
<&'a i32 as Mul<i32>>
- <&'a i64 as Mul<i64>>
- <&'a i8 as Mul<i8>>
- <&'a isize as Mul<isize>>
- and 49 others
+ <&i32 as Mul<&i32>>
+ <i32 as Mul<&i32>>
+ <i32 as Mul>
error: aborting due to previous error
error[E0080]: evaluation of constant value failed
--> $DIR/issue-81899.rs:11:5
|
-LL | const _CONST: &[u8] = &f(&[], |_| {});
- | -------------- inside `_CONST` at $DIR/issue-81899.rs:4:24
-...
+LL | panic!()
+ | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/issue-81899.rs:11:5
+ |
+note: inside `f::<[closure@$DIR/issue-81899.rs:4:31: 4:34]>`
+ --> $DIR/issue-81899.rs:11:5
+ |
LL | panic!()
| ^^^^^^^^
- | |
- | the evaluated program panicked at 'explicit panic', $DIR/issue-81899.rs:11:5
- | inside `f::<[closure@$DIR/issue-81899.rs:4:31: 4:34]>` at $SRC_DIR/std/src/panic.rs:LL:COL
+note: inside `_CONST`
+ --> $DIR/issue-81899.rs:4:24
|
+LL | const _CONST: &[u8] = &f(&[], |_| {});
+ | ^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
note: erroneous constant used
error[E0080]: evaluation of constant value failed
--> $DIR/issue-88434-minimal-example.rs:10:5
|
-LL | const _CONST: &() = &f(&|_| {});
- | ---------- inside `_CONST` at $DIR/issue-88434-minimal-example.rs:3:22
-...
+LL | panic!()
+ | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/issue-88434-minimal-example.rs:10:5
+ |
+note: inside `f::<[closure@$DIR/issue-88434-minimal-example.rs:3:25: 3:28]>`
+ --> $DIR/issue-88434-minimal-example.rs:10:5
+ |
LL | panic!()
| ^^^^^^^^
- | |
- | the evaluated program panicked at 'explicit panic', $DIR/issue-88434-minimal-example.rs:10:5
- | inside `f::<[closure@$DIR/issue-88434-minimal-example.rs:3:25: 3:28]>` at $SRC_DIR/std/src/panic.rs:LL:COL
+note: inside `_CONST`
+ --> $DIR/issue-88434-minimal-example.rs:3:22
|
+LL | const _CONST: &() = &f(&|_| {});
+ | ^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
note: erroneous constant used
error[E0080]: evaluation of constant value failed
--> $DIR/issue-88434-removal-index-should-be-less.rs:10:5
|
-LL | const _CONST: &[u8] = &f(&[], |_| {});
- | -------------- inside `_CONST` at $DIR/issue-88434-removal-index-should-be-less.rs:3:24
-...
+LL | panic!()
+ | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/issue-88434-removal-index-should-be-less.rs:10:5
+ |
+note: inside `f::<[closure@$DIR/issue-88434-removal-index-should-be-less.rs:3:31: 3:34]>`
+ --> $DIR/issue-88434-removal-index-should-be-less.rs:10:5
+ |
LL | panic!()
| ^^^^^^^^
- | |
- | the evaluated program panicked at 'explicit panic', $DIR/issue-88434-removal-index-should-be-less.rs:10:5
- | inside `f::<[closure@$DIR/issue-88434-removal-index-should-be-less.rs:3:31: 3:34]>` at $SRC_DIR/std/src/panic.rs:LL:COL
+note: inside `_CONST`
+ --> $DIR/issue-88434-removal-index-should-be-less.rs:3:24
|
+LL | const _CONST: &[u8] = &f(&[], |_| {});
+ | ^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
note: erroneous constant used
--> $DIR/closure-return-type-must-be-sized.rs:14:19
|
LL | pub fn bar<F: FnOnce() -> R, R: ?Sized>() {}
- | ^^^^^^^^^^^^^ required by this bound in `a::bar`
+ | ^^^^^^^^^^^^^ required by this bound in `bar`
error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
--> $DIR/closure-return-type-must-be-sized.rs:56:5
--> $DIR/closure-return-type-must-be-sized.rs:28:19
|
LL | pub fn bar<F: Fn() -> R, R: ?Sized>() {}
- | ^^^^^^^^^ required by this bound in `b::bar`
+ | ^^^^^^^^^ required by this bound in `bar`
error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
--> $DIR/closure-return-type-must-be-sized.rs:63:5
--> $DIR/closure-return-type-must-be-sized.rs:42:19
|
LL | pub fn bar<F: FnMut() -> R, R: ?Sized>() {}
- | ^^^^^^^^^^^^ required by this bound in `c::bar`
+ | ^^^^^^^^^^^^ required by this bound in `bar`
error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
--> $DIR/closure-return-type-must-be-sized.rs:70:5
+#![feature(type_ascription)]
+
fn main() {
- 2: n([u8; || 1])
+ type_ascribe!(2, n([u8; || 1]))
//~^ ERROR cannot find type `n` in this scope
//~| ERROR mismatched types
}
error[E0412]: cannot find type `n` in this scope
- --> $DIR/issue-90871.rs:2:8
+ --> $DIR/issue-90871.rs:4:22
|
-LL | 2: n([u8; || 1])
- | ^ expecting a type here because of type ascription
+LL | type_ascribe!(2, n([u8; || 1]))
+ | ^ help: a trait with a similar name exists: `Fn`
+ |
+ ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+ |
+LL | pub trait Fn<Args: Tuple>: FnMut<Args> {
+ | -------------------------------------- similarly named trait `Fn` defined here
error[E0308]: mismatched types
- --> $DIR/issue-90871.rs:2:15
+ --> $DIR/issue-90871.rs:4:29
|
-LL | 2: n([u8; || 1])
- | ^^^^ expected `usize`, found closure
+LL | type_ascribe!(2, n([u8; || 1]))
+ | ^^^^ expected `usize`, found closure
|
= note: expected type `usize`
- found closure `[closure@$DIR/issue-90871.rs:2:15: 2:17]`
+ found closure `[closure@$DIR/issue-90871.rs:4:29: 4:31]`
help: use parentheses to call this closure
|
-LL | 2: n([u8; (|| 1)()])
- | + +++
+LL | type_ascribe!(2, n([u8; (|| 1)()]))
+ | + +++
error: aborting due to 2 previous errors
use std::fmt::Debug;
pub fn main() {
- let _ = box { [1, 2, 3] }: Box<[i32]>; //~ ERROR mismatched types
- let _ = box if true { [1, 2, 3] } else { [1, 3, 4] }: Box<[i32]>; //~ ERROR mismatched types
- let _ = box match true { true => [1, 2, 3], false => [1, 3, 4] }: Box<[i32]>;
+ let _ = type_ascribe!(box { [1, 2, 3] }, Box<[i32]>); //~ ERROR mismatched types
+ let _ = type_ascribe!(box if true { [1, 2, 3] } else { [1, 3, 4] }, Box<[i32]>); //~ ERROR mismatched types
+ let _ = type_ascribe!(box match true { true => [1, 2, 3], false => [1, 3, 4] }, Box<[i32]>);
//~^ ERROR mismatched types
- let _ = box { |x| (x as u8) }: Box<dyn Fn(i32) -> _>; //~ ERROR mismatched types
- let _ = box if true { false } else { true }: Box<dyn Debug>; //~ ERROR mismatched types
- let _ = box match true { true => 'a', false => 'b' }: Box<dyn Debug>; //~ ERROR mismatched types
+ let _ = type_ascribe!(box { |x| (x as u8) }, Box<dyn Fn(i32) -> _>); //~ ERROR mismatched types
+ let _ = type_ascribe!(box if true { false } else { true }, Box<dyn Debug>); //~ ERROR mismatched types
+ let _ = type_ascribe!(box match true { true => 'a', false => 'b' }, Box<dyn Debug>); //~ ERROR mismatched types
- let _ = &{ [1, 2, 3] }: &[i32]; //~ ERROR mismatched types
- let _ = &if true { [1, 2, 3] } else { [1, 3, 4] }: &[i32]; //~ ERROR mismatched types
- let _ = &match true { true => [1, 2, 3], false => [1, 3, 4] }: &[i32];
+ let _ = type_ascribe!(&{ [1, 2, 3] }, &[i32]); //~ ERROR mismatched types
+ let _ = type_ascribe!(&if true { [1, 2, 3] } else { [1, 3, 4] }, &[i32]); //~ ERROR mismatched types
+ let _ = type_ascribe!(&match true { true => [1, 2, 3], false => [1, 3, 4] }, &[i32]);
//~^ ERROR mismatched types
- let _ = &{ |x| (x as u8) }: &dyn Fn(i32) -> _; //~ ERROR mismatched types
- let _ = &if true { false } else { true }: &dyn Debug; //~ ERROR mismatched types
- let _ = &match true { true => 'a', false => 'b' }: &dyn Debug; //~ ERROR mismatched types
+ let _ = type_ascribe!(&{ |x| (x as u8) }, &dyn Fn(i32) -> _); //~ ERROR mismatched types
+ let _ = type_ascribe!(&if true { false } else { true }, &dyn Debug); //~ ERROR mismatched types
+ let _ = type_ascribe!(&match true { true => 'a', false => 'b' }, &dyn Debug); //~ ERROR mismatched types
- let _ = Box::new([1, 2, 3]): Box<[i32]>; //~ ERROR mismatched types
- let _ = Box::new(|x| (x as u8)): Box<dyn Fn(i32) -> _>; //~ ERROR mismatched types
+ let _ = type_ascribe!(Box::new([1, 2, 3]), Box<[i32]>); //~ ERROR mismatched types
+ let _ = type_ascribe!(Box::new(|x| (x as u8)), Box<dyn Fn(i32) -> _>); //~ ERROR mismatched types
- let _ = vec![
+ let _ = type_ascribe!(vec![
Box::new(|x| (x as u8)),
box |x| (x as i16 as u8),
- ]: Vec<Box<dyn Fn(i32) -> _>>;
+ ], Vec<Box<dyn Fn(i32) -> _>>);
}
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:9:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:9:27
|
-LL | let _ = box { [1, 2, 3] }: Box<[i32]>;
- | ^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL | let _ = type_ascribe!(box { [1, 2, 3] }, Box<[i32]>);
+ | ^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
|
= note: expected struct `Box<[i32]>`
found struct `Box<[i32; 3]>`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:10:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:10:27
|
-LL | let _ = box if true { [1, 2, 3] } else { [1, 3, 4] }: Box<[i32]>;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL | let _ = type_ascribe!(box if true { [1, 2, 3] } else { [1, 3, 4] }, Box<[i32]>);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
|
= note: expected struct `Box<[i32]>`
found struct `Box<[i32; 3]>`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:11:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:11:27
|
-LL | let _ = box match true { true => [1, 2, 3], false => [1, 3, 4] }: Box<[i32]>;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL | let _ = type_ascribe!(box match true { true => [1, 2, 3], false => [1, 3, 4] }, Box<[i32]>);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
|
= note: expected struct `Box<[i32]>`
found struct `Box<[i32; 3]>`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:13:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:13:27
|
-LL | let _ = box { |x| (x as u8) }: Box<dyn Fn(i32) -> _>;
- | ^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
+LL | let _ = type_ascribe!(box { |x| (x as u8) }, Box<dyn Fn(i32) -> _>);
+ | ^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
|
= note: expected struct `Box<dyn Fn(i32) -> u8>`
- found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:13:19: 13:22]>`
+ found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:13:33: 13:36]>`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:14:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:14:27
|
-LL | let _ = box if true { false } else { true }: Box<dyn Debug>;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool`
+LL | let _ = type_ascribe!(box if true { false } else { true }, Box<dyn Debug>);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool`
|
= note: expected struct `Box<dyn Debug>`
found struct `Box<bool>`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:15:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:15:27
|
-LL | let _ = box match true { true => 'a', false => 'b' }: Box<dyn Debug>;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char`
+LL | let _ = type_ascribe!(box match true { true => 'a', false => 'b' }, Box<dyn Debug>);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char`
|
= note: expected struct `Box<dyn Debug>`
found struct `Box<char>`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:17:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:17:27
|
-LL | let _ = &{ [1, 2, 3] }: &[i32];
- | ^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL | let _ = type_ascribe!(&{ [1, 2, 3] }, &[i32]);
+ | ^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
|
= note: expected reference `&[i32]`
found reference `&[i32; 3]`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:18:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:18:27
|
-LL | let _ = &if true { [1, 2, 3] } else { [1, 3, 4] }: &[i32];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL | let _ = type_ascribe!(&if true { [1, 2, 3] } else { [1, 3, 4] }, &[i32]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
|
= note: expected reference `&[i32]`
found reference `&[i32; 3]`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:19:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:19:27
|
-LL | let _ = &match true { true => [1, 2, 3], false => [1, 3, 4] }: &[i32];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL | let _ = type_ascribe!(&match true { true => [1, 2, 3], false => [1, 3, 4] }, &[i32]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
|
= note: expected reference `&[i32]`
found reference `&[i32; 3]`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:21:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:21:27
|
-LL | let _ = &{ |x| (x as u8) }: &dyn Fn(i32) -> _;
- | ^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
+LL | let _ = type_ascribe!(&{ |x| (x as u8) }, &dyn Fn(i32) -> _);
+ | ^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
|
= note: expected reference `&dyn Fn(i32) -> u8`
- found reference `&[closure@$DIR/coerce-expect-unsized-ascribed.rs:21:16: 21:19]`
+ found reference `&[closure@$DIR/coerce-expect-unsized-ascribed.rs:21:30: 21:33]`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:22:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:22:27
|
-LL | let _ = &if true { false } else { true }: &dyn Debug;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool`
+LL | let _ = type_ascribe!(&if true { false } else { true }, &dyn Debug);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `bool`
|
= note: expected reference `&dyn Debug`
found reference `&bool`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:23:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:23:27
|
-LL | let _ = &match true { true => 'a', false => 'b' }: &dyn Debug;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char`
+LL | let _ = type_ascribe!(&match true { true => 'a', false => 'b' }, &dyn Debug);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Debug`, found `char`
|
= note: expected reference `&dyn Debug`
found reference `&char`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:25:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:25:27
|
-LL | let _ = Box::new([1, 2, 3]): Box<[i32]>;
- | ^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
+LL | let _ = type_ascribe!(Box::new([1, 2, 3]), Box<[i32]>);
+ | ^^^^^^^^^^^^^^^^^^^ expected slice `[i32]`, found array `[i32; 3]`
|
= note: expected struct `Box<[i32]>`
found struct `Box<[i32; 3]>`
error[E0308]: mismatched types
- --> $DIR/coerce-expect-unsized-ascribed.rs:26:13
+ --> $DIR/coerce-expect-unsized-ascribed.rs:26:27
|
-LL | let _ = Box::new(|x| (x as u8)): Box<dyn Fn(i32) -> _>;
- | ^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
+LL | let _ = type_ascribe!(Box::new(|x| (x as u8)), Box<dyn Fn(i32) -> _>);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found closure
|
= note: expected struct `Box<dyn Fn(i32) -> u8>`
- found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:26:22: 26:25]>`
+ found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:26:36: 26:39]>`
error: aborting due to 14 previous errors
--> $DIR/cfg-attr-syntax-validation.rs:7:1
|
LL | #[cfg()]
- | ^^^^^^^^
+ | ^^^^^^^^ help: expected syntax is: `cfg(/* predicate */)`
error: multiple `cfg` predicates are specified
--> $DIR/cfg-attr-syntax-validation.rs:10:10
LL | 1_u32
| ----- return type was inferred to be `u32` here
|
- = help: the following other types implement trait `Traitor<N, M>`:
- <u32 as Traitor<N, 2>>
- <u64 as Traitor<1, 2>>
+ = help: the trait `Traitor<N, 2>` is implemented for `u32`
error[E0277]: the trait bound `u64: Traitor` is not satisfied
--> $DIR/rp_impl_trait_fail.rs:21:13
LL | 1_u64
| ----- return type was inferred to be `u64` here
|
- = help: the following other types implement trait `Traitor<N, M>`:
- <u32 as Traitor<N, 2>>
- <u64 as Traitor<1, 2>>
+ = help: the trait `Traitor<1, 2>` is implemented for `u64`
error: aborting due to 3 previous errors
--- /dev/null
+trait Foo<const M: u8, const M: u8 = M> {}
+//~^ ERROR the name `M` is already used for a generic parameter in this item's generic parameters
+impl Foo<2> for () {}
+fn main() {}
--- /dev/null
+error[E0403]: the name `M` is already used for a generic parameter in this item's generic parameters
+ --> $DIR/self-referential.rs:1:30
+ |
+LL | trait Foo<const M: u8, const M: u8 = M> {}
+ | - ^ already used
+ | |
+ | first use of `M`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0403`.
--> $DIR/abstract-const-as-cast-3.rs:14:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error[E0308]: mismatched types
--> $DIR/abstract-const-as-cast-3.rs:17:5
--> $DIR/abstract-const-as-cast-3.rs:14:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error: unconstrained generic constant
--> $DIR/abstract-const-as-cast-3.rs:20:19
--> $DIR/abstract-const-as-cast-3.rs:14:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error[E0308]: mismatched types
--> $DIR/abstract-const-as-cast-3.rs:20:5
--> $DIR/abstract-const-as-cast-3.rs:14:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error[E0308]: mismatched types
--> $DIR/abstract-const-as-cast-3.rs:23:5
--> $DIR/abstract-const-as-cast-3.rs:14:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error[E0308]: mismatched types
--> $DIR/abstract-const-as-cast-3.rs:25:5
--> $DIR/abstract-const-as-cast-3.rs:14:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error: unconstrained generic constant
--> $DIR/abstract-const-as-cast-3.rs:35:19
--> $DIR/abstract-const-as-cast-3.rs:32:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error[E0308]: mismatched types
--> $DIR/abstract-const-as-cast-3.rs:35:5
--> $DIR/abstract-const-as-cast-3.rs:32:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error: unconstrained generic constant
--> $DIR/abstract-const-as-cast-3.rs:38:19
--> $DIR/abstract-const-as-cast-3.rs:32:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error[E0308]: mismatched types
--> $DIR/abstract-const-as-cast-3.rs:38:5
--> $DIR/abstract-const-as-cast-3.rs:32:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error[E0308]: mismatched types
--> $DIR/abstract-const-as-cast-3.rs:41:5
--> $DIR/abstract-const-as-cast-3.rs:32:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error[E0308]: mismatched types
--> $DIR/abstract-const-as-cast-3.rs:43:5
--> $DIR/abstract-const-as-cast-3.rs:32:23
|
LL | fn assert_impl<T: Trait>() {}
- | ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
+ | ^^^^^ required by this bound in `assert_impl`
error: aborting due to 12 previous errors
--- /dev/null
+#![feature(generic_const_exprs, generic_arg_infer)]
+#![allow(incomplete_features)]
+
+// minimized repro for #105205
+//
+// the `foo::<_, L>` call results in a `WellFormed(_)` obligation and a
+// `ConstEvaluatable(Unevaluated(_ + 1 + L))` obligation. Attempting to fulfill the latter
+// unifies the `_` with `Expr(L - 1)` from the paramenv which turns the `WellFormed`
+// obligation into `WellFormed(Expr(L - 1))`
+
+fn foo<const N: usize, const M: usize>(_: [(); N + 1 + M]) {}
+
+fn ice<const L: usize>()
+where
+ [(); (L - 1) + 1 + L]:,
+{
+ foo::<_, L>([(); L + 1 + L]);
+ //~^ ERROR: mismatched types
+ //~^^ ERROR: unconstrained generic constant
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/wf_obligation.rs:17:17
+ |
+LL | foo::<_, L>([(); L + 1 + L]);
+ | ^^^^^^^^^^^^^^^ expected `N + 1 + M`, found `L + 1 + L`
+ |
+ = note: expected constant `N + 1 + M`
+ found constant `L + 1 + L`
+
+error: unconstrained generic constant
+ --> $DIR/wf_obligation.rs:17:22
+ |
+LL | foo::<_, L>([(); L + 1 + L]);
+ | ^^^^^^^^^
+ |
+ = help: try adding a `where` bound using this expression: `where [(); L + 1 + L]:`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+trait Trait<T> {
+ fn fnc<const N: usize = "">(&self) {} //~ERROR defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+ fn foo<const N: usize = { std::mem::size_of::<T>() }>(&self) {} //~ERROR defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+}
+
+fn main() {}
--- /dev/null
+error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+ --> $DIR/issue-105257.rs:5:12
+ |
+LL | fn fnc<const N: usize = "">(&self) {}
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+ --> $DIR/issue-105257.rs:6:12
+ |
+LL | fn foo<const N: usize = { std::mem::size_of::<T>() }>(&self) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
--> $DIR/issue-79518-default_trait_method_normalization.rs:16:32
|
LL | Self::AssocInstance == [(); std::mem::size_of::<Self::Assoc>()];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found array `[(); _]`
+ | ------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found array `[(); _]`
+ | |
+ | expected because this is `<Self as Foo>::Assoc`
|
= note: expected associated type `<Self as Foo>::Assoc`
found array `[(); _]`
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | intrinsics::size_of::<T>()
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | size_of called on unsized type `dyn Debug`
- | inside `std::mem::size_of::<dyn Debug>` at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ size_of called on unsized type `dyn Debug`
+ |
+note: inside `std::mem::size_of::<dyn Debug>`
+ --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
- ::: $DIR/issue-80742.rs:22:10
+LL | intrinsics::size_of::<T>()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `Inline::<dyn Debug>::{constant#0}`
+ --> $DIR/issue-80742.rs:22:10
|
LL | [u8; size_of::<T>() + 1]: ,
- | -------------- inside `Inline::<dyn Debug>::{constant#0}` at $DIR/issue-80742.rs:22:10
+ | ^^^^^^^^^^^^^^
error[E0599]: the function or associated item `new` exists for struct `Inline<dyn Debug>`, but its trait bounds were not satisfied
--> $DIR/issue-80742.rs:30:36
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | intrinsics::size_of::<T>()
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | size_of called on unsized type `dyn Debug`
- | inside `std::mem::size_of::<dyn Debug>` at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ size_of called on unsized type `dyn Debug`
+ |
+note: inside `std::mem::size_of::<dyn Debug>`
+ --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
- ::: $DIR/issue-80742.rs:14:10
+LL | intrinsics::size_of::<T>()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `Inline::<dyn Debug>::{constant#0}`
+ --> $DIR/issue-80742.rs:14:10
|
LL | [u8; size_of::<T>() + 1]: ,
- | -------------- inside `Inline::<dyn Debug>::{constant#0}` at $DIR/issue-80742.rs:14:10
+ | ^^^^^^^^^^^^^^
error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time
--> $DIR/issue-80742.rs:30:15
--- /dev/null
+// check-pass
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features, unused_braces)]
+
+#[rustfmt::skip]
+fn foo<const N: usize>() {
+ bar::<{{{{{{ N }}}}}}>();
+}
+
+fn bar<const N: usize>() {}
+
+fn main() {}
error[E0080]: evaluation of constant value failed
--> $DIR/issue-100313.rs:10:13
|
+LL | *(B as *const bool as *mut bool) = false;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ writing to alloc7 which is read-only
+ |
+note: inside `T::<&true>::set_false`
+ --> $DIR/issue-100313.rs:10:13
+ |
LL | *(B as *const bool as *mut bool) = false;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | writing to alloc7 which is read-only
- | inside `T::<&true>::set_false` at $DIR/issue-100313.rs:10:13
-...
+note: inside `_`
+ --> $DIR/issue-100313.rs:18:5
+ |
LL | x.set_false();
- | ------------- inside `_` at $DIR/issue-100313.rs:18:5
+ | ^^^^^^^^^^^^^
error: aborting due to previous error
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
LL | &*ptr::slice_from_raw_parts(data, len)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
- | inside `std::slice::from_raw_parts::<'_, u32>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
+ |
+note: inside `std::slice::from_raw_parts::<'_, u32>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:18:34
+LL | &*ptr::slice_from_raw_parts(data, len)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `S0`
+ --> $DIR/forbidden_slices.rs:18:34
|
LL | pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) };
- | ------------------------------ inside `S0` at $DIR/forbidden_slices.rs:18:34
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
LL | &*ptr::slice_from_raw_parts(data, len)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
- | inside `std::slice::from_raw_parts::<'_, ()>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
+ |
+note: inside `std::slice::from_raw_parts::<'_, ()>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:19:33
+LL | &*ptr::slice_from_raw_parts(data, len)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `S1`
+ --> $DIR/forbidden_slices.rs:19:33
|
LL | pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) };
- | ------------------------------ inside `S1` at $DIR/forbidden_slices.rs:19:33
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
LL | &*ptr::slice_from_raw_parts(data, len)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | dereferencing pointer failed: ALLOC_ID has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
- | inside `std::slice::from_raw_parts::<'_, u32>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC_ID has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
+ |
+note: inside `std::slice::from_raw_parts::<'_, u32>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:22:34
+LL | &*ptr::slice_from_raw_parts(data, len)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `S2`
+ --> $DIR/forbidden_slices.rs:22:34
|
LL | pub static S2: &[u32] = unsafe { from_raw_parts(&D0, 2) };
- | ---------------------- inside `S2` at $DIR/forbidden_slices.rs:22:34
+ | ^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:25:1
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
LL | &*ptr::slice_from_raw_parts(data, len)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | dereferencing pointer failed: ALLOC_ID has size 8, so pointer to 8 bytes starting at offset 1 is out-of-bounds
- | inside `std::slice::from_raw_parts::<'_, u64>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC_ID has size 8, so pointer to 8 bytes starting at offset 1 is out-of-bounds
|
- ::: $DIR/forbidden_slices.rs:43:5
+note: inside `std::slice::from_raw_parts::<'_, u64>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
+ |
+LL | &*ptr::slice_from_raw_parts(data, len)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `S8`
+ --> $DIR/forbidden_slices.rs:43:5
|
LL | from_raw_parts(ptr, 1)
- | ---------------------- inside `S8` at $DIR/forbidden_slices.rs:43:5
+ | ^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
- | inside `ptr::const_ptr::<impl *const u32>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
|
- ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const u32>::sub_ptr`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
-LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
- | ------------------------------ inside `from_ptr_range::<'_, u32>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `from_ptr_range::<'_, u32>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:46:34
+LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R0`
+ --> $DIR/forbidden_slices.rs:46:34
|
LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
- | ---------------------------------------- inside `R0` at $DIR/forbidden_slices.rs:46:34
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | the evaluated program panicked at 'assertion failed: 0 < pointee_size && pointee_size <= isize::MAX as usize', $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- | inside `ptr::const_ptr::<impl *const ()>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: 0 < pointee_size && pointee_size <= isize::MAX as usize', $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const ()>::sub_ptr`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
-LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
- | ------------------------------ inside `from_ptr_range::<'_, ()>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+LL | assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `from_ptr_range::<'_, ()>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:47:33
+LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R1`
+ --> $DIR/forbidden_slices.rs:47:33
|
LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
- | ---------------------------------------- inside `R1` at $DIR/forbidden_slices.rs:47:33
- |
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC_ID has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
+ |
+note: inside `ptr::const_ptr::<impl *const u32>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
LL | unsafe { intrinsics::offset(self, count) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: ALLOC_ID has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
- | inside `ptr::const_ptr::<impl *const u32>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-...
-LL | unsafe { self.offset(count as isize) }
- | --------------------------- inside `ptr::const_ptr::<impl *const u32>::add` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const u32>::add`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:50:25
+LL | unsafe { self.offset(count as isize) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R2`
+ --> $DIR/forbidden_slices.rs:50:25
|
LL | from_ptr_range(ptr..ptr.add(2))
- | ---------- inside `R2` at $DIR/forbidden_slices.rs:50:25
+ | ^^^^^^^^^^
error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:52:1
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC_ID has size 8, so pointer to 8 bytes starting at offset 1 is out-of-bounds
+ |
+note: inside `ptr::const_ptr::<impl *const u64>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
LL | unsafe { intrinsics::offset(self, count) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: ALLOC_ID has size 8, so pointer to 8 bytes starting at offset 1 is out-of-bounds
- | inside `ptr::const_ptr::<impl *const u64>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-...
-LL | unsafe { self.offset(count as isize) }
- | --------------------------- inside `ptr::const_ptr::<impl *const u64>::add` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const u64>::add`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:74:25
+LL | unsafe { self.offset(count as isize) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R8`
+ --> $DIR/forbidden_slices.rs:74:25
|
LL | from_ptr_range(ptr..ptr.add(1))
- | ---------- inside `R8` at $DIR/forbidden_slices.rs:74:25
+ | ^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | `ptr_offset_from_unsigned` called on pointers into different allocations
- | inside `ptr::const_ptr::<impl *const u32>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on pointers into different allocations
|
- ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const u32>::sub_ptr`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
-LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
- | ------------------------------ inside `from_ptr_range::<'_, u32>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `from_ptr_range::<'_, u32>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:79:34
+LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R9`
+ --> $DIR/forbidden_slices.rs:79:34
|
LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) };
- | ----------------------------------------------- inside `R9` at $DIR/forbidden_slices.rs:79:34
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | `ptr_offset_from_unsigned` called on pointers into different allocations
- | inside `ptr::const_ptr::<impl *const u32>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on pointers into different allocations
|
- ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const u32>::sub_ptr`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
-LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
- | ------------------------------ inside `from_ptr_range::<'_, u32>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `from_ptr_range::<'_, u32>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:80:35
+LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R10`
+ --> $DIR/forbidden_slices.rs:80:35
|
LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) };
- | ------------------------ inside `R10` at $DIR/forbidden_slices.rs:80:35
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 18 previous errors
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
LL | &*ptr::slice_from_raw_parts(data, len)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
- | inside `std::slice::from_raw_parts::<'_, u32>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
+ |
+note: inside `std::slice::from_raw_parts::<'_, u32>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:18:34
+LL | &*ptr::slice_from_raw_parts(data, len)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `S0`
+ --> $DIR/forbidden_slices.rs:18:34
|
LL | pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) };
- | ------------------------------ inside `S0` at $DIR/forbidden_slices.rs:18:34
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
LL | &*ptr::slice_from_raw_parts(data, len)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
- | inside `std::slice::from_raw_parts::<'_, ()>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
+ |
+note: inside `std::slice::from_raw_parts::<'_, ()>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:19:33
+LL | &*ptr::slice_from_raw_parts(data, len)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `S1`
+ --> $DIR/forbidden_slices.rs:19:33
|
LL | pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) };
- | ------------------------------ inside `S1` at $DIR/forbidden_slices.rs:19:33
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
LL | &*ptr::slice_from_raw_parts(data, len)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | dereferencing pointer failed: ALLOC_ID has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
- | inside `std::slice::from_raw_parts::<'_, u32>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC_ID has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
+ |
+note: inside `std::slice::from_raw_parts::<'_, u32>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:22:34
+LL | &*ptr::slice_from_raw_parts(data, len)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `S2`
+ --> $DIR/forbidden_slices.rs:22:34
|
LL | pub static S2: &[u32] = unsafe { from_raw_parts(&D0, 2) };
- | ---------------------- inside `S2` at $DIR/forbidden_slices.rs:22:34
+ | ^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:25:1
--> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
LL | &*ptr::slice_from_raw_parts(data, len)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | dereferencing pointer failed: ALLOC_ID has size 8, so pointer to 8 bytes starting at offset 1 is out-of-bounds
- | inside `std::slice::from_raw_parts::<'_, u64>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC_ID has size 8, so pointer to 8 bytes starting at offset 1 is out-of-bounds
|
- ::: $DIR/forbidden_slices.rs:43:5
+note: inside `std::slice::from_raw_parts::<'_, u64>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
+ |
+LL | &*ptr::slice_from_raw_parts(data, len)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `S8`
+ --> $DIR/forbidden_slices.rs:43:5
|
LL | from_raw_parts(ptr, 1)
- | ---------------------- inside `S8` at $DIR/forbidden_slices.rs:43:5
+ | ^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
- | inside `ptr::const_ptr::<impl *const u32>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
|
- ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const u32>::sub_ptr`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
-LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
- | ------------------------------ inside `from_ptr_range::<'_, u32>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `from_ptr_range::<'_, u32>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:46:34
+LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R0`
+ --> $DIR/forbidden_slices.rs:46:34
|
LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
- | ---------------------------------------- inside `R0` at $DIR/forbidden_slices.rs:46:34
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | the evaluated program panicked at 'assertion failed: 0 < pointee_size && pointee_size <= isize::MAX as usize', $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
- | inside `ptr::const_ptr::<impl *const ()>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: 0 < pointee_size && pointee_size <= isize::MAX as usize', $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const ()>::sub_ptr`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
-LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
- | ------------------------------ inside `from_ptr_range::<'_, ()>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+LL | assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `from_ptr_range::<'_, ()>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:47:33
+LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R1`
+ --> $DIR/forbidden_slices.rs:47:33
|
LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
- | ---------------------------------------- inside `R1` at $DIR/forbidden_slices.rs:47:33
- |
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC_ID has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
+ |
+note: inside `ptr::const_ptr::<impl *const u32>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
LL | unsafe { intrinsics::offset(self, count) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: ALLOC_ID has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
- | inside `ptr::const_ptr::<impl *const u32>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-...
-LL | unsafe { self.offset(count as isize) }
- | --------------------------- inside `ptr::const_ptr::<impl *const u32>::add` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const u32>::add`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:50:25
+LL | unsafe { self.offset(count as isize) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R2`
+ --> $DIR/forbidden_slices.rs:50:25
|
LL | from_ptr_range(ptr..ptr.add(2))
- | ---------- inside `R2` at $DIR/forbidden_slices.rs:50:25
+ | ^^^^^^^^^^
error[E0080]: it is undefined behavior to use this value
--> $DIR/forbidden_slices.rs:52:1
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC_ID has size 8, so pointer to 8 bytes starting at offset 1 is out-of-bounds
+ |
+note: inside `ptr::const_ptr::<impl *const u64>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
LL | unsafe { intrinsics::offset(self, count) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: ALLOC_ID has size 8, so pointer to 8 bytes starting at offset 1 is out-of-bounds
- | inside `ptr::const_ptr::<impl *const u64>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-...
-LL | unsafe { self.offset(count as isize) }
- | --------------------------- inside `ptr::const_ptr::<impl *const u64>::add` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const u64>::add`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:74:25
+LL | unsafe { self.offset(count as isize) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R8`
+ --> $DIR/forbidden_slices.rs:74:25
|
LL | from_ptr_range(ptr..ptr.add(1))
- | ---------- inside `R8` at $DIR/forbidden_slices.rs:74:25
+ | ^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | `ptr_offset_from_unsigned` called on pointers into different allocations
- | inside `ptr::const_ptr::<impl *const u32>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on pointers into different allocations
|
- ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const u32>::sub_ptr`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
-LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
- | ------------------------------ inside `from_ptr_range::<'_, u32>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `from_ptr_range::<'_, u32>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:79:34
+LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R9`
+ --> $DIR/forbidden_slices.rs:79:34
|
LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) };
- | ----------------------------------------------- inside `R9` at $DIR/forbidden_slices.rs:79:34
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: could not evaluate static initializer
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | `ptr_offset_from_unsigned` called on pointers into different allocations
- | inside `ptr::const_ptr::<impl *const u32>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on pointers into different allocations
|
- ::: $SRC_DIR/core/src/slice/raw.rs:LL:COL
+note: inside `ptr::const_ptr::<impl *const u32>::sub_ptr`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
-LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
- | ------------------------------ inside `from_ptr_range::<'_, u32>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL
+LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `from_ptr_range::<'_, u32>`
+ --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
|
- ::: $DIR/forbidden_slices.rs:80:35
+LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `R10`
+ --> $DIR/forbidden_slices.rs:80:35
|
LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) };
- | ------------------------ inside `R10` at $DIR/forbidden_slices.rs:80:35
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 18 previous errors
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | memory access failed: alloc5 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds
- | inside `std::ptr::read::<u32>` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: alloc5 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds
+ |
+note: inside `std::ptr::read::<u32>`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
- ::: $DIR/out_of_bounds_read.rs:12:33
+LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `_READ`
+ --> $DIR/out_of_bounds_read.rs:12:33
|
LL | const _READ: u32 = unsafe { ptr::read(PAST_END_PTR) };
- | ----------------------- inside `_READ` at $DIR/out_of_bounds_read.rs:12:33
+ | ^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | memory access failed: alloc5 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds
- | inside `std::ptr::read::<u32>` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: alloc5 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds
|
- ::: $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+note: inside `std::ptr::read::<u32>`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
-LL | unsafe { read(self) }
- | ---------- inside `ptr::const_ptr::<impl *const u32>::read` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `ptr::const_ptr::<impl *const u32>::read`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/out_of_bounds_read.rs:13:39
+LL | unsafe { read(self) }
+ | ^^^^^^^^^^
+note: inside `_CONST_READ`
+ --> $DIR/out_of_bounds_read.rs:13:39
|
LL | const _CONST_READ: u32 = unsafe { PAST_END_PTR.read() };
- | ------------------- inside `_CONST_READ` at $DIR/out_of_bounds_read.rs:13:39
+ | ^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | memory access failed: alloc5 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds
- | inside `std::ptr::read::<u32>` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: alloc5 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds
+ |
+note: inside `std::ptr::read::<u32>`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
- ::: $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
+LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `ptr::mut_ptr::<impl *mut u32>::read`
+ --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
|
LL | unsafe { read(self) }
- | ---------- inside `ptr::mut_ptr::<impl *mut u32>::read` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
- |
- ::: $DIR/out_of_bounds_read.rs:14:37
+ | ^^^^^^^^^^
+note: inside `_MUT_READ`
+ --> $DIR/out_of_bounds_read.rs:14:37
|
LL | const _MUT_READ: u32 = unsafe { (PAST_END_PTR as *mut u32).read() };
- | --------------------------------- inside `_MUT_READ` at $DIR/out_of_bounds_read.rs:14:37
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
|
= help: the trait `~const Add<u8>` is not implemented for `i8`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
<&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&i8 as Add<&i8>>
+ <i8 as Add<&i8>>
+ <i8 as Add>
error: aborting due to 2 previous errors
|
= help: the trait `~const Add<u8>` is not implemented for `i8`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
<&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&i8 as Add<&i8>>
+ <i8 as Add<&i8>>
+ <i8 as Add>
error[E0604]: only `u8` can be cast as `char`, not `i8`
--> $DIR/const-eval-overflow-4b.rs:22:13
error[E0080]: evaluation of constant value failed
--> $DIR/const_fn_ptr_fail2.rs:9:5
|
+LL | x(y)
+ | ^^^^ calling non-const function `double`
+ |
+note: inside `bar`
+ --> $DIR/const_fn_ptr_fail2.rs:9:5
+ |
LL | x(y)
| ^^^^
- | |
- | calling non-const function `double`
- | inside `bar` at $DIR/const_fn_ptr_fail2.rs:9:5
-...
+note: inside `Y`
+ --> $DIR/const_fn_ptr_fail2.rs:14:18
+ |
LL | const Y: usize = bar(X, 2); // FIXME: should fail to typeck someday
- | --------- inside `Y` at $DIR/const_fn_ptr_fail2.rs:14:18
+ | ^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $DIR/const_fn_ptr_fail2.rs:9:5
|
+LL | x(y)
+ | ^^^^ calling non-const function `double`
+ |
+note: inside `bar`
+ --> $DIR/const_fn_ptr_fail2.rs:9:5
+ |
LL | x(y)
| ^^^^
- | |
- | calling non-const function `double`
- | inside `bar` at $DIR/const_fn_ptr_fail2.rs:9:5
-...
+note: inside `Z`
+ --> $DIR/const_fn_ptr_fail2.rs:15:18
+ |
LL | const Z: usize = bar(double, 2); // FIXME: should fail to typeck someday
- | -------------- inside `Z` at $DIR/const_fn_ptr_fail2.rs:15:18
+ | ^^^^^^^^^^^^^^
warning: skipping const checks
|
error[E0080]: evaluation of constant value failed
--> $DIR/const_panic_track_caller.rs:15:5
|
+LL | b()
+ | ^^^ the evaluated program panicked at 'hey', $DIR/const_panic_track_caller.rs:15:5
+ |
+note: inside `c`
+ --> $DIR/const_panic_track_caller.rs:15:5
+ |
LL | b()
| ^^^
- | |
- | the evaluated program panicked at 'hey', $DIR/const_panic_track_caller.rs:15:5
- | inside `c` at $DIR/const_panic_track_caller.rs:15:5
-...
+note: inside `X`
+ --> $DIR/const_panic_track_caller.rs:21:16
+ |
LL | const X: u32 = c();
- | --- inside `X` at $DIR/const_panic_track_caller.rs:21:16
+ | ^^^
error: aborting due to previous error
error[E0080]: evaluation of constant value failed
--> $DIR/alloc_intrinsic_errors.rs:9:17
|
-LL | const FOO: i32 = foo();
- | ----- inside `FOO` at $DIR/alloc_intrinsic_errors.rs:6:18
-...
+LL | let _ = intrinsics::const_allocate(4, 3) as *mut i32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ align has to be a power of 2, `3` is not a power of 2
+ |
+note: inside `foo`
+ --> $DIR/alloc_intrinsic_errors.rs:9:17
+ |
LL | let _ = intrinsics::const_allocate(4, 3) as *mut i32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | align has to be a power of 2, `3` is not a power of 2
- | inside `foo` at $DIR/alloc_intrinsic_errors.rs:9:17
+note: inside `FOO`
+ --> $DIR/alloc_intrinsic_errors.rs:6:18
+ |
+LL | const FOO: i32 = foo();
+ | ^^^^^
error: aborting due to previous error
error[E0080]: evaluation of constant value failed
--> $DIR/unwind-abort.rs:4:5
|
+LL | panic!()
+ | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/unwind-abort.rs:4:5
+ |
+note: inside `foo`
+ --> $DIR/unwind-abort.rs:4:5
+ |
LL | panic!()
| ^^^^^^^^
- | |
- | the evaluated program panicked at 'explicit panic', $DIR/unwind-abort.rs:4:5
- | inside `foo` at $SRC_DIR/std/src/panic.rs:LL:COL
-...
-LL | const _: () = foo();
- | ----- inside `_` at $DIR/unwind-abort.rs:7:15
+note: inside `_`
+ --> $DIR/unwind-abort.rs:7:15
|
+LL | const _: () = foo();
+ | ^^^^^
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
error[E0080]: evaluation of constant value failed
--> $DIR/validate_uninhabited_zsts.rs:4:14
|
+LL | unsafe { std::mem::transmute(()) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
+ |
+note: inside `foo`
+ --> $DIR/validate_uninhabited_zsts.rs:4:14
+ |
LL | unsafe { std::mem::transmute(()) }
| ^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | transmuting to uninhabited type
- | inside `foo` at $DIR/validate_uninhabited_zsts.rs:4:14
-...
+note: inside `FOO`
+ --> $DIR/validate_uninhabited_zsts.rs:19:33
+ |
LL | const FOO: [empty::Empty; 3] = [foo(); 3];
- | ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:19:33
+ | ^^^^^
error[E0080]: it is undefined behavior to use this value
--> $DIR/validate_uninhabited_zsts.rs:21:1
error[E0080]: evaluation of constant value failed
--> $DIR/validate_uninhabited_zsts.rs:4:14
|
+LL | unsafe { std::mem::transmute(()) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
+ |
+note: inside `foo`
+ --> $DIR/validate_uninhabited_zsts.rs:4:14
+ |
LL | unsafe { std::mem::transmute(()) }
| ^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | transmuting to uninhabited type
- | inside `foo` at $DIR/validate_uninhabited_zsts.rs:4:14
-...
+note: inside `FOO`
+ --> $DIR/validate_uninhabited_zsts.rs:19:33
+ |
LL | const FOO: [empty::Empty; 3] = [foo(); 3];
- | ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:19:33
+ | ^^^^^
error[E0080]: it is undefined behavior to use this value
--> $DIR/validate_uninhabited_zsts.rs:21:1
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/num/f32.rs:LL:COL
|
+LL | panic!("const-eval error: cannot use f32::to_bits on a NaN")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
+ |
+note: inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32`
+ --> $SRC_DIR/core/src/num/f32.rs:LL:COL
+ |
LL | panic!("const-eval error: cannot use f32::to_bits on a NaN")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
- | inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32` at $SRC_DIR/core/src/panic.rs:LL:COL
-...
-LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
- | -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
+note: inside `core::f32::<impl f32>::to_bits`
+ --> $SRC_DIR/core/src/num/f32.rs:LL:COL
|
- ::: $DIR/const-float-bits-reject-conv.rs:28:30
+LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `f32::MASKED_NAN1`
+ --> $DIR/const-float-bits-reject-conv.rs:28:30
|
LL | const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
- | ------------------ inside `f32::MASKED_NAN1` at $DIR/const-float-bits-reject-conv.rs:28:30
- |
+ | ^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/num/f32.rs:LL:COL
|
+LL | panic!("const-eval error: cannot use f32::to_bits on a NaN")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
+ |
+note: inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32`
+ --> $SRC_DIR/core/src/num/f32.rs:LL:COL
+ |
LL | panic!("const-eval error: cannot use f32::to_bits on a NaN")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
- | inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32` at $SRC_DIR/core/src/panic.rs:LL:COL
-...
-LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
- | -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
+note: inside `core::f32::<impl f32>::to_bits`
+ --> $SRC_DIR/core/src/num/f32.rs:LL:COL
|
- ::: $DIR/const-float-bits-reject-conv.rs:30:30
+LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `f32::MASKED_NAN2`
+ --> $DIR/const-float-bits-reject-conv.rs:30:30
|
LL | const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
- | ------------------ inside `f32::MASKED_NAN2` at $DIR/const-float-bits-reject-conv.rs:30:30
- |
+ | ^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
note: erroneous constant used
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/num/f64.rs:LL:COL
|
+LL | panic!("const-eval error: cannot use f64::to_bits on a NaN")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
+ |
+note: inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64`
+ --> $SRC_DIR/core/src/num/f64.rs:LL:COL
+ |
LL | panic!("const-eval error: cannot use f64::to_bits on a NaN")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
- | inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64` at $SRC_DIR/core/src/panic.rs:LL:COL
-...
-LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
- | -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
+note: inside `core::f64::<impl f64>::to_bits`
+ --> $SRC_DIR/core/src/num/f64.rs:LL:COL
|
- ::: $DIR/const-float-bits-reject-conv.rs:50:30
+LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `f64::MASKED_NAN1`
+ --> $DIR/const-float-bits-reject-conv.rs:50:30
|
LL | const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
- | ------------------ inside `f64::MASKED_NAN1` at $DIR/const-float-bits-reject-conv.rs:50:30
- |
+ | ^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/num/f64.rs:LL:COL
|
+LL | panic!("const-eval error: cannot use f64::to_bits on a NaN")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
+ |
+note: inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64`
+ --> $SRC_DIR/core/src/num/f64.rs:LL:COL
+ |
LL | panic!("const-eval error: cannot use f64::to_bits on a NaN")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
- | inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64` at $SRC_DIR/core/src/panic.rs:LL:COL
-...
-LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
- | -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
+note: inside `core::f64::<impl f64>::to_bits`
+ --> $SRC_DIR/core/src/num/f64.rs:LL:COL
|
- ::: $DIR/const-float-bits-reject-conv.rs:52:30
+LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `f64::MASKED_NAN2`
+ --> $DIR/const-float-bits-reject-conv.rs:52:30
|
LL | const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
- | ------------------ inside `f64::MASKED_NAN2` at $DIR/const-float-bits-reject-conv.rs:52:30
- |
+ | ^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
note: erroneous constant used
error[E0080]: evaluation of constant value failed
--> $DIR/mut_ref_in_final_dynamic_check.rs:13:10
|
+LL | Some(&mut *(42 as *mut i32))
+ | ^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: 0x2a[noalloc] is a dangling pointer (it has no provenance)
+ |
+note: inside `helper`
+ --> $DIR/mut_ref_in_final_dynamic_check.rs:13:10
+ |
LL | Some(&mut *(42 as *mut i32))
| ^^^^^^^^^^^^^^^^^^^^^^
- | |
- | dereferencing pointer failed: 0x2a[noalloc] is a dangling pointer (it has no provenance)
- | inside `helper` at $DIR/mut_ref_in_final_dynamic_check.rs:13:10
-...
+note: inside `A`
+ --> $DIR/mut_ref_in_final_dynamic_check.rs:18:29
+ |
LL | const A: Option<&mut i32> = helper();
- | -------- inside `A` at $DIR/mut_ref_in_final_dynamic_check.rs:18:29
+ | ^^^^^^^^
error: encountered dangling pointer in final constant
--> $DIR/mut_ref_in_final_dynamic_check.rs:25:1
const TUPLE: (OND, OND) = (None, None);
match (None, None) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), };
- const TYPE_ASCRIPTION: OND = None: OND;
+ const TYPE_ASCRIPTION: OND = type_ascribe!(None, OND);
match None { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), };
const ARRAY: [OND; 2] = [None, None];
match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), };
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
- const TYPE_ASCRIPTION: OND = Some(NoDerive): OND;
+ const TYPE_ASCRIPTION: OND = type_ascribe!(Some(NoDerive), OND);
match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), };
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
--> $SRC_DIR/core/src/hint.rs:LL:COL
|
LL | intrinsics::unreachable()
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | entering unreachable code
- | inside `unreachable_unchecked` at $SRC_DIR/core/src/hint.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code
+ |
+note: inside `unreachable_unchecked`
+ --> $SRC_DIR/core/src/hint.rs:LL:COL
|
- ::: $DIR/const_unsafe_unreachable_ub.rs:6:18
+LL | intrinsics::unreachable()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `foo`
+ --> $DIR/const_unsafe_unreachable_ub.rs:6:18
|
LL | false => std::hint::unreachable_unchecked(),
- | ---------------------------------- inside `foo` at $DIR/const_unsafe_unreachable_ub.rs:6:18
-...
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `BAR`
+ --> $DIR/const_unsafe_unreachable_ub.rs:10:28
+ |
LL | const BAR: bool = unsafe { foo(false) };
- | ---------- inside `BAR` at $DIR/const_unsafe_unreachable_ub.rs:10:28
+ | ^^^^^^^^^^
error: aborting due to previous error
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | accessing memory with alignment 1, but alignment 4 is required
- | inside `std::ptr::read::<u32>` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment 1, but alignment 4 is required
|
- ::: $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+note: inside `std::ptr::read::<u32>`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
-LL | unsafe { read(self) }
- | ---------- inside `ptr::const_ptr::<impl *const u32>::read` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `ptr::const_ptr::<impl *const u32>::read`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/detect-extra-ub.rs:38:9
+LL | unsafe { read(self) }
+ | ^^^^^^^^^^
+note: inside `INNER`
+ --> $DIR/detect-extra-ub.rs:38:9
|
LL | ptr.read();
- | ---------- inside `INNER` at $DIR/detect-extra-ub.rs:38:9
+ | ^^^^^^^^^^
note: erroneous constant used
--> $DIR/detect-extra-ub.rs:32:5
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | unable to copy parts of a pointer from memory at ALLOC
- | inside `std::ptr::read::<u8>` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to copy parts of a pointer from memory at ALLOC
|
- ::: $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
+note: inside `std::ptr::read::<u8>`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
-LL | unsafe { read(self) }
- | ---------- inside `ptr::const_ptr::<impl *const u8>::read` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+LL | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `ptr::const_ptr::<impl *const u8>::read`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/issue-miri-1910.rs:8:5
+LL | unsafe { read(self) }
+ | ^^^^^^^^^^
+note: inside `C`
+ --> $DIR/issue-miri-1910.rs:8:5
|
LL | (&foo as *const _ as *const u8).add(one_and_a_half_pointers).read();
- | ------------------------------------------------------------------- inside `C` at $DIR/issue-miri-1910.rs:8:5
- |
- = help: this code performed an operation that depends on the underlying bytes representing a pointer
- = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
error[E0080]: could not evaluate static initializer
--> $DIR/abi-mismatch.rs:9:5
|
+LL | my_fn();
+ | ^^^^^^^ calling a function with calling convention C using calling convention Rust
+ |
+note: inside `call_rust_fn`
+ --> $DIR/abi-mismatch.rs:9:5
+ |
LL | my_fn();
| ^^^^^^^
- | |
- | calling a function with calling convention C using calling convention Rust
- | inside `call_rust_fn` at $DIR/abi-mismatch.rs:9:5
-...
+note: inside `VAL`
+ --> $DIR/abi-mismatch.rs:15:18
+ |
LL | static VAL: () = call_rust_fn(unsafe { std::mem::transmute(c_fn as extern "C" fn()) });
- | --------------------------------------------------------------------- inside `VAL` at $DIR/abi-mismatch.rs:15:18
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: skipping const checks
|
error[E0080]: evaluation of `<std::string::String as Bar<std::vec::Vec<u32>, std::string::String>>::F` failed
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
+LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling non-const function `<Vec<u32> as Drop>::drop`
+ |
+note: inside `std::ptr::drop_in_place::<Vec<u32>> - shim(Some(Vec<u32>))`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+ |
LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | calling non-const function `<Vec<u32> as Drop>::drop`
- | inside `std::ptr::drop_in_place::<Vec<u32>> - shim(Some(Vec<u32>))` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
- | inside `std::ptr::drop_in_place::<(Vec<u32>, u32)> - shim(Some((Vec<u32>, u32)))` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+note: inside `std::ptr::drop_in_place::<(Vec<u32>, u32)> - shim(Some((Vec<u32>, u32)))`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
- ::: $DIR/assoc_const.rs:12:31
+LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `<String as Bar<Vec<u32>, String>>::F`
+ --> $DIR/assoc_const.rs:12:31
|
LL | const F: u32 = (U::X, 42).1;
- | - inside `<String as Bar<Vec<u32>, String>>::F` at $DIR/assoc_const.rs:12:31
+ | ^
note: erroneous constant used
--> $DIR/assoc_const.rs:29:13
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | calling non-const function `<Vec<i32> as Drop>::drop`
- | inside `std::ptr::drop_in_place::<Vec<i32>> - shim(Some(Vec<i32>))` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling non-const function `<Vec<i32> as Drop>::drop`
+ |
+note: inside `std::ptr::drop_in_place::<Vec<i32>> - shim(Some(Vec<i32>))`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
- ::: $DIR/drop.rs:17:1
+LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `TEST_BAD`
+ --> $DIR/drop.rs:17:1
|
LL | };
- | - inside `TEST_BAD` at $DIR/drop.rs:17:1
+ | ^
warning: skipping const checks
|
--> $DIR/tls.rs:11:25
|
LL | unsafe { let _val = A; }
- | ^ cannot access thread local static (DefId(0:6 ~ tls[78b0]::A))
+ | ^ cannot access thread local static (DefId(0:4 ~ tls[78b0]::A))
error[E0080]: could not evaluate static initializer
--> $DIR/tls.rs:18:26
|
LL | unsafe { let _val = &A; }
- | ^ cannot access thread local static (DefId(0:6 ~ tls[78b0]::A))
+ | ^ cannot access thread local static (DefId(0:4 ~ tls[78b0]::A))
warning: skipping const checks
|
--- /dev/null
+// compile-flags: -Z simulate-remapped-rust-src-base=/rustc/FAKE_PREFIX -Z translate-remapped-path-to-local-path=no -Z ui-testing=no
+// normalize-stderr-test "alloc[0-9]+" -> "ALLOC_ID"
+
+#![feature(const_swap)]
+#![feature(const_mut_refs)]
+use std::{
+ mem::{self, MaybeUninit},
+ ptr,
+};
+
+const X: () = {
+ let mut ptr1 = &1;
+ let mut ptr2 = &2;
+
+ // Swap them, bytewise.
+ unsafe {
+ ptr::swap_nonoverlapping(
+ &mut ptr1 as *mut _ as *mut MaybeUninit<u8>,
+ &mut ptr2 as *mut _ as *mut MaybeUninit<u8>,
+ mem::size_of::<&i32>(),
+ );
+ }
+};
+
+fn main() {
+ X
+}
--- /dev/null
+error[E0080]: evaluation of constant value failed
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+ |
+ = note: unable to copy parts of a pointer from memory at ALLOC_ID
+ |
+ = help: this code performed an operation that depends on the underlying bytes representing a pointer
+ = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
+note: inside `std::ptr::read::<MaybeUninit<MaybeUninit<u8>>>`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+note: inside `mem::swap_simple::<MaybeUninit<MaybeUninit<u8>>>`
+ --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
+note: inside `ptr::swap_nonoverlapping_simple_untyped::<MaybeUninit<u8>>`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+note: inside `swap_nonoverlapping::<MaybeUninit<u8>>`
+ --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+note: inside `X`
+ --> $DIR/missing_span_in_backtrace.rs:17:9
+ |
+17 | / ptr::swap_nonoverlapping(
+18 | | &mut ptr1 as *mut _ as *mut MaybeUninit<u8>,
+19 | | &mut ptr2 as *mut _ as *mut MaybeUninit<u8>,
+20 | | mem::size_of::<&i32>(),
+21 | | );
+ | |_________^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | `ptr_offset_from` called on pointers into different allocations
- | inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on pointers into different allocations
+ |
+note: inside `ptr::const_ptr::<impl *const u8>::offset_from`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/offset_from_ub.rs:24:14
+LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `NOT_PTR`
+ --> $DIR/offset_from_ub.rs:24:14
|
LL | unsafe { (42 as *const u8).offset_from(&5u8) as usize }
- | ----------------------------------- inside `NOT_PTR` at $DIR/offset_from_ub.rs:24:14
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $DIR/offset_from_ub.rs:31:14
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
- | inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
|
- ::: $DIR/offset_from_ub.rs:115:14
+note: inside `ptr::const_ptr::<impl *const u8>::offset_from`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
+LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `OFFSET_VERY_FAR1`
+ --> $DIR/offset_from_ub.rs:115:14
|
LL | unsafe { ptr2.offset_from(ptr1) }
- | ---------------------- inside `OFFSET_VERY_FAR1` at $DIR/offset_from_ub.rs:115:14
+ | ^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
- | inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
|
- ::: $DIR/offset_from_ub.rs:121:14
+note: inside `ptr::const_ptr::<impl *const u8>::offset_from`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
+LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `OFFSET_VERY_FAR2`
+ --> $DIR/offset_from_ub.rs:121:14
|
LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
- | ----------------------------------------- inside `OFFSET_VERY_FAR2` at $DIR/offset_from_ub.rs:121:14
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 15 previous errors
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | overflowing in-bounds pointer arithmetic
- | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing in-bounds pointer arithmetic
+ |
+note: inside `ptr::const_ptr::<impl *const u8>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/offset_ub.rs:7:46
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `BEFORE_START`
+ --> $DIR/offset_ub.rs:7:46
|
LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) };
- | ------------------------------ inside `BEFORE_START` at $DIR/offset_ub.rs:7:46
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: allocN has size 1, so pointer to 2 bytes starting at offset 0 is out-of-bounds
- | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: allocN has size 1, so pointer to 2 bytes starting at offset 0 is out-of-bounds
|
- ::: $DIR/offset_ub.rs:8:43
+note: inside `ptr::const_ptr::<impl *const u8>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `AFTER_END`
+ --> $DIR/offset_ub.rs:8:43
|
LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) };
- | ----------------------------- inside `AFTER_END` at $DIR/offset_ub.rs:8:43
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: allocN has size 100, so pointer to 101 bytes starting at offset 0 is out-of-bounds
- | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: allocN has size 100, so pointer to 101 bytes starting at offset 0 is out-of-bounds
|
- ::: $DIR/offset_ub.rs:9:45
+note: inside `ptr::const_ptr::<impl *const u8>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `AFTER_ARRAY`
+ --> $DIR/offset_ub.rs:9:45
|
LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) };
- | ------------------------------- inside `AFTER_ARRAY` at $DIR/offset_ub.rs:9:45
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | overflowing in-bounds pointer arithmetic
- | inside `ptr::const_ptr::<impl *const u16>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing in-bounds pointer arithmetic
+ |
+note: inside `ptr::const_ptr::<impl *const u16>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/offset_ub.rs:11:43
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `OVERFLOW`
+ --> $DIR/offset_ub.rs:11:43
|
LL | pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) };
- | ------------------------------------- inside `OVERFLOW` at $DIR/offset_ub.rs:11:43
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | overflowing in-bounds pointer arithmetic
- | inside `ptr::const_ptr::<impl *const u16>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing in-bounds pointer arithmetic
|
- ::: $DIR/offset_ub.rs:12:44
+note: inside `ptr::const_ptr::<impl *const u16>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `UNDERFLOW`
+ --> $DIR/offset_ub.rs:12:44
|
LL | pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) };
- | ------------------------------------- inside `UNDERFLOW` at $DIR/offset_ub.rs:12:44
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | overflowing in-bounds pointer arithmetic
- | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing in-bounds pointer arithmetic
|
- ::: $DIR/offset_ub.rs:13:56
+note: inside `ptr::const_ptr::<impl *const u8>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `OVERFLOW_ADDRESS_SPACE`
+ --> $DIR/offset_ub.rs:13:56
|
LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) };
- | ----------------------------------- inside `OVERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:13:56
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | overflowing in-bounds pointer arithmetic
- | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflowing in-bounds pointer arithmetic
+ |
+note: inside `ptr::const_ptr::<impl *const u8>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/offset_ub.rs:14:57
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `UNDERFLOW_ADDRESS_SPACE`
+ --> $DIR/offset_ub.rs:14:57
|
LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) };
- | --------------------------- inside `UNDERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:14:57
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: allocN has size 1, so pointer to 2 bytes starting at offset -4 is out-of-bounds
- | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: allocN has size 1, so pointer to 2 bytes starting at offset -4 is out-of-bounds
|
- ::: $DIR/offset_ub.rs:15:49
+note: inside `ptr::const_ptr::<impl *const u8>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `NEGATIVE_OFFSET`
+ --> $DIR/offset_ub.rs:15:49
|
LL | pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offset(-2).offset(-2) };
- | ------------------------------------------------ inside `NEGATIVE_OFFSET` at $DIR/offset_ub.rs:15:49
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: allocN has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
- | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: allocN has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
|
- ::: $DIR/offset_ub.rs:17:50
+note: inside `ptr::const_ptr::<impl *const u8>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `ZERO_SIZED_ALLOC`
+ --> $DIR/offset_ub.rs:17:50
|
LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) };
- | --------------------------- inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:17:50
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) as *mut T }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: 0x1[noalloc] is a dangling pointer (it has no provenance)
- | inside `ptr::mut_ptr::<impl *mut u8>::offset` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: 0x1[noalloc] is a dangling pointer (it has no provenance)
+ |
+note: inside `ptr::mut_ptr::<impl *mut u8>::offset`
+ --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL
|
- ::: $DIR/offset_ub.rs:18:42
+LL | unsafe { intrinsics::offset(self, count) as *mut T }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `DANGLING`
+ --> $DIR/offset_ub.rs:18:42
|
LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::<u8>::dangling().as_ptr().offset(4) };
- | ------------------------------------------------- inside `DANGLING` at $DIR/offset_ub.rs:18:42
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance)
- | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance)
|
- ::: $DIR/offset_ub.rs:21:50
+note: inside `ptr::const_ptr::<impl *const u8>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `NULL_OFFSET_ZERO`
+ --> $DIR/offset_ub.rs:21:50
|
LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) };
- | --------------------------- inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:21:50
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: 0x7f..f[noalloc] is a dangling pointer (it has no provenance)
- | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: 0x7f..f[noalloc] is a dangling pointer (it has no provenance)
|
- ::: $DIR/offset_ub.rs:24:47
+note: inside `ptr::const_ptr::<impl *const u8>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ |
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `UNDERFLOW_ABS`
+ --> $DIR/offset_ub.rs:24:47
|
LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) };
- | -------------------------------------------- inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:24:47
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 12 previous errors
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
LL | unsafe { intrinsics::offset(self, count) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | out-of-bounds pointer arithmetic: alloc3 has size $WORD, so pointer to $TWO_WORDS bytes starting at offset 0 is out-of-bounds
- | inside `ptr::const_ptr::<impl *const usize>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: alloc3 has size $WORD, so pointer to $TWO_WORDS bytes starting at offset 0 is out-of-bounds
+ |
+note: inside `ptr::const_ptr::<impl *const usize>::offset`
+ --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
- ::: $DIR/ptr_comparisons.rs:50:34
+LL | unsafe { intrinsics::offset(self, count) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `_`
+ --> $DIR/ptr_comparisons.rs:50:34
|
LL | const _: *const usize = unsafe { (FOO as *const usize).offset(2) };
- | ------------------------------- inside `_` at $DIR/ptr_comparisons.rs:50:34
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0080]: evaluation of constant value failed
--> $DIR/ptr_comparisons.rs:53:33
error[E0080]: evaluation of constant value failed
--> $DIR/recursive.rs:4:5
|
+LL | f(x);
+ | ^^^^ reached the configured maximum number of stack frames
+ |
+note: inside `f::<i32>`
+ --> $DIR/recursive.rs:4:5
+ |
LL | f(x);
| ^^^^
- | |
- | reached the configured maximum number of stack frames
- | inside `f::<i32>` at $DIR/recursive.rs:4:5
- | [... 126 additional calls inside `f::<i32>` at $DIR/recursive.rs:4:5 ...]
-...
+note: [... 126 additional calls inside `f::<i32>` ...]
+ --> $DIR/recursive.rs:4:5
+ |
+LL | f(x);
+ | ^^^^
+note: inside `X`
+ --> $DIR/recursive.rs:8:15
+ |
LL | const X: () = f(1);
- | ---- inside `X` at $DIR/recursive.rs:8:15
+ | ^^^^
error: aborting due to previous error; 1 warning emitted
error[E0080]: evaluation of `<i32 as Const>::CONSTANT` failed
--> $DIR/uninhabited-const-issue-61744.rs:4:5
|
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^ reached the configured maximum number of stack frames
+ |
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
LL | hint_unreachable()
| ^^^^^^^^^^^^^^^^^^
- | |
- | reached the configured maximum number of stack frames
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:4:5
- | inside `fake_type::<i32>` at $DIR/uninhabited-const-issue-61744.rs:4:5
-...
-LL | fake_type()
- | -----------
- | |
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
- | inside `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:8:5
-...
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<!>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `hint_unreachable`
+ --> $DIR/uninhabited-const-issue-61744.rs:8:5
+ |
+LL | fake_type()
+ | ^^^^^^^^^^^
+note: inside `fake_type::<i32>`
+ --> $DIR/uninhabited-const-issue-61744.rs:4:5
+ |
+LL | hint_unreachable()
+ | ^^^^^^^^^^^^^^^^^^
+note: inside `<i32 as Const>::CONSTANT`
+ --> $DIR/uninhabited-const-issue-61744.rs:12:36
+ |
LL | const CONSTANT: i32 = unsafe { fake_type() };
- | ----------- inside `<i32 as Const>::CONSTANT` at $DIR/uninhabited-const-issue-61744.rs:12:36
+ | ^^^^^^^^^^^
note: erroneous constant used
--> $DIR/uninhabited-const-issue-61744.rs:18:10
--- /dev/null
+// run-pass
+// compile-flags: --edition 2021 -Copt-level=3 -Cdebuginfo=2 -Zmir-opt-level=3
+
+fn main() {
+ TranslatorI.visit_pre();
+}
+
+impl TranslatorI {
+ fn visit_pre(self) {
+ Some(())
+ .map(|_| self.flags())
+ .unwrap_or_else(|| self.flags());
+ }
+}
+
+struct TranslatorI;
+
+impl TranslatorI {
+ fn flags(&self) {}
+}
--- /dev/null
+// compile-flags: --diagnostic-width=60
+// normalize-stderr-test: "long-type-\d+" -> "long-type-hash"
+
+mod a {
+ // Force the "short path for unique types" machinery to trip up
+ pub struct Atype;
+ pub struct Btype;
+ pub struct Ctype;
+}
+
+mod b {
+ pub struct Atype<T, K>(T, K);
+ pub struct Btype<T, K>(T, K);
+ pub struct Ctype<T, K>(T, K);
+}
+
+use b::*;
+
+fn main() {
+ let x: Atype<
+ Btype<
+ Ctype<
+ Atype<
+ Btype<
+ Ctype<
+ Atype<
+ Btype<
+ Ctype<i32, i32>,
+ i32
+ >,
+ i32
+ >,
+ i32
+ >,
+ i32
+ >,
+ i32
+ >,
+ i32
+ >,
+ i32
+ >,
+ i32
+ > = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+ Ok("")
+ ))))))))))))))))))))))))))))))
+ ))))))))))))))))))))))))))))));
+ //~^^^^^ ERROR E0308
+
+ let _ = Some(Ok(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(
+ Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(
+ Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(
+ Some(Some(Some(Some(Some(Some(Some(Some(Some("")))))))))
+ )))))))))))))))))
+ ))))))))))))))))))
+ ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+ Ok(Ok(Ok(Ok(Ok(Ok(Ok("")))))))
+ ))))))))))))))))))))))))))))))
+ ))))))))))))))))))))))));
+ //~^^^^^ ERROR E0308
+
+ let x: Atype<
+ Btype<
+ Ctype<
+ Atype<
+ Btype<
+ Ctype<
+ Atype<
+ Btype<
+ Ctype<i32, i32>,
+ i32
+ >,
+ i32
+ >,
+ i32
+ >,
+ i32
+ >,
+ i32
+ >,
+ i32
+ >,
+ i32
+ >,
+ i32
+ > = ();
+ //~^ ERROR E0308
+
+ let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+ Ok(Ok(Ok(Ok(Ok(Ok(Ok("")))))))
+ ))))))))))))))))))))))))))))))
+ ))))))))))))))))))))))));
+ //~^^^^^ ERROR E0308
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/long-E0308.rs:44:9
+ |
+LL | let x: Atype<
+ | _____________-
+LL | | Btype<
+LL | | Ctype<
+LL | | Atype<
+... |
+LL | | i32
+LL | | > = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok...
+ | | _____-___^
+ | ||_____|
+ | | expected due to this
+LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok...
+LL | | Ok("")
+LL | | ))))))))))))))))))))))))))))))
+LL | | ))))))))))))))))))))))))))))));
+ | |__________________________________^ expected struct `Atype`, found enum `Result`
+ |
+ = note: expected struct `Atype<Btype<..., ...>, ...>`
+ the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308/long-E0308.long-type-hash.txt'
+ found enum `Result<Result<..., ...>, ...>`
+ the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308/long-E0308.long-type-hash.txt'
+
+error[E0308]: mismatched types
+ --> $DIR/long-E0308.rs:57:26
+ |
+LL | ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O...
+ | __________________________^
+LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(...
+LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok("")))))))
+LL | | ))))))))))))))))))))))))))))))
+LL | | ))))))))))))))))))))))));
+ | |____________________________^ expected enum `Option`, found enum `Result`
+ |
+ = note: expected enum `Option<Result<..., ...>>`
+ the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308/long-E0308.long-type-hash.txt'
+ found enum `Result<Result<..., ...>, ...>`
+ the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308/long-E0308.long-type-hash.txt'
+
+error[E0308]: mismatched types
+ --> $DIR/long-E0308.rs:88:9
+ |
+LL | let x: Atype<
+ | ____________-
+LL | | Btype<
+LL | | Ctype<
+LL | | Atype<
+... |
+LL | | i32
+LL | | > = ();
+ | | - ^^ expected struct `Atype`, found `()`
+ | |_____|
+ | expected due to this
+ |
+ = note: expected struct `Atype<Btype<..., ...>, ...>`
+ the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308/long-E0308.long-type-hash.txt'
+ found unit type `()`
+
+error[E0308]: mismatched types
+ --> $DIR/long-E0308.rs:91:17
+ |
+LL | let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O...
+ | ____________--___^
+ | | |
+ | | expected due to this
+LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(...
+LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok("")))))))
+LL | | ))))))))))))))))))))))))))))))
+LL | | ))))))))))))))))))))))));
+ | |____________________________^ expected `()`, found enum `Result`
+ |
+ = note: expected unit type `()`
+ found enum `Result<Result<..., ...>, ...>`
+ the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308/long-E0308.long-type-hash.txt'
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
<i8 as Foo<u32>>
<i8 as Foo<u64>>
<i8 as Foo<u8>>
- <u8 as Foo<bool>>
- <u8 as Foo<u16>>
- <u8 as Foo<u32>>
- <u8 as Foo<u64>>
error[E0277]: the trait bound `u8: Foo<i32>` is not satisfied
--> $DIR/issue-39802-show-5-trait-impls.rs:25:21
| required by a bound introduced by this call
|
= help: the following other types implement trait `Foo<B>`:
- <i8 as Foo<bool>>
- <i8 as Foo<u16>>
- <i8 as Foo<u32>>
- <i8 as Foo<u64>>
- <i8 as Foo<u8>>
<u8 as Foo<bool>>
<u8 as Foo<u16>>
<u8 as Foo<u32>>
--> $DIR/disambiguate-identical-names.rs:13:10
|
LL | test(&v);
- | ---- ^^ expected struct `std::vec::Vec`, found struct `HashMap`
+ | ---- ^^ expected struct `Vec`, found struct `HashMap`
| |
| arguments to this function are incorrect
|
}
if {
- if self.option_loud_drop(7).is_some() && self.option_loud_drop(6).is_some() {
+ if self.option_loud_drop(6).is_some() && self.option_loud_drop(7).is_some() {
self.loud_drop(8);
true
} else {
}
}
+ fn and_chain(&self) {
+ // issue-103107
+ if self.option_loud_drop(1).is_some() // 1
+ && self.option_loud_drop(2).is_some() // 2
+ && self.option_loud_drop(3).is_some() // 3
+ && self.option_loud_drop(4).is_some() // 4
+ && self.option_loud_drop(5).is_some() // 5
+ {
+ self.print(6); // 6
+ }
+
+ let _ = self.option_loud_drop(7).is_some() // 1
+ && self.option_loud_drop(8).is_some() // 2
+ && self.option_loud_drop(9).is_some(); // 3
+ self.print(10); // 4
+
+ // Test associativity
+ if self.option_loud_drop(11).is_some() // 1
+ && (self.option_loud_drop(12).is_some() // 2
+ && self.option_loud_drop(13).is_some() // 3
+ && self.option_loud_drop(14).is_some()) // 4
+ && self.option_loud_drop(15).is_some() // 5
+ {
+ self.print(16); // 6
+ }
+ }
+
+ fn or_chain(&self) {
+ // issue-103107
+ if self.option_loud_drop(1).is_none() // 1
+ || self.option_loud_drop(2).is_none() // 2
+ || self.option_loud_drop(3).is_none() // 3
+ || self.option_loud_drop(4).is_none() // 4
+ || self.option_loud_drop(5).is_some() // 5
+ {
+ self.print(6); // 6
+ }
+
+ let _ = self.option_loud_drop(7).is_none() // 1
+ || self.option_loud_drop(8).is_none() // 2
+ || self.option_loud_drop(9).is_none(); // 3
+ self.print(10); // 4
+
+ // Test associativity
+ if self.option_loud_drop(11).is_none() // 1
+ || (self.option_loud_drop(12).is_none() // 2
+ || self.option_loud_drop(13).is_none() // 3
+ || self.option_loud_drop(14).is_none()) // 4
+ || self.option_loud_drop(15).is_some() // 5
+ {
+ self.print(16); // 6
+ }
+ }
+
+ fn mixed_and_or_chain(&self) {
+ // issue-103107
+ if self.option_loud_drop(1).is_none() // 1
+ || self.option_loud_drop(2).is_none() // 2
+ || self.option_loud_drop(3).is_some() // 3
+ && self.option_loud_drop(4).is_some() // 4
+ && self.option_loud_drop(5).is_none() // 5
+ || self.option_loud_drop(6).is_none() // 6
+ || self.option_loud_drop(7).is_some() // 7
+ {
+ self.print(8); // 8
+ }
+ }
+
fn let_chain(&self) {
// take the "then" branch
- if self.option_loud_drop(2).is_some() // 2
- && self.option_loud_drop(1).is_some() // 1
+ if self.option_loud_drop(1).is_some() // 1
+ && self.option_loud_drop(2).is_some() // 2
&& let Some(_d) = self.option_loud_drop(4) { // 4
self.print(3); // 3
}
// take the "else" branch
- if self.option_loud_drop(6).is_some() // 2
- && self.option_loud_drop(5).is_some() // 1
+ if self.option_loud_drop(5).is_some() // 1
+ && self.option_loud_drop(6).is_some() // 2
&& let None = self.option_loud_drop(8) { // 4
unreachable!();
} else {
}
// let exprs last
- if self.option_loud_drop(20).is_some() // 2
- && self.option_loud_drop(19).is_some() // 1
+ if self.option_loud_drop(19).is_some() // 1
+ && self.option_loud_drop(20).is_some() // 2
&& let Some(_d) = self.option_loud_drop(23) // 5
&& let Some(_e) = self.option_loud_drop(22) { // 4
self.print(21); // 3
collector.if_();
collector.assert_sorted();
+ println!("-- and chain --");
+ let collector = DropOrderCollector::default();
+ collector.and_chain();
+ collector.assert_sorted();
+
+ println!("-- or chain --");
+ let collector = DropOrderCollector::default();
+ collector.or_chain();
+ collector.assert_sorted();
+
+ println!("-- mixed and/or chain --");
+ let collector = DropOrderCollector::default();
+ collector.mixed_and_or_chain();
+ collector.assert_sorted();
+
println!("-- if let --");
let collector = DropOrderCollector::default();
collector.if_let();
--- /dev/null
+// check-pass
+// compile-flags: -Z validate-mir
+
+struct Foo<'a>(&'a mut u32);
+
+impl<'a> Drop for Foo<'a> {
+ fn drop(&mut self) {
+ *self.0 = 0;
+ }
+}
+
+fn and() {
+ let mut foo = 0;
+ // This used to compile also before the fix
+ if true && *Foo(&mut foo).0 == 0 && ({ foo = 0; true}) {}
+
+ // This used to fail before the fix
+ if *Foo(&mut foo).0 == 0 && ({ foo = 0; true}) {}
+
+ println!("{foo}");
+}
+
+fn or() {
+ let mut foo = 0;
+ // This used to compile also before the fix
+ if false || *Foo(&mut foo).0 == 1 || ({ foo = 0; true}) {}
+
+ // This used to fail before the fix
+ if *Foo(&mut foo).0 == 1 || ({ foo = 0; true}) {}
+
+ println!("{foo}");
+}
+
+fn main() {
+ and();
+ or();
+}
| first use of `T`
error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
- --> $DIR/duplicate-type-parameter.rs:24:6
+ --> $DIR/duplicate-type-parameter.rs:24:8
|
LL | impl<T,T> Qux<T,T> for Option<T> {}
- | ^ unconstrained type parameter
+ | ^ unconstrained type parameter
error: aborting due to 8 previous errors
--> $DIR/no-implicit-dyn-star.rs:6:48
|
LL | dyn_star_foreign::require_dyn_star_display(1usize);
- | ------------------------------------------ ^^^^^^ expected trait object `dyn std::fmt::Display`, found `usize`
+ | ------------------------------------------ ^^^^^^ expected trait object `dyn Display`, found `usize`
| |
| arguments to this function are incorrect
|
--- /dev/null
+// run-pass
+#![allow(dead_code)]
+
+enum OpenResult {
+ Ok(()),
+ Err(()),
+ TransportErr(TransportErr),
+}
+
+#[repr(i32)]
+enum TransportErr {
+ UnknownMethod = -2,
+}
+
+#[inline(never)]
+fn some_match(result: OpenResult) -> u8 {
+ match result {
+ OpenResult::Ok(()) => 0,
+ _ => 1,
+ }
+}
+
+fn main() {
+ let result = OpenResult::Ok(());
+ assert_eq!(some_match(result), 0);
+
+ let result = OpenResult::Ok(());
+ match result {
+ OpenResult::Ok(()) => (),
+ _ => unreachable!("message a"),
+ }
+ match result {
+ OpenResult::Ok(()) => (),
+ _ => unreachable!("message b"),
+ }
+}
#![feature(type_ascription)]
enum Bug<S> { //~ ERROR parameter `S` is never used
- Var = 0: S,
+ Var = type_ascribe!(0, S),
//~^ ERROR generic parameters may not be used
}
error: generic parameters may not be used in const operations
- --> $DIR/issue-67945-2.rs:4:14
+ --> $DIR/issue-67945-2.rs:4:28
|
-LL | Var = 0: S,
- | ^ cannot perform const operation using `S`
+LL | Var = type_ascribe!(0, S),
+ | ^ cannot perform const operation using `S`
|
= note: type parameters may not be used in const expressions
= help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
| ^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`E0275`)
-note: required for `Bar<Bar<Bar<Bar<Bar<Bar<...>>>>>>` to implement `Foo`
+note: required for `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<...>>>>>>>>>>>>>>>>>>>>>` to implement `Foo`
--> $DIR/E0275.rs:6:9
|
LL | impl<T> Foo for T where Bar<T>: Foo {}
|
LL | fn f() -> impl Fn() -> impl Sized { || () }
| ^^^^^^^^^^
+ |
+ = note: see issue #99697 <https://github.com/rust-lang/rust/issues/99697> for more information
+ = help: add `#![feature(impl_trait_in_fn_trait_return)]` to the crate attributes to enable
error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in `Fn` trait return
--> $DIR/feature-gate-impl_trait_in_fn_trait_return.rs:3:32
|
LL | fn g() -> &'static dyn Fn() -> impl Sized { &|| () }
| ^^^^^^^^^^
+ |
+ = note: see issue #99697 <https://github.com/rust-lang/rust/issues/99697> for more information
+ = help: add `#![feature(impl_trait_in_fn_trait_return)]` to the crate attributes to enable
error: aborting due to 2 previous errors
extern "C" {
- #[linkage = "extern_weak"] static foo: isize;
+ #[linkage = "extern_weak"] static foo: *mut isize;
//~^ ERROR: the `linkage` attribute is experimental and not portable
}
error[E0658]: the `linkage` attribute is experimental and not portable across platforms
--> $DIR/feature-gate-linkage.rs:2:5
|
-LL | #[linkage = "extern_weak"] static foo: isize;
+LL | #[linkage = "extern_weak"] static foo: *mut isize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #29603 <https://github.com/rust-lang/rust/issues/29603> for more information
--- /dev/null
+// compile-flags: -Zverbose
+
+fn foo(_: i32, _: i32) {}
+
+fn needs_ptr(_: fn(i32, u32)) {}
+//~^ NOTE function defined here
+//~| NOTE
+
+fn main() {
+ needs_ptr(foo);
+ //~^ ERROR mismatched types
+ //~| NOTE expected `u32`, found `i32`
+ //~| NOTE expected fn pointer `fn(i32, u32)`
+ //~| NOTE arguments to this function are incorrect
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/signature-error-reporting-under-verbose.rs:10:15
+ |
+LL | needs_ptr(foo);
+ | --------- ^^^ expected `u32`, found `i32`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected fn pointer `fn(i32, u32)`
+ found fn item `fn(i32, i32) {foo}`
+note: function defined here
+ --> $DIR/signature-error-reporting-under-verbose.rs:5:4
+ |
+LL | fn needs_ptr(_: fn(i32, u32)) {}
+ | ^^^^^^^^^ ---------------
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--> $DIR/generator-print-verbose-1.rs:35:9
|
LL | let _non_send_gen = make_non_send_generator();
- | ------------- has type `Opaque(DefId(0:44 ~ generator_print_verbose_1[749a]::make_non_send_generator::{opaque#0}), [])` which is not `Send`
+ | ------------- has type `Opaque(DefId(0:34 ~ generator_print_verbose_1[749a]::make_non_send_generator::{opaque#0}), [])` which is not `Send`
LL | yield;
| ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
LL | };
|
LL | || {
| ^^
-note: required because it appears within the type `Opaque(DefId(0:45 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [std::sync::Arc<std::cell::RefCell<i32>>])`
+note: required because it appears within the type `Opaque(DefId(0:35 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [std::sync::Arc<std::cell::RefCell<i32>>])`
--> $DIR/generator-print-verbose-1.rs:41:30
|
LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: required because it appears within the type `Opaque(DefId(0:46 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`
+note: required because it appears within the type `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`
--> $DIR/generator-print-verbose-1.rs:47:34
|
LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: required because it captures the following types: `Opaque(DefId(0:46 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`, `()`
+ = note: required because it captures the following types: `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`, `()`
note: required because it's used within this generator
--> $DIR/generator-print-verbose-1.rs:52:20
|
--> $DIR/auxiliary/foo_defn.rs:4:15
|
LL | type Bar: AsRef<()>;
- | ^^^^^^^^^ required by this bound in `foo_defn::Foo::Bar`
+ | ^^^^^^^^^ required by this bound in `Foo::Bar`
error: aborting due to previous error
--- /dev/null
+struct S;
+
+trait D {
+ type P<T: Copy>;
+ //~^ NOTE required by this bound in `D::P`
+ //~| NOTE required by a bound in `D::P`
+}
+
+impl D for S {
+ type P<T: Copy> = ();
+}
+
+fn main() {
+ let _: <S as D>::P<String>;
+ //~^ ERROR the trait bound `String: Copy` is not satisfied
+ //~| NOTE the trait `Copy` is not implemented for `String`
+}
--- /dev/null
+error[E0277]: the trait bound `String: Copy` is not satisfied
+ --> $DIR/own-bound-span.rs:14:12
+ |
+LL | let _: <S as D>::P<String>;
+ | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
+ |
+note: required by a bound in `D::P`
+ --> $DIR/own-bound-span.rs:4:15
+ |
+LL | type P<T: Copy>;
+ | ^^^^ required by this bound in `D::P`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
-error: missing required bound on `Item`
- --> $DIR/self-outlives-lint.rs:140:5
- |
-LL | type Item<'a>;
- | ^^^^^^^^^^^^^-
- | |
- | help: add the required where clause: `where Self: 'a`
- |
- = note: this bound is currently required to ensure that impls have maximum flexibility
- = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
-
error: missing required bound on `Iterator`
--> $DIR/self-outlives-lint.rs:142:5
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
+error: missing required bound on `Item`
+ --> $DIR/self-outlives-lint.rs:140:5
+ |
+LL | type Item<'a>;
+ | ^^^^^^^^^^^^^-
+ | |
+ | help: add the required where clause: `where Self: 'a`
+ |
+ = note: this bound is currently required to ensure that impls have maximum flexibility
+ = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
+
error: missing required bound on `Item`
--> $DIR/self-outlives-lint.rs:148:5
|
LL | match [5..4, 99..105, 43..44] {
| ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
LL | [_, 99.., _] => {},
- | ^^ expected struct `std::ops::Range`, found integer
+ | ^^ expected struct `Range`, found integer
|
= note: expected struct `std::ops::Range<{integer}>`
found type `{integer}`
LL | match [5..4, 99..105, 43..44] {
| ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
LL | [_, 99..] => {},
- | ^^ expected struct `std::ops::Range`, found integer
+ | ^^ expected struct `Range`, found integer
|
= note: expected struct `std::ops::Range<{integer}>`
found type `{integer}`
LL | match [5..4, 99..105, 43..44] {
| ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
LL | [..9, 99..100, _] => {},
- | ^ expected struct `std::ops::Range`, found integer
+ | ^ expected struct `Range`, found integer
|
= note: expected struct `std::ops::Range<{integer}>`
found type `{integer}`
LL | [..9, 99..100, _] => {},
| ^^ --- this is of type `{integer}`
| |
- | expected struct `std::ops::Range`, found integer
+ | expected struct `Range`, found integer
|
= note: expected struct `std::ops::Range<{integer}>`
found type `{integer}`
LL | match [5..4, 99..105, 43..44] {
| ----------------------- this expression has type `[std::ops::Range<{integer}>; 3]`
LL | [..9, 99..100, _] => {},
- | -- ^^^ expected struct `std::ops::Range`, found integer
+ | -- ^^^ expected struct `Range`, found integer
| |
| this is of type `{integer}`
|
|
= help: the trait `Add<impl Foo>` is not implemented for `u32`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&'a u32 as Add<u32>>
+ <&u32 as Add<&u32>>
+ <u32 as Add<&u32>>
+ <u32 as Add>
error: aborting due to 2 previous errors; 1 warning emitted
--- /dev/null
+// check-pass
+// edition: 2021
+
+#![feature(return_position_impl_trait_in_trait)]
+#![allow(incomplete_features)]
+
+use std::fmt::Debug;
+
+trait Foo<Item> {
+ fn foo<'a>(&'a self) -> impl Debug
+ where
+ Item: 'a;
+}
+
+impl<Item, D: Debug + Clone> Foo<Item> for D {
+ fn foo<'a>(&'a self) -> impl Debug
+ where
+ Item: 'a,
+ {
+ self.clone()
+ }
+}
+
+fn main() {}
--- /dev/null
+// edition: 2021
+
+fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> {
+ async move { let _s = s; }
+ //~^ ERROR hidden type for `impl Future<Output = impl Sized>` captures lifetime that does not appear in bounds
+}
+
+fn main() {}
--- /dev/null
+error[E0700]: hidden type for `impl Future<Output = impl Sized>` captures lifetime that does not appear in bounds
+ --> $DIR/nested-return-type4.rs:4:5
+ |
+LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> {
+ | -- hidden type `[async block@$DIR/nested-return-type4.rs:4:5: 4:31]` captures the lifetime `'s` as defined here
+LL | async move { let _s = s; }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: to declare that `impl Future<Output = impl Sized>` captures `'s`, you can add an explicit `'s` lifetime bound
+ |
+LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> + 's {
+ | ++++
+help: to declare that `impl Sized` captures `'s`, you can add an explicit `'s` lifetime bound
+ |
+LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized + 's> {
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0700`.
LL | fn eq(&self, _other: &(Foo, i32)) -> bool {
| ^^^^^^^^^^^
| |
- | expected struct `a::Bar`, found opaque type
+ | expected struct `Bar`, found opaque type
| help: change the parameter type to match the trait: `&(a::Bar, i32)`
|
= note: expected fn pointer `fn(&a::Bar, &(a::Bar, i32)) -> _`
LL | fn eq(&self, _other: &(Bar, i32)) -> bool {
| ^^^^^^^^^^^
| |
- | expected opaque type, found struct `b::Bar`
+ | expected opaque type, found struct `Bar`
| help: change the parameter type to match the trait: `&(b::Foo, i32)`
|
= note: expected fn pointer `fn(&b::Bar, &(b::Foo, i32)) -> _`
--> $DIR/deref-suggestion.rs:37:5
|
LL | assert_eq!(3i32, &3i32);
- | ^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `&i32`
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | expected `i32`, found `&i32`
+ | expected because this is `i32`
|
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
--> $DIR/infinite-recursion-const-fn.rs:4:5
|
LL | b()
+ | ^^^ reached the configured maximum number of stack frames
+ |
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
| ^^^
- | |
- | reached the configured maximum number of stack frames
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
- | inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5
-...
-LL | a()
- | ---
- | |
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
- | inside `b` at $DIR/infinite-recursion-const-fn.rs:7:5
-LL | }
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `b`
+ --> $DIR/infinite-recursion-const-fn.rs:7:5
+ |
+LL | a()
+ | ^^^
+note: inside `a`
+ --> $DIR/infinite-recursion-const-fn.rs:4:5
+ |
+LL | b()
+ | ^^^
+note: inside `ARR::{constant#0}`
+ --> $DIR/infinite-recursion-const-fn.rs:9:18
+ |
LL | const ARR: [i32; a()] = [5; 6];
- | --- inside `ARR::{constant#0}` at $DIR/infinite-recursion-const-fn.rs:9:18
+ | ^^^
error: aborting due to previous error
--- /dev/null
+#![feature(inline_const)]
+
+fn main() {
+ const { 2 } - const { 1 };
+ //~^ ERROR mismatched types
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/expr-with-block-err.rs:4:13
+ |
+LL | const { 2 } - const { 1 };
+ | ^ expected `()`, found integer
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// check-pass
+#![feature(inline_const)]
+fn main() {
+ match true {
+ true => const {}
+ false => ()
+ }
+ const {}
+ ()
+}
LL | #[no_sanitize(brontosaurus)]
| ^^^^^^^^^^^^
|
- = note: expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
+ = note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
error: aborting due to previous error
--- /dev/null
+pub trait TraitWAssocConst {
+ const A: usize;
+}
+pub struct Demo {}
+
+impl TraitWAssocConst for impl Demo { //~ ERROR E0404
+ //~^ ERROR E0562
+ pubconst A: str = 32; //~ ERROR expected one of
+}
+
+fn foo<A: TraitWAssocConst<A=32>>() { //~ ERROR E0658
+ foo::<Demo>()(); //~ ERROR E0271
+ //~^ ERROR E0618
+ //~| ERROR E0277
+}
+
+fn main<A: TraitWAssocConst<A=32>>() { //~ ERROR E0131
+ //~^ ERROR E0658
+ foo::<Demo>(); //~ ERROR E0277
+ //~^ ERROR E0271
+}
--- /dev/null
+error: expected one of `!` or `::`, found `A`
+ --> $DIR/issue-105330.rs:8:14
+ |
+LL | impl TraitWAssocConst for impl Demo {
+ | - while parsing this item list starting here
+LL |
+LL | pubconst A: str = 32;
+ | ^ expected one of `!` or `::`
+LL | }
+ | - the item list ends here
+
+error[E0404]: expected trait, found struct `Demo`
+ --> $DIR/issue-105330.rs:6:32
+ |
+LL | impl TraitWAssocConst for impl Demo {
+ | ^^^^ not a trait
+
+error[E0658]: associated const equality is incomplete
+ --> $DIR/issue-105330.rs:11:28
+ |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+ | ^^^^
+ |
+ = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
+ = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
+
+error[E0658]: associated const equality is incomplete
+ --> $DIR/issue-105330.rs:17:29
+ |
+LL | fn main<A: TraitWAssocConst<A=32>>() {
+ | ^^^^
+ |
+ = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
+ = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
+
+error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in type
+ --> $DIR/issue-105330.rs:6:27
+ |
+LL | impl TraitWAssocConst for impl Demo {
+ | ^^^^^^^^^
+
+error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied
+ --> $DIR/issue-105330.rs:12:11
+ |
+LL | foo::<Demo>()();
+ | ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo`
+ |
+note: required by a bound in `foo`
+ --> $DIR/issue-105330.rs:11:11
+ |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+ | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `foo`
+
+error[E0271]: type mismatch resolving `<Demo as TraitWAssocConst>::A == 32`
+ --> $DIR/issue-105330.rs:12:11
+ |
+LL | foo::<Demo>()();
+ | ^^^^ types differ
+ |
+note: required by a bound in `foo`
+ --> $DIR/issue-105330.rs:11:28
+ |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+ | ^^^^ required by this bound in `foo`
+
+error[E0618]: expected function, found `()`
+ --> $DIR/issue-105330.rs:12:5
+ |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+ | ----------------------------------- `foo::<Demo>` defined here returns `()`
+LL | foo::<Demo>()();
+ | ^^^^^^^^^^^^^--
+ | |
+ | call expression requires function
+
+error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied
+ --> $DIR/issue-105330.rs:19:11
+ |
+LL | foo::<Demo>();
+ | ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo`
+ |
+note: required by a bound in `foo`
+ --> $DIR/issue-105330.rs:11:11
+ |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+ | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `foo`
+
+error[E0271]: type mismatch resolving `<Demo as TraitWAssocConst>::A == 32`
+ --> $DIR/issue-105330.rs:19:11
+ |
+LL | foo::<Demo>();
+ | ^^^^ types differ
+ |
+note: required by a bound in `foo`
+ --> $DIR/issue-105330.rs:11:28
+ |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+ | ^^^^ required by this bound in `foo`
+
+error[E0131]: `main` function is not allowed to have generic parameters
+ --> $DIR/issue-105330.rs:17:8
+ |
+LL | fn main<A: TraitWAssocConst<A=32>>() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` cannot have generic parameters
+
+error: aborting due to 11 previous errors
+
+Some errors have detailed explanations: E0131, E0271, E0277, E0404, E0562, E0618, E0658.
+For more information about an error, try `rustc --explain E0131`.
| ^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
-note: required for `NoData<NoData<NoData<NoData<NoData<NoData<...>>>>>>` to implement `Foo`
+note: required for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<...>>>>>>>>>>>>>` to implement `Foo`
--> $DIR/issue-20413.rs:9:9
|
LL | impl<T> Foo for T where NoData<T>: Foo {
| ^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
-note: required for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<...>>>>>>` to implement `Bar`
+note: required for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<...>>>>>>>` to implement `Bar`
--> $DIR/issue-20413.rs:28:9
|
LL | impl<T> Bar for T where EvenLessData<T>: Baz {
| ^^^ ^
= note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-20413/issue-20413.long-type-hash.txt'
-note: required for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<...>>>>>>` to implement `Baz`
+note: required for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<...>>>>>>>` to implement `Baz`
--> $DIR/issue-20413.rs:35:9
|
LL | impl<T> Baz for T where AlmostNoData<T>: Bar {
| ^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
-note: required for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<...>>>>>>` to implement `Baz`
+note: required for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<...>>>>>>>` to implement `Baz`
--> $DIR/issue-20413.rs:35:9
|
LL | impl<T> Baz for T where AlmostNoData<T>: Bar {
| ^^^ ^
= note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-20413/issue-20413.long-type-hash.txt'
-note: required for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<...>>>>>>` to implement `Bar`
+note: required for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<...>>>>>>>` to implement `Bar`
--> $DIR/issue-20413.rs:28:9
|
LL | impl<T> Bar for T where EvenLessData<T>: Baz {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_23122_2`)
-note: required for `GetNext<<<<<<... as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` to implement `Next`
+note: required for `GetNext<<<<<<<... as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` to implement `Next`
--> $DIR/issue-23122-2.rs:10:15
|
LL | impl<T: Next> Next for GetNext<T> {
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | F: FnMut(B, Self::Item) -> B,
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `fold`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::fold`
error: aborting due to previous error
|
= help: the trait `Sub<{integer}>` is not implemented for `f64`
= help: the following other types implement trait `Sub<Rhs>`:
- <&'a f32 as Sub<f32>>
<&'a f64 as Sub<f64>>
- <&'a i128 as Sub<i128>>
- <&'a i16 as Sub<i16>>
- <&'a i32 as Sub<i32>>
- <&'a i64 as Sub<i64>>
- <&'a i8 as Sub<i8>>
- <&'a isize as Sub<isize>>
- and 48 others
+ <&f64 as Sub<&f64>>
+ <f64 as Sub<&f64>>
+ <f64 as Sub>
help: consider using a floating-point literal by writing it with `.0`
|
LL | 1.0f64 - 1.0
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | Self: Sized + Iterator<Item = &'a T>,
- | ^^^^^^^^^^^^ required by this bound in `cloned`
+ | ^^^^^^^^^^^^ required by this bound in `Iterator::cloned`
error[E0599]: the method `collect` exists for struct `Cloned<TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:8:21: 8:25]>>`, but its trait bounds were not satisfied
--> $DIR/issue-31173.rs:13:10
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | Self: Sized + Iterator<Item = &'a T>,
- | ^^^^^^^^^^^^ required by this bound in `cloned`
+ | ^^^^^^^^^^^^ required by this bound in `Iterator::cloned`
error[E0271]: expected `std::collections::hash_map::Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
--> $DIR/issue-33941.rs:6:14
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
- | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
error: aborting due to 2 previous errors
+// revisions: imported unimported
+//[imported] check-pass
+
mod private {
pub trait Future {
+ //[unimported]~^^ HELP perhaps add a `use` for it
fn wait(&self) where Self: Sized;
}
}
}
-//use private::Future;
+#[cfg(imported)]
+use private::Future;
fn bar(arg: Box<dyn private::Future>) {
+ // Importing the trait means that we don't autoderef `Box<dyn Future>`
arg.wait();
- //~^ ERROR the `wait` method cannot be invoked on a trait object
+ //[unimported]~^ ERROR the `wait` method cannot be invoked on a trait object
}
-fn main() {
-
-}
+fn main() {}
+++ /dev/null
-error: the `wait` method cannot be invoked on a trait object
- --> $DIR/issue-35976.rs:14:9
- |
-LL | fn wait(&self) where Self: Sized;
- | ----- this has a `Sized` requirement
-...
-LL | arg.wait();
- | ^^^^
-
-error: aborting due to previous error
-
--- /dev/null
+error: the `wait` method cannot be invoked on a trait object
+ --> $DIR/issue-35976.rs:20:9
+ |
+LL | fn wait(&self) where Self: Sized;
+ | ----- this has a `Sized` requirement
+...
+LL | arg.wait();
+ | ^^^^
+ |
+help: another candidate was found in the following trait, perhaps add a `use` for it:
+ |
+LL | use private::Future;
+ |
+
+error: aborting due to previous error
+
--> $DIR/issue-47486.rs:2:10
|
LL | () < std::mem::size_of::<_>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `usize`
+ | -- ^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `usize`
+ | |
+ | expected because this is `()`
error[E0282]: type annotations needed
--> $DIR/issue-47486.rs:3:11
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
- | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
error[E0277]: a value of type `Vec<f64>` cannot be built from an iterator over elements of type `&f64`
--> $DIR/issue-66923-show-error-for-correct-call.rs:12:14
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
- | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
error: aborting due to 2 previous errors
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
- | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
error: aborting due to previous error
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
- | ^ required by this bound in `collect`
+ | ^ required by this bound in `Iterator::collect`
error[E0277]: a slice of type `[i32]` cannot be built since `[i32]` has no definite size
--> $DIR/collect-into-slice.rs:6:30
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
- | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
error: aborting due to 3 previous errors
LL | assert_copy::<&'static mut isize>();
| ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'static mut isize`
|
- = help: the following other types implement trait `Copy`:
- f32
- f64
- i128
- i16
- i32
- i64
- i8
- isize
- and 6 others
+ = help: the trait `Copy` is implemented for `isize`
note: required by a bound in `assert_copy`
--> $DIR/kindck-copy.rs:5:18
|
LL | assert_copy::<&'a mut isize>();
| ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'a mut isize`
|
- = help: the following other types implement trait `Copy`:
- f32
- f64
- i128
- i16
- i32
- i64
- i8
- isize
- and 6 others
+ = help: the trait `Copy` is implemented for `isize`
note: required by a bound in `assert_copy`
--> $DIR/kindck-copy.rs:5:18
|
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
- | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
error: aborting due to previous error
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
- | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
error[E0277]: a value of type `impl Debug` cannot be built from an iterator over elements of type `_`
--> $DIR/recursion4.rs:19:9
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
- | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::collect`
error: aborting due to 2 previous errors
LL | let Bar(z) = x;
| ^^^^^^ - this expression has type `&mut irrefutable::Foo`
| |
- | expected struct `irrefutable::Foo`, found struct `irrefutable::Bar`
+ | expected struct `Foo`, found struct `Bar`
error: aborting due to 2 previous errors
<&'a str as PartialEq<OsString>>
<&'a str as PartialEq<String>>
<&'b str as PartialEq<Cow<'a, str>>>
- <String as PartialEq<&'a str>>
- <String as PartialEq<Cow<'a, str>>>
- <String as PartialEq<str>>
- <String as PartialEq>
<str as PartialEq<Cow<'a, str>>>
- and 4 others
+ <str as PartialEq<OsStr>>
+ <str as PartialEq<OsString>>
+ <str as PartialEq<String>>
+ <str as PartialEq>
error[E0308]: mismatched types
--> $DIR/lex-bad-char-literals-6.rs:15:20
<&'a str as PartialEq<OsString>>
<&'a str as PartialEq<String>>
<&'b str as PartialEq<Cow<'a, str>>>
- <String as PartialEq<&'a str>>
- <String as PartialEq<Cow<'a, str>>>
- <String as PartialEq<str>>
- <String as PartialEq>
<str as PartialEq<Cow<'a, str>>>
- and 4 others
+ <str as PartialEq<OsStr>>
+ <str as PartialEq<OsString>>
+ <str as PartialEq<String>>
+ <str as PartialEq>
error: aborting due to 6 previous errors
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | intrinsics::size_of::<T>()
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ inside `std::mem::size_of::<[u8; SIZE]>` at $SRC_DIR/core/src/mem/mod.rs:LL:COL
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- ::: $DIR/issue-55878.rs:7:26
+note: inside `std::mem::size_of::<[u8; SIZE]>`
+ --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
+ |
+LL | intrinsics::size_of::<T>()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `main`
+ --> $DIR/issue-55878.rs:7:26
|
LL | println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>());
- | ---------------------------------------------- inside `main` at $DIR/issue-55878.rs:7:26
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: erroneous constant used
--> $DIR/issue-55878.rs:7:26
--- /dev/null
+#![feature(linkage)]
+#![crate_type = "lib"]
+
+#[linkage="external"]
+pub static EXTERN: u32 = 0;
+++ /dev/null
-#![feature(linkage)]
-#![crate_type = "lib"]
-
-#[linkage="external"]
-pub static EXTERN: u32 = 0;
--- /dev/null
+// build-pass
+// aux-build:def_external.rs
+
+extern crate def_external as dep;
+
+fn main() {
+ println!("{:p}", &dep::EXTERN);
+}
+++ /dev/null
-// rust-lang/rust#59548: We used to ICE when trying to use a static
-// with a type that violated its own `#[linkage]`.
-
-// build-fail
-// aux-build:def_illtyped_external.rs
-
-extern crate def_illtyped_external as dep;
-
-fn main() {
- println!("{:p}", &dep::EXTERN);
-}
+++ /dev/null
-error: must have type `*const T` or `*mut T` due to `#[linkage]` attribute
- --> $DIR/auxiliary/def_illtyped_external.rs:5:1
- |
-LL | pub static EXTERN: u32 = 0;
- | ^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to previous error
-
-// FIXME https://github.com/rust-lang/rust/issues/59774
-
-// build-fail
-// normalize-stderr-test "thread.*panicked.*Metadata module not compiled.*\n" -> ""
-// normalize-stderr-test "note:.*RUST_BACKTRACE=1.*\n" -> ""
-// ignore-sgx no weak linkages permitted
+// check-fail
#![feature(linkage)]
extern "C" {
#[linkage = "extern_weak"]
static foo: i32;
-//~^ ERROR: must have type `*const T` or `*mut T` due to `#[linkage]` attribute
+//~^ ERROR: invalid type for variable with `#[linkage]` attribute
}
fn main() {
-error: must have type `*const T` or `*mut T` due to `#[linkage]` attribute
- --> $DIR/linkage2.rs:12:5
+error[E0791]: invalid type for variable with `#[linkage]` attribute
+ --> $DIR/linkage2.rs:7:5
|
LL | static foo: i32;
| ^^^^^^^^^^^^^^^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0791`.
--- /dev/null
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: format argument must be a string literal
+
+fn f(){(print!(á
--- /dev/null
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-104897.rs:6:18
+ |
+LL | fn f(){(print!(á
+ | -- - ^
+ | || |
+ | || unclosed delimiter
+ | |unclosed delimiter
+ | unclosed delimiter
+
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-104897.rs:6:18
+ |
+LL | fn f(){(print!(á
+ | -- - ^
+ | || |
+ | || unclosed delimiter
+ | |unclosed delimiter
+ | unclosed delimiter
+
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-104897.rs:6:18
+ |
+LL | fn f(){(print!(á
+ | -- - ^
+ | || |
+ | || unclosed delimiter
+ | |unclosed delimiter
+ | unclosed delimiter
+
+error: format argument must be a string literal
+ --> $DIR/issue-104897.rs:6:16
+ |
+LL | fn f(){(print!(á
+ | ^
+ |
+help: you might be missing a string literal to format with
+ |
+LL | fn f(){(print!("{}", á
+ | +++++
+
+error: aborting due to 4 previous errors
+
+++ /dev/null
-error: unknown lint: `nonex_lint_top_level`
- --> $DIR/issue-97094.rs:14:26
- |
-LL | #![cfg_attr(all(), allow(nonex_lint_top_level))]
- | ^^^^^^^^^^^^^^^^^^^^
- |
-note: the lint level is defined here
- --> $DIR/issue-97094.rs:10:9
- |
-LL | #![deny(warnings)]
- | ^^^^^^^^
- = note: `#[deny(unknown_lints)]` implied by `#[deny(warnings)]`
-
-error: lint `bare_trait_object` has been renamed to `bare_trait_objects`
- --> $DIR/issue-97094.rs:16:26
- |
-LL | #![cfg_attr(all(), allow(bare_trait_object))]
- | ^^^^^^^^^^^^^^^^^ help: use the new name: `bare_trait_objects`
- |
- = note: `#[deny(renamed_and_removed_lints)]` implied by `#[deny(warnings)]`
-
-error: unknown lint: `nonex_lint_mod`
- --> $DIR/issue-97094.rs:19:25
- |
-LL | #[cfg_attr(all(), allow(nonex_lint_mod))]
- | ^^^^^^^^^^^^^^
-
-error: unknown lint: `nonex_lint_mod_inner`
- --> $DIR/issue-97094.rs:22:30
- |
-LL | #![cfg_attr(all(), allow(nonex_lint_mod_inner))]
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: unknown lint: `nonex_lint_fn`
- --> $DIR/issue-97094.rs:26:25
- |
-LL | #[cfg_attr(all(), allow(nonex_lint_fn))]
- | ^^^^^^^^^^^^^
-
-error: unknown lint: `nonex_lint_in_macro`
- --> $DIR/issue-97094.rs:37:29
- |
-LL | #[cfg_attr(all(), allow(nonex_lint_in_macro))]
- | ^^^^^^^^^^^^^^^^^^^
-
-error: unknown lint: `nonex_lint_fn`
- --> $DIR/issue-97094.rs:56:13
- |
-LL | #[allow(nonex_lint_fn)]
- | ^^^^^^^^^^^^^
-
-error: aborting due to 7 previous errors
-
+++ /dev/null
-error: unknown lint: `nonex_lint_top_level`
- --> $DIR/issue-97094.rs:14:26
- |
-LL | #![cfg_attr(all(), allow(nonex_lint_top_level))]
- | ^^^^^^^^^^^^^^^^^^^^
- |
-note: the lint level is defined here
- --> $DIR/issue-97094.rs:10:9
- |
-LL | #![deny(warnings)]
- | ^^^^^^^^
- = note: `#[deny(unknown_lints)]` implied by `#[deny(warnings)]`
-
-error: lint `bare_trait_object` has been renamed to `bare_trait_objects`
- --> $DIR/issue-97094.rs:16:26
- |
-LL | #![cfg_attr(all(), allow(bare_trait_object))]
- | ^^^^^^^^^^^^^^^^^ help: use the new name: `bare_trait_objects`
- |
- = note: `#[deny(renamed_and_removed_lints)]` implied by `#[deny(warnings)]`
-
-error: unknown lint: `nonex_lint_mod`
- --> $DIR/issue-97094.rs:19:25
- |
-LL | #[cfg_attr(all(), allow(nonex_lint_mod))]
- | ^^^^^^^^^^^^^^
-
-error: unknown lint: `nonex_lint_mod_inner`
- --> $DIR/issue-97094.rs:22:30
- |
-LL | #![cfg_attr(all(), allow(nonex_lint_mod_inner))]
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: unknown lint: `nonex_lint_fn`
- --> $DIR/issue-97094.rs:26:25
- |
-LL | #[cfg_attr(all(), allow(nonex_lint_fn))]
- | ^^^^^^^^^^^^^
-
-error: unknown lint: `nonex_lint_in_macro`
- --> $DIR/issue-97094.rs:37:29
- |
-LL | #[cfg_attr(all(), allow(nonex_lint_in_macro))]
- | ^^^^^^^^^^^^^^^^^^^
-
-error: unknown lint: `nonex_lint_fn`
- --> $DIR/issue-97094.rs:56:13
- |
-LL | #[allow(nonex_lint_fn)]
- | ^^^^^^^^^^^^^
-
-error: aborting due to 7 previous errors
-
-// revisions: interleaved nointerleaved
-// [nointerleaved]compile-flags: -Z no-interleave-lints
-
-// This test has two revisions because the logic change
-// needed to make this test pass had to be adjusted
-// for no-interleave-lints. Should the debug option
-// be removed one day, please don't remove this
-// test entirely, just remove the revision from it.
-
#![deny(warnings)]
// Ensure that unknown lints inside cfg-attr's are linted for
--- /dev/null
+error: unknown lint: `nonex_lint_top_level`
+ --> $DIR/issue-97094.rs:5:26
+ |
+LL | #![cfg_attr(all(), allow(nonex_lint_top_level))]
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-97094.rs:1:9
+ |
+LL | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[deny(unknown_lints)]` implied by `#[deny(warnings)]`
+
+error: lint `bare_trait_object` has been renamed to `bare_trait_objects`
+ --> $DIR/issue-97094.rs:7:26
+ |
+LL | #![cfg_attr(all(), allow(bare_trait_object))]
+ | ^^^^^^^^^^^^^^^^^ help: use the new name: `bare_trait_objects`
+ |
+ = note: `#[deny(renamed_and_removed_lints)]` implied by `#[deny(warnings)]`
+
+error: unknown lint: `nonex_lint_mod`
+ --> $DIR/issue-97094.rs:10:25
+ |
+LL | #[cfg_attr(all(), allow(nonex_lint_mod))]
+ | ^^^^^^^^^^^^^^
+
+error: unknown lint: `nonex_lint_mod_inner`
+ --> $DIR/issue-97094.rs:13:30
+ |
+LL | #![cfg_attr(all(), allow(nonex_lint_mod_inner))]
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: unknown lint: `nonex_lint_fn`
+ --> $DIR/issue-97094.rs:17:25
+ |
+LL | #[cfg_attr(all(), allow(nonex_lint_fn))]
+ | ^^^^^^^^^^^^^
+
+error: unknown lint: `nonex_lint_in_macro`
+ --> $DIR/issue-97094.rs:28:29
+ |
+LL | #[cfg_attr(all(), allow(nonex_lint_in_macro))]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: unknown lint: `nonex_lint_fn`
+ --> $DIR/issue-97094.rs:47:13
+ |
+LL | #[allow(nonex_lint_fn)]
+ | ^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
--- /dev/null
+// check-pass
+#![deny(missing_copy_implementations)]
+
+// Don't recommend implementing Copy on something stateful like an iterator.
+pub struct MyIterator {
+ num: u8,
+}
+
+impl Iterator for MyIterator {
+ type Item = u8;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ todo!()
+ }
+}
+
+pub struct Handle {
+ inner: *mut (),
+}
+
+pub struct Handle2 {
+ inner: *const (),
+}
+
+pub enum MaybeHandle {
+ Ptr(*mut ()),
+}
+
+pub union UnionHandle {
+ ptr: *mut (),
+}
+
+pub struct Array([u8; 2048]);
+
+fn main() {}
// run-rustfix
-#![feature(box_patterns, stmt_expr_attributes)]
+#![feature(box_patterns, stmt_expr_attributes, yeet_expr)]
#![allow(
dead_code,
let _x = #[allow(dead_code)] (1 + 2);
}
+fn _no_lint_yeet() -> Result<(), ()> {
+ #[allow(unreachable_code)]
+ if (do yeet) {}
+
+ Ok(())
+}
+
// Don't lint in these cases (#64106).
fn or_patterns_no_lint() {
match Box::new(0) {
// run-rustfix
-#![feature(box_patterns, stmt_expr_attributes)]
+#![feature(box_patterns, stmt_expr_attributes, yeet_expr)]
#![allow(
dead_code,
let _x = #[allow(dead_code)] (1 + 2);
}
+fn _no_lint_yeet() -> Result<(), ()> {
+ #[allow(unreachable_code)]
+ if (do yeet) {}
+
+ Ok(())
+}
+
// Don't lint in these cases (#64106).
fn or_patterns_no_lint() {
match Box::new(0) {
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:49:12
+ --> $DIR/issue-54538-unused-parens-lint.rs:56:12
|
LL | if let (0 | 1) = 0 {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:50:13
+ --> $DIR/issue-54538-unused-parens-lint.rs:57:13
|
LL | if let ((0 | 1),) = (0,) {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:51:13
+ --> $DIR/issue-54538-unused-parens-lint.rs:58:13
|
LL | if let [(0 | 1)] = [0] {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:52:16
+ --> $DIR/issue-54538-unused-parens-lint.rs:59:16
|
LL | if let 0 | (1 | 2) = 0 {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:54:15
+ --> $DIR/issue-54538-unused-parens-lint.rs:61:15
|
LL | if let TS((0 | 1)) = TS(0) {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:56:20
+ --> $DIR/issue-54538-unused-parens-lint.rs:63:20
|
LL | if let NS { f: (0 | 1) } = (NS { f: 0 }) {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:66:9
+ --> $DIR/issue-54538-unused-parens-lint.rs:73:9
|
LL | (_) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:67:9
+ --> $DIR/issue-54538-unused-parens-lint.rs:74:9
|
LL | (y) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:68:9
+ --> $DIR/issue-54538-unused-parens-lint.rs:75:9
|
LL | (ref r) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:69:9
+ --> $DIR/issue-54538-unused-parens-lint.rs:76:9
|
LL | (e @ 1...2) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:75:9
+ --> $DIR/issue-54538-unused-parens-lint.rs:82:9
|
LL | (e @ &(1...2)) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:76:10
+ --> $DIR/issue-54538-unused-parens-lint.rs:83:10
|
LL | &(_) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:87:9
+ --> $DIR/issue-54538-unused-parens-lint.rs:94:9
|
LL | (_) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:88:9
+ --> $DIR/issue-54538-unused-parens-lint.rs:95:9
|
LL | (y) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:89:9
+ --> $DIR/issue-54538-unused-parens-lint.rs:96:9
|
LL | (ref r) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:90:9
+ --> $DIR/issue-54538-unused-parens-lint.rs:97:9
|
LL | (e @ 1..=2) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:96:9
+ --> $DIR/issue-54538-unused-parens-lint.rs:103:9
|
LL | (e @ &(1..=2)) => {}
| ^ ^
|
error: unnecessary parentheses around pattern
- --> $DIR/issue-54538-unused-parens-lint.rs:97:10
+ --> $DIR/issue-54538-unused-parens-lint.rs:104:10
|
LL | &(_) => {}
| ^ ^
mod typeascription {
fn outside() -> u8 {
- ({ 0 }): u8
- }
- fn inside() -> u8 {
- ({ 0 }: u8)
+ type_ascribe!(({ 0 }), u8)
}
fn outside_match() -> u8 {
- (match 0 { x => x }): u8
- }
- fn inside_match() -> u8 {
- (match 0 { x => x }: u8)
+ type_ascribe!((match 0 { x => x }), u8)
}
fn outside_if() -> u8 {
- (if false { 0 } else { 0 }): u8
- }
- fn inside_if() -> u8 {
- (if false { 0 } else { 0 }: u8)
+ type_ascribe!((if false { 0 } else { 0 }), u8)
}
}
--- /dev/null
+macro_rules! m {
+ ($s:stmt) => {}
+}
+
+m! { mut x }
+//~^ ERROR expected expression, found keyword `mut`
+//~| ERROR expected a statement
+m! { auto x }
+//~^ ERROR invalid variable declaration
+m! { var x }
+//~^ ERROR invalid variable declaration
+
+fn main() {}
--- /dev/null
+error: expected expression, found keyword `mut`
+ --> $DIR/issue-103529.rs:5:6
+ |
+LL | m! { mut x }
+ | ^^^ expected expression
+
+error: expected a statement
+ --> $DIR/issue-103529.rs:5:10
+ |
+LL | ($s:stmt) => {}
+ | ------- while parsing argument for this `stmt` macro fragment
+...
+LL | m! { mut x }
+ | ^
+
+error: invalid variable declaration
+ --> $DIR/issue-103529.rs:8:6
+ |
+LL | m! { auto x }
+ | ^^^^
+ |
+help: write `let` instead of `auto` to introduce a new variable
+ |
+LL | m! { let x }
+ | ~~~
+
+error: invalid variable declaration
+ --> $DIR/issue-103529.rs:10:6
+ |
+LL | m! { var x }
+ | ^^^
+ |
+help: write `let` instead of `var` to introduce a new variable
+ |
+LL | m! { let x }
+ | ~~~
+
+error: aborting due to 4 previous errors
+
| ^^^^^^^^^
...
LL | foo!()
- | ------- help: you might be missing a semicolon here: `;`
- | |
- | caused by the macro expansion here
+ | ------ caused by the macro expansion here
|
= note: the usage of `foo!` is likely invalid in expression context
+help: you might be missing a semicolon here
+ |
+LL | foo!();
+ | +
warning: trailing semicolon in macro used in expression position
--> $DIR/macro-in-expression-context.rs:5:29
LL | values!(STRING(1) as (String) => cfg(test),);
| -------------------------------------------- in this macro invocation
|
+ = help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
= note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info)
error: macro expansion ignores token `(String)` and any following
--- /dev/null
+// compile-flags: -Zmaximal-hir-to-mir-coverage
+// run-pass
+
+// Just making sure this flag is accepted and doesn't crash the compiler
+
+fn main() {
+ let x = 1;
+ let y = x + 1;
+ println!("{y}");
+}
--> $DIR/issue-90315.rs:28:8
|
LL | if 1..(end + 1).is_empty() {
- | ^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<{integer}>`
--> $DIR/issue-90315.rs:34:8
|
LL | if 1..(end + 1).is_sorted() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<{integer}>`
--> $DIR/issue-90315.rs:40:21
|
LL | let _res: i32 = 3..6.take(2).sum();
- | --- ^^^^^^^^^^^^^^^^^^ expected `i32`, found struct `std::ops::Range`
+ | --- ^^^^^^^^^^^^^^^^^^ expected `i32`, found struct `Range`
| |
| expected due to this
|
--> $DIR/issue-90315.rs:45:21
|
LL | let _sum: i32 = 3..6.sum();
- | --- ^^^^^^^^^^ expected `i32`, found struct `std::ops::Range`
+ | --- ^^^^^^^^^^ expected `i32`, found struct `Range`
| |
| expected due to this
|
--> $DIR/issue-90315.rs:62:8
|
LL | if 1..end.error_method() {
- | ^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<{integer}>`
+++ /dev/null
-// check-pass
-// compile-flags: -Zvalidate-mir
-
-fn foo(_a: &str) {}
-
-fn main() {
- let x = foo as fn(&'static str);
-
- let _ = x == foo;
-}
fn main() {
let x = [1, 2, 3];
// The RHS should coerce to &[i32]
- let _y : &[i32] = &x : &[i32; 3];
+ let _y : &[i32] = type_ascribe!(&x, &[i32; 3]);
}
--- /dev/null
+// check-pass
+// compile-flags: -Zvalidate-mir
+
+fn foo(_a: &str) {}
+
+fn main() {
+ let x = foo as fn(&'static str);
+
+ let _ = x == foo;
+}
--- /dev/null
+// Regression test for #105009. the issue here was that even after the `RevealAll` pass,
+// `validate` still used `Reveal::UserFacing`. This meant that it now ends up comparing
+// opaque types with their revealed version, resulting in an ICE.
+//
+// We're using these flags to run the `RevealAll` pass while making it less likely to
+// accidentally removing the assignment from `Foo<fn_ptr>` to `Foo<fn_def>`.
+
+// compile-flags: -Zinline_mir=yes -Zmir-opt-level=0 -Zvalidate-mir
+// run-pass
+
+use std::hint::black_box;
+
+trait Func {
+ type Ret: Id;
+}
+
+trait Id {
+ type Assoc;
+}
+impl Id for u32 {
+ type Assoc = u32;
+}
+impl Id for i32 {
+ type Assoc = i32;
+}
+
+impl<F: FnOnce() -> R, R: Id> Func for F {
+ type Ret = R;
+}
+
+fn bar() -> impl Copy + Id {
+ 0u32
+}
+
+struct Foo<T: Func> {
+ _func: T,
+ value: Option<<<T as Func>::Ret as Id>::Assoc>,
+}
+
+fn main() {
+ let mut fn_def = black_box(Foo {
+ _func: bar,
+ value: None,
+ });
+ let fn_ptr = black_box(Foo {
+ _func: bar as fn() -> _,
+ value: None,
+ });
+
+ fn_def.value = fn_ptr.value;
+ black_box(fn_def);
+}
|
= help: the trait `Sub<Option<{integer}>>` is not implemented for `usize`
= help: the following other types implement trait `Sub<Rhs>`:
- <&'a f32 as Sub<f32>>
- <&'a f64 as Sub<f64>>
- <&'a i128 as Sub<i128>>
- <&'a i16 as Sub<i16>>
- <&'a i32 as Sub<i32>>
- <&'a i64 as Sub<i64>>
- <&'a i8 as Sub<i8>>
- <&'a isize as Sub<isize>>
- and 48 others
+ <&'a usize as Sub<usize>>
+ <&usize as Sub<&usize>>
+ <usize as Sub<&usize>>
+ <usize as Sub>
error[E0277]: cannot multiply `{integer}` by `()`
--> $DIR/binops.rs:4:7
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | F: FnMut(Self::Item) -> B,
- | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`
+ | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::map`
error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 3 distinct arguments
--> $DIR/closure-arg-count.rs:27:57
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | F: FnMut(Self::Item) -> B,
- | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`
+ | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::map`
error[E0593]: function is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments
--> $DIR/closure-arg-count.rs:29:57
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | F: FnMut(Self::Item) -> B,
- | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`
+ | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::map`
error[E0593]: function is expected to take 1 argument, but it takes 2 arguments
--> $DIR/closure-arg-count.rs:32:45
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | F: FnMut(Self::Item) -> B,
- | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`
+ | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::map`
error[E0593]: function is expected to take 0 arguments, but it takes 1 argument
--> $DIR/closure-arg-count.rs:35:10
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | F: FnMut(Self::Item) -> B,
- | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`
+ | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::map`
error[E0631]: type mismatch in closure arguments
--> $DIR/closure-arg-type-mismatch.rs:4:14
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | F: FnMut(Self::Item) -> B,
- | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`
+ | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::map`
error[E0631]: type mismatch in closure arguments
--> $DIR/closure-arg-type-mismatch.rs:5:14
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | F: FnMut(Self::Item) -> B,
- | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`
+ | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::map`
error: aborting due to 3 previous errors
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
LL | P: FnMut(&Self::Item) -> bool,
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `filter`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::filter`
error[E0599]: the method `count` exists for struct `Filter<Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:7:39: 7:48]>`, but its trait bounds were not satisfied
--> $DIR/issue-36053-2.rs:7:55
--> $DIR/wrap-suggestion-privacy.rs:22:17
|
LL | needs_ready(Some(0));
- | ----------- ^^^^^^^ expected struct `std::future::Ready`, found enum `Option`
+ | ----------- ^^^^^^^ expected struct `Ready`, found enum `Option`
| |
| arguments to this function are incorrect
|
|
= help: the trait `Add<()>` is not implemented for `usize`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&'a usize as Add<usize>>
+ <&usize as Add<&usize>>
+ <usize as Add<&usize>>
+ <usize as Add>
error: aborting due to previous error
type PairCoupledRegions<'a, T> = (&'a T, &'a T);
fn uncoupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 {
- let ((y, _z),) = ((s, _x),): (PairUncoupled<_>,);
+ let ((y, _z),) = type_ascribe!(((s, _x),), (PairUncoupled<_>,));
y // OK
}
fn coupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 {
- let ((y, _z),) = ((s, _x),): (PairCoupledTypes<_>,);
+ let ((y, _z),) = type_ascribe!(((s, _x),), (PairCoupledTypes<_>,));
y //~ ERROR lifetime may not live long enough
}
fn coupled_regions_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 {
- let ((y, _z),) = ((s, _x),): (PairCoupledRegions<_>,);
+ let ((y, _z),) = type_ascribe!(((s, _x),), (PairCoupledRegions<_>,));
y //~ ERROR lifetime may not live long enough
}
|
LL | fn coupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 {
| -- lifetime `'a` defined here
-LL | let ((y, _z),) = ((s, _x),): (PairCoupledTypes<_>,);
+LL | let ((y, _z),) = type_ascribe!(((s, _x),), (PairCoupledTypes<_>,));
LL | y
| ^ returning this value requires that `'a` must outlive `'static`
|
LL | fn coupled_regions_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 {
| -- lifetime `'a` defined here
-LL | let ((y, _z),) = ((s, _x),): (PairCoupledRegions<_>,);
+LL | let ((y, _z),) = type_ascribe!(((s, _x),), (PairCoupledRegions<_>,));
LL | y
| ^ returning this value requires that `'a` must outlive `'static`
fn main() {
let x = 22_u32;
- let y: &u32 = &x: &'static u32; //~ ERROR E0597
+ let y: &u32 = type_ascribe!(&x, &'static u32); //~ ERROR E0597
}
error[E0597]: `x` does not live long enough
- --> $DIR/type_ascription_static_lifetime.rs:6:19
+ --> $DIR/type_ascription_static_lifetime.rs:6:33
|
-LL | let y: &u32 = &x: &'static u32;
- | ^^--------------
- | |
- | borrowed value does not live long enough
+LL | let y: &u32 = type_ascribe!(&x, &'static u32);
+ | --------------^^---------------
+ | | |
+ | | borrowed value does not live long enough
| type annotation requires that `x` is borrowed for `'static`
LL | }
| - `x` dropped here while still borrowed
|
= help: the trait `Add<{float}>` is not implemented for `u8`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&'a u8 as Add<u8>>
+ <&u8 as Add<&u8>>
+ <u8 as Add<&u8>>
+ <u8 as Add>
error[E0277]: cannot add `&str` to `f64`
--> $DIR/not-suggest-float-literal.rs:6:7
|
= help: the trait `Add<&str>` is not implemented for `f64`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
<&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&f64 as Add<&f64>>
+ <f64 as Add<&f64>>
+ <f64 as Add>
error[E0277]: cannot add `{integer}` to `f64`
--> $DIR/not-suggest-float-literal.rs:11:7
|
= help: the trait `Add<{integer}>` is not implemented for `f64`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
<&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&f64 as Add<&f64>>
+ <f64 as Add<&f64>>
+ <f64 as Add>
error[E0277]: cannot subtract `{float}` from `u8`
--> $DIR/not-suggest-float-literal.rs:15:7
|
= help: the trait `Sub<{float}>` is not implemented for `u8`
= help: the following other types implement trait `Sub<Rhs>`:
- <&'a f32 as Sub<f32>>
- <&'a f64 as Sub<f64>>
- <&'a i128 as Sub<i128>>
- <&'a i16 as Sub<i16>>
- <&'a i32 as Sub<i32>>
- <&'a i64 as Sub<i64>>
- <&'a i8 as Sub<i8>>
- <&'a isize as Sub<isize>>
- and 48 others
+ <&'a u8 as Sub<u8>>
+ <&u8 as Sub<&u8>>
+ <u8 as Sub<&u8>>
+ <u8 as Sub>
error[E0277]: cannot subtract `&str` from `f64`
--> $DIR/not-suggest-float-literal.rs:19:7
|
= help: the trait `Sub<&str>` is not implemented for `f64`
= help: the following other types implement trait `Sub<Rhs>`:
- <&'a f32 as Sub<f32>>
<&'a f64 as Sub<f64>>
- <&'a i128 as Sub<i128>>
- <&'a i16 as Sub<i16>>
- <&'a i32 as Sub<i32>>
- <&'a i64 as Sub<i64>>
- <&'a i8 as Sub<i8>>
- <&'a isize as Sub<isize>>
- and 48 others
+ <&f64 as Sub<&f64>>
+ <f64 as Sub<&f64>>
+ <f64 as Sub>
error[E0277]: cannot subtract `{integer}` from `f64`
--> $DIR/not-suggest-float-literal.rs:24:7
|
= help: the trait `Sub<{integer}>` is not implemented for `f64`
= help: the following other types implement trait `Sub<Rhs>`:
- <&'a f32 as Sub<f32>>
<&'a f64 as Sub<f64>>
- <&'a i128 as Sub<i128>>
- <&'a i16 as Sub<i16>>
- <&'a i32 as Sub<i32>>
- <&'a i64 as Sub<i64>>
- <&'a i8 as Sub<i8>>
- <&'a isize as Sub<isize>>
- and 48 others
+ <&f64 as Sub<&f64>>
+ <f64 as Sub<&f64>>
+ <f64 as Sub>
error[E0277]: cannot multiply `u8` by `{float}`
--> $DIR/not-suggest-float-literal.rs:28:7
|
= help: the trait `Mul<{float}>` is not implemented for `u8`
= help: the following other types implement trait `Mul<Rhs>`:
- <&'a f32 as Mul<f32>>
- <&'a f64 as Mul<f64>>
- <&'a i128 as Mul<i128>>
- <&'a i16 as Mul<i16>>
- <&'a i32 as Mul<i32>>
- <&'a i64 as Mul<i64>>
- <&'a i8 as Mul<i8>>
- <&'a isize as Mul<isize>>
- and 49 others
+ <&'a u8 as Mul<u8>>
+ <&u8 as Mul<&u8>>
+ <u8 as Mul<&u8>>
+ <u8 as Mul>
error[E0277]: cannot multiply `f64` by `&str`
--> $DIR/not-suggest-float-literal.rs:32:7
|
= help: the trait `Mul<&str>` is not implemented for `f64`
= help: the following other types implement trait `Mul<Rhs>`:
- <&'a f32 as Mul<f32>>
<&'a f64 as Mul<f64>>
- <&'a i128 as Mul<i128>>
- <&'a i16 as Mul<i16>>
- <&'a i32 as Mul<i32>>
- <&'a i64 as Mul<i64>>
- <&'a i8 as Mul<i8>>
- <&'a isize as Mul<isize>>
- and 49 others
+ <&f64 as Mul<&f64>>
+ <f64 as Mul<&f64>>
+ <f64 as Mul>
error[E0277]: cannot multiply `f64` by `{integer}`
--> $DIR/not-suggest-float-literal.rs:37:7
|
= help: the trait `Mul<{integer}>` is not implemented for `f64`
= help: the following other types implement trait `Mul<Rhs>`:
- <&'a f32 as Mul<f32>>
<&'a f64 as Mul<f64>>
- <&'a i128 as Mul<i128>>
- <&'a i16 as Mul<i16>>
- <&'a i32 as Mul<i32>>
- <&'a i64 as Mul<i64>>
- <&'a i8 as Mul<i8>>
- <&'a isize as Mul<isize>>
- and 49 others
+ <&f64 as Mul<&f64>>
+ <f64 as Mul<&f64>>
+ <f64 as Mul>
error[E0277]: cannot divide `u8` by `{float}`
--> $DIR/not-suggest-float-literal.rs:41:7
|
= help: the trait `Div<{float}>` is not implemented for `u8`
= help: the following other types implement trait `Div<Rhs>`:
- <&'a f32 as Div<f32>>
- <&'a f64 as Div<f64>>
- <&'a i128 as Div<i128>>
- <&'a i16 as Div<i16>>
- <&'a i32 as Div<i32>>
- <&'a i64 as Div<i64>>
- <&'a i8 as Div<i8>>
- <&'a isize as Div<isize>>
- and 54 others
+ <&'a u8 as Div<u8>>
+ <&u8 as Div<&u8>>
+ <u8 as Div<&u8>>
+ <u8 as Div<NonZeroU8>>
+ <u8 as Div>
error[E0277]: cannot divide `f64` by `&str`
--> $DIR/not-suggest-float-literal.rs:45:7
|
= help: the trait `Div<&str>` is not implemented for `f64`
= help: the following other types implement trait `Div<Rhs>`:
- <&'a f32 as Div<f32>>
<&'a f64 as Div<f64>>
- <&'a i128 as Div<i128>>
- <&'a i16 as Div<i16>>
- <&'a i32 as Div<i32>>
- <&'a i64 as Div<i64>>
- <&'a i8 as Div<i8>>
- <&'a isize as Div<isize>>
- and 54 others
+ <&f64 as Div<&f64>>
+ <f64 as Div<&f64>>
+ <f64 as Div>
error[E0277]: cannot divide `f64` by `{integer}`
--> $DIR/not-suggest-float-literal.rs:50:7
|
= help: the trait `Div<{integer}>` is not implemented for `f64`
= help: the following other types implement trait `Div<Rhs>`:
- <&'a f32 as Div<f32>>
<&'a f64 as Div<f64>>
- <&'a i128 as Div<i128>>
- <&'a i16 as Div<i16>>
- <&'a i32 as Div<i32>>
- <&'a i64 as Div<i64>>
- <&'a i8 as Div<i8>>
- <&'a isize as Div<isize>>
- and 54 others
+ <&f64 as Div<&f64>>
+ <f64 as Div<&f64>>
+ <f64 as Div>
error: aborting due to 12 previous errors
= help: the trait `Add<{integer}>` is not implemented for `f32`
= help: the following other types implement trait `Add<Rhs>`:
<&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&f32 as Add<&f32>>
+ <f32 as Add<&f32>>
+ <f32 as Add>
help: consider using a floating-point literal by writing it with `.0`
|
LL | x + 100.0
|
= help: the trait `Add<{integer}>` is not implemented for `f64`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
<&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&f64 as Add<&f64>>
+ <f64 as Add<&f64>>
+ <f64 as Add>
help: consider using a floating-point literal by writing it with `.0`
|
LL | x + 100.0
= help: the trait `Sub<{integer}>` is not implemented for `f32`
= help: the following other types implement trait `Sub<Rhs>`:
<&'a f32 as Sub<f32>>
- <&'a f64 as Sub<f64>>
- <&'a i128 as Sub<i128>>
- <&'a i16 as Sub<i16>>
- <&'a i32 as Sub<i32>>
- <&'a i64 as Sub<i64>>
- <&'a i8 as Sub<i8>>
- <&'a isize as Sub<isize>>
- and 48 others
+ <&f32 as Sub<&f32>>
+ <f32 as Sub<&f32>>
+ <f32 as Sub>
help: consider using a floating-point literal by writing it with `.0`
|
LL | x - 100.0
|
= help: the trait `Sub<{integer}>` is not implemented for `f64`
= help: the following other types implement trait `Sub<Rhs>`:
- <&'a f32 as Sub<f32>>
<&'a f64 as Sub<f64>>
- <&'a i128 as Sub<i128>>
- <&'a i16 as Sub<i16>>
- <&'a i32 as Sub<i32>>
- <&'a i64 as Sub<i64>>
- <&'a i8 as Sub<i8>>
- <&'a isize as Sub<isize>>
- and 48 others
+ <&f64 as Sub<&f64>>
+ <f64 as Sub<&f64>>
+ <f64 as Sub>
help: consider using a floating-point literal by writing it with `.0`
|
LL | x - 100.0
= help: the trait `Mul<{integer}>` is not implemented for `f32`
= help: the following other types implement trait `Mul<Rhs>`:
<&'a f32 as Mul<f32>>
- <&'a f64 as Mul<f64>>
- <&'a i128 as Mul<i128>>
- <&'a i16 as Mul<i16>>
- <&'a i32 as Mul<i32>>
- <&'a i64 as Mul<i64>>
- <&'a i8 as Mul<i8>>
- <&'a isize as Mul<isize>>
- and 49 others
+ <&f32 as Mul<&f32>>
+ <f32 as Mul<&f32>>
+ <f32 as Mul>
help: consider using a floating-point literal by writing it with `.0`
|
LL | x * 100.0
|
= help: the trait `Mul<{integer}>` is not implemented for `f64`
= help: the following other types implement trait `Mul<Rhs>`:
- <&'a f32 as Mul<f32>>
<&'a f64 as Mul<f64>>
- <&'a i128 as Mul<i128>>
- <&'a i16 as Mul<i16>>
- <&'a i32 as Mul<i32>>
- <&'a i64 as Mul<i64>>
- <&'a i8 as Mul<i8>>
- <&'a isize as Mul<isize>>
- and 49 others
+ <&f64 as Mul<&f64>>
+ <f64 as Mul<&f64>>
+ <f64 as Mul>
help: consider using a floating-point literal by writing it with `.0`
|
LL | x * 100.0
= help: the trait `Div<{integer}>` is not implemented for `f32`
= help: the following other types implement trait `Div<Rhs>`:
<&'a f32 as Div<f32>>
- <&'a f64 as Div<f64>>
- <&'a i128 as Div<i128>>
- <&'a i16 as Div<i16>>
- <&'a i32 as Div<i32>>
- <&'a i64 as Div<i64>>
- <&'a i8 as Div<i8>>
- <&'a isize as Div<isize>>
- and 54 others
+ <&f32 as Div<&f32>>
+ <f32 as Div<&f32>>
+ <f32 as Div>
help: consider using a floating-point literal by writing it with `.0`
|
LL | x / 100.0
|
= help: the trait `Div<{integer}>` is not implemented for `f64`
= help: the following other types implement trait `Div<Rhs>`:
- <&'a f32 as Div<f32>>
<&'a f64 as Div<f64>>
- <&'a i128 as Div<i128>>
- <&'a i16 as Div<i16>>
- <&'a i32 as Div<i32>>
- <&'a i64 as Div<i64>>
- <&'a i8 as Div<i8>>
- <&'a isize as Div<isize>>
- and 54 others
+ <&f64 as Div<&f64>>
+ <f64 as Div<&f64>>
+ <f64 as Div>
help: consider using a floating-point literal by writing it with `.0`
|
LL | x / 100.0
--> $DIR/numeric-cast-binop.rs:23:16
|
LL | x_u8 > x_u16;
- | ^^^^^ expected `u8`, found `u16`
+ | ---- ^^^^^ expected `u8`, found `u16`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `u16`, matching the type of `x_u16`
|
--> $DIR/numeric-cast-binop.rs:25:16
|
LL | x_u8 > x_u32;
- | ^^^^^ expected `u8`, found `u32`
+ | ---- ^^^^^ expected `u8`, found `u32`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `u32`, matching the type of `x_u32`
|
--> $DIR/numeric-cast-binop.rs:27:16
|
LL | x_u8 > x_u64;
- | ^^^^^ expected `u8`, found `u64`
+ | ---- ^^^^^ expected `u8`, found `u64`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `u64`, matching the type of `x_u64`
|
--> $DIR/numeric-cast-binop.rs:29:16
|
LL | x_u8 > x_u128;
- | ^^^^^^ expected `u8`, found `u128`
+ | ---- ^^^^^^ expected `u8`, found `u128`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `u128`, matching the type of `x_u128`
|
--> $DIR/numeric-cast-binop.rs:31:16
|
LL | x_u8 > x_usize;
- | ^^^^^^^ expected `u8`, found `usize`
+ | ---- ^^^^^^^ expected `u8`, found `usize`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `usize`, matching the type of `x_usize`
|
--> $DIR/numeric-cast-binop.rs:34:17
|
LL | x_u16 > x_u8;
- | ^^^^ expected `u16`, found `u8`
+ | ----- ^^^^ expected `u16`, found `u8`
+ | |
+ | expected because this is `u16`
|
help: you can convert a `u8` to a `u16`
|
--> $DIR/numeric-cast-binop.rs:36:17
|
LL | x_u16 > x_u32;
- | ^^^^^ expected `u16`, found `u32`
+ | ----- ^^^^^ expected `u16`, found `u32`
+ | |
+ | expected because this is `u16`
|
help: you can convert `x_u16` from `u16` to `u32`, matching the type of `x_u32`
|
--> $DIR/numeric-cast-binop.rs:38:17
|
LL | x_u16 > x_u64;
- | ^^^^^ expected `u16`, found `u64`
+ | ----- ^^^^^ expected `u16`, found `u64`
+ | |
+ | expected because this is `u16`
|
help: you can convert `x_u16` from `u16` to `u64`, matching the type of `x_u64`
|
--> $DIR/numeric-cast-binop.rs:40:17
|
LL | x_u16 > x_u128;
- | ^^^^^^ expected `u16`, found `u128`
+ | ----- ^^^^^^ expected `u16`, found `u128`
+ | |
+ | expected because this is `u16`
|
help: you can convert `x_u16` from `u16` to `u128`, matching the type of `x_u128`
|
--> $DIR/numeric-cast-binop.rs:42:17
|
LL | x_u16 > x_usize;
- | ^^^^^^^ expected `u16`, found `usize`
+ | ----- ^^^^^^^ expected `u16`, found `usize`
+ | |
+ | expected because this is `u16`
|
help: you can convert `x_u16` from `u16` to `usize`, matching the type of `x_usize`
|
--> $DIR/numeric-cast-binop.rs:45:17
|
LL | x_u32 > x_u8;
- | ^^^^ expected `u32`, found `u8`
+ | ----- ^^^^ expected `u32`, found `u8`
+ | |
+ | expected because this is `u32`
|
help: you can convert a `u8` to a `u32`
|
--> $DIR/numeric-cast-binop.rs:47:17
|
LL | x_u32 > x_u16;
- | ^^^^^ expected `u32`, found `u16`
+ | ----- ^^^^^ expected `u32`, found `u16`
+ | |
+ | expected because this is `u32`
|
help: you can convert a `u16` to a `u32`
|
--> $DIR/numeric-cast-binop.rs:49:17
|
LL | x_u32 > x_u64;
- | ^^^^^ expected `u32`, found `u64`
+ | ----- ^^^^^ expected `u32`, found `u64`
+ | |
+ | expected because this is `u32`
|
help: you can convert `x_u32` from `u32` to `u64`, matching the type of `x_u64`
|
--> $DIR/numeric-cast-binop.rs:51:17
|
LL | x_u32 > x_u128;
- | ^^^^^^ expected `u32`, found `u128`
+ | ----- ^^^^^^ expected `u32`, found `u128`
+ | |
+ | expected because this is `u32`
|
help: you can convert `x_u32` from `u32` to `u128`, matching the type of `x_u128`
|
--> $DIR/numeric-cast-binop.rs:53:17
|
LL | x_u32 > x_usize;
- | ^^^^^^^ expected `u32`, found `usize`
+ | ----- ^^^^^^^ expected `u32`, found `usize`
+ | |
+ | expected because this is `u32`
|
help: you can convert a `usize` to a `u32` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:56:17
|
LL | x_u64 > x_u8;
- | ^^^^ expected `u64`, found `u8`
+ | ----- ^^^^ expected `u64`, found `u8`
+ | |
+ | expected because this is `u64`
|
help: you can convert a `u8` to a `u64`
|
--> $DIR/numeric-cast-binop.rs:58:17
|
LL | x_u64 > x_u16;
- | ^^^^^ expected `u64`, found `u16`
+ | ----- ^^^^^ expected `u64`, found `u16`
+ | |
+ | expected because this is `u64`
|
help: you can convert a `u16` to a `u64`
|
--> $DIR/numeric-cast-binop.rs:60:17
|
LL | x_u64 > x_u32;
- | ^^^^^ expected `u64`, found `u32`
+ | ----- ^^^^^ expected `u64`, found `u32`
+ | |
+ | expected because this is `u64`
|
help: you can convert a `u32` to a `u64`
|
--> $DIR/numeric-cast-binop.rs:62:17
|
LL | x_u64 > x_u128;
- | ^^^^^^ expected `u64`, found `u128`
+ | ----- ^^^^^^ expected `u64`, found `u128`
+ | |
+ | expected because this is `u64`
|
help: you can convert `x_u64` from `u64` to `u128`, matching the type of `x_u128`
|
--> $DIR/numeric-cast-binop.rs:64:17
|
LL | x_u64 > x_usize;
- | ^^^^^^^ expected `u64`, found `usize`
+ | ----- ^^^^^^^ expected `u64`, found `usize`
+ | |
+ | expected because this is `u64`
|
help: you can convert a `usize` to a `u64` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:67:18
|
LL | x_u128 > x_u8;
- | ^^^^ expected `u128`, found `u8`
+ | ------ ^^^^ expected `u128`, found `u8`
+ | |
+ | expected because this is `u128`
|
help: you can convert a `u8` to a `u128`
|
--> $DIR/numeric-cast-binop.rs:69:18
|
LL | x_u128 > x_u16;
- | ^^^^^ expected `u128`, found `u16`
+ | ------ ^^^^^ expected `u128`, found `u16`
+ | |
+ | expected because this is `u128`
|
help: you can convert a `u16` to a `u128`
|
--> $DIR/numeric-cast-binop.rs:71:18
|
LL | x_u128 > x_u32;
- | ^^^^^ expected `u128`, found `u32`
+ | ------ ^^^^^ expected `u128`, found `u32`
+ | |
+ | expected because this is `u128`
|
help: you can convert a `u32` to a `u128`
|
--> $DIR/numeric-cast-binop.rs:73:18
|
LL | x_u128 > x_u64;
- | ^^^^^ expected `u128`, found `u64`
+ | ------ ^^^^^ expected `u128`, found `u64`
+ | |
+ | expected because this is `u128`
|
help: you can convert a `u64` to a `u128`
|
--> $DIR/numeric-cast-binop.rs:75:18
|
LL | x_u128 > x_usize;
- | ^^^^^^^ expected `u128`, found `usize`
+ | ------ ^^^^^^^ expected `u128`, found `usize`
+ | |
+ | expected because this is `u128`
|
help: you can convert a `usize` to a `u128` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:78:19
|
LL | x_usize > x_u8;
- | ^^^^ expected `usize`, found `u8`
+ | ------- ^^^^ expected `usize`, found `u8`
+ | |
+ | expected because this is `usize`
|
help: you can convert a `u8` to a `usize`
|
--> $DIR/numeric-cast-binop.rs:80:19
|
LL | x_usize > x_u16;
- | ^^^^^ expected `usize`, found `u16`
+ | ------- ^^^^^ expected `usize`, found `u16`
+ | |
+ | expected because this is `usize`
|
help: you can convert a `u16` to a `usize`
|
--> $DIR/numeric-cast-binop.rs:82:19
|
LL | x_usize > x_u32;
- | ^^^^^ expected `usize`, found `u32`
+ | ------- ^^^^^ expected `usize`, found `u32`
+ | |
+ | expected because this is `usize`
|
help: you can convert a `u32` to a `usize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:84:19
|
LL | x_usize > x_u64;
- | ^^^^^ expected `usize`, found `u64`
+ | ------- ^^^^^ expected `usize`, found `u64`
+ | |
+ | expected because this is `usize`
|
help: you can convert a `u64` to a `usize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:86:19
|
LL | x_usize > x_u128;
- | ^^^^^^ expected `usize`, found `u128`
+ | ------- ^^^^^^ expected `usize`, found `u128`
+ | |
+ | expected because this is `usize`
|
help: you can convert a `u128` to a `usize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:92:16
|
LL | x_i8 > x_i16;
- | ^^^^^ expected `i8`, found `i16`
+ | ---- ^^^^^ expected `i8`, found `i16`
+ | |
+ | expected because this is `i8`
|
help: you can convert `x_i8` from `i8` to `i16`, matching the type of `x_i16`
|
--> $DIR/numeric-cast-binop.rs:94:16
|
LL | x_i8 > x_i32;
- | ^^^^^ expected `i8`, found `i32`
+ | ---- ^^^^^ expected `i8`, found `i32`
+ | |
+ | expected because this is `i8`
|
help: you can convert `x_i8` from `i8` to `i32`, matching the type of `x_i32`
|
--> $DIR/numeric-cast-binop.rs:96:16
|
LL | x_i8 > x_i64;
- | ^^^^^ expected `i8`, found `i64`
+ | ---- ^^^^^ expected `i8`, found `i64`
+ | |
+ | expected because this is `i8`
|
help: you can convert `x_i8` from `i8` to `i64`, matching the type of `x_i64`
|
--> $DIR/numeric-cast-binop.rs:98:16
|
LL | x_i8 > x_i128;
- | ^^^^^^ expected `i8`, found `i128`
+ | ---- ^^^^^^ expected `i8`, found `i128`
+ | |
+ | expected because this is `i8`
|
help: you can convert `x_i8` from `i8` to `i128`, matching the type of `x_i128`
|
--> $DIR/numeric-cast-binop.rs:100:16
|
LL | x_i8 > x_isize;
- | ^^^^^^^ expected `i8`, found `isize`
+ | ---- ^^^^^^^ expected `i8`, found `isize`
+ | |
+ | expected because this is `i8`
|
help: you can convert `x_i8` from `i8` to `isize`, matching the type of `x_isize`
|
--> $DIR/numeric-cast-binop.rs:103:17
|
LL | x_i16 > x_i8;
- | ^^^^ expected `i16`, found `i8`
+ | ----- ^^^^ expected `i16`, found `i8`
+ | |
+ | expected because this is `i16`
|
help: you can convert an `i8` to an `i16`
|
--> $DIR/numeric-cast-binop.rs:105:17
|
LL | x_i16 > x_i32;
- | ^^^^^ expected `i16`, found `i32`
+ | ----- ^^^^^ expected `i16`, found `i32`
+ | |
+ | expected because this is `i16`
|
help: you can convert `x_i16` from `i16` to `i32`, matching the type of `x_i32`
|
--> $DIR/numeric-cast-binop.rs:107:17
|
LL | x_i16 > x_i64;
- | ^^^^^ expected `i16`, found `i64`
+ | ----- ^^^^^ expected `i16`, found `i64`
+ | |
+ | expected because this is `i16`
|
help: you can convert `x_i16` from `i16` to `i64`, matching the type of `x_i64`
|
--> $DIR/numeric-cast-binop.rs:109:17
|
LL | x_i16 > x_i128;
- | ^^^^^^ expected `i16`, found `i128`
+ | ----- ^^^^^^ expected `i16`, found `i128`
+ | |
+ | expected because this is `i16`
|
help: you can convert `x_i16` from `i16` to `i128`, matching the type of `x_i128`
|
--> $DIR/numeric-cast-binop.rs:111:17
|
LL | x_i16 > x_isize;
- | ^^^^^^^ expected `i16`, found `isize`
+ | ----- ^^^^^^^ expected `i16`, found `isize`
+ | |
+ | expected because this is `i16`
|
help: you can convert `x_i16` from `i16` to `isize`, matching the type of `x_isize`
|
--> $DIR/numeric-cast-binop.rs:114:17
|
LL | x_i32 > x_i8;
- | ^^^^ expected `i32`, found `i8`
+ | ----- ^^^^ expected `i32`, found `i8`
+ | |
+ | expected because this is `i32`
|
help: you can convert an `i8` to an `i32`
|
--> $DIR/numeric-cast-binop.rs:116:17
|
LL | x_i32 > x_i16;
- | ^^^^^ expected `i32`, found `i16`
+ | ----- ^^^^^ expected `i32`, found `i16`
+ | |
+ | expected because this is `i32`
|
help: you can convert an `i16` to an `i32`
|
--> $DIR/numeric-cast-binop.rs:118:17
|
LL | x_i32 > x_i64;
- | ^^^^^ expected `i32`, found `i64`
+ | ----- ^^^^^ expected `i32`, found `i64`
+ | |
+ | expected because this is `i32`
|
help: you can convert `x_i32` from `i32` to `i64`, matching the type of `x_i64`
|
--> $DIR/numeric-cast-binop.rs:120:17
|
LL | x_i32 > x_i128;
- | ^^^^^^ expected `i32`, found `i128`
+ | ----- ^^^^^^ expected `i32`, found `i128`
+ | |
+ | expected because this is `i32`
|
help: you can convert `x_i32` from `i32` to `i128`, matching the type of `x_i128`
|
--> $DIR/numeric-cast-binop.rs:122:17
|
LL | x_i32 > x_isize;
- | ^^^^^^^ expected `i32`, found `isize`
+ | ----- ^^^^^^^ expected `i32`, found `isize`
+ | |
+ | expected because this is `i32`
|
help: you can convert an `isize` to an `i32` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:125:17
|
LL | x_i64 > x_i8;
- | ^^^^ expected `i64`, found `i8`
+ | ----- ^^^^ expected `i64`, found `i8`
+ | |
+ | expected because this is `i64`
|
help: you can convert an `i8` to an `i64`
|
--> $DIR/numeric-cast-binop.rs:127:17
|
LL | x_i64 > x_i16;
- | ^^^^^ expected `i64`, found `i16`
+ | ----- ^^^^^ expected `i64`, found `i16`
+ | |
+ | expected because this is `i64`
|
help: you can convert an `i16` to an `i64`
|
--> $DIR/numeric-cast-binop.rs:129:17
|
LL | x_i64 > x_i32;
- | ^^^^^ expected `i64`, found `i32`
+ | ----- ^^^^^ expected `i64`, found `i32`
+ | |
+ | expected because this is `i64`
|
help: you can convert an `i32` to an `i64`
|
--> $DIR/numeric-cast-binop.rs:131:17
|
LL | x_i64 > x_i128;
- | ^^^^^^ expected `i64`, found `i128`
+ | ----- ^^^^^^ expected `i64`, found `i128`
+ | |
+ | expected because this is `i64`
|
help: you can convert `x_i64` from `i64` to `i128`, matching the type of `x_i128`
|
--> $DIR/numeric-cast-binop.rs:133:17
|
LL | x_i64 > x_isize;
- | ^^^^^^^ expected `i64`, found `isize`
+ | ----- ^^^^^^^ expected `i64`, found `isize`
+ | |
+ | expected because this is `i64`
|
help: you can convert an `isize` to an `i64` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:136:18
|
LL | x_i128 > x_i8;
- | ^^^^ expected `i128`, found `i8`
+ | ------ ^^^^ expected `i128`, found `i8`
+ | |
+ | expected because this is `i128`
|
help: you can convert an `i8` to an `i128`
|
--> $DIR/numeric-cast-binop.rs:138:18
|
LL | x_i128 > x_i16;
- | ^^^^^ expected `i128`, found `i16`
+ | ------ ^^^^^ expected `i128`, found `i16`
+ | |
+ | expected because this is `i128`
|
help: you can convert an `i16` to an `i128`
|
--> $DIR/numeric-cast-binop.rs:140:18
|
LL | x_i128 > x_i32;
- | ^^^^^ expected `i128`, found `i32`
+ | ------ ^^^^^ expected `i128`, found `i32`
+ | |
+ | expected because this is `i128`
|
help: you can convert an `i32` to an `i128`
|
--> $DIR/numeric-cast-binop.rs:142:18
|
LL | x_i128 > x_i64;
- | ^^^^^ expected `i128`, found `i64`
+ | ------ ^^^^^ expected `i128`, found `i64`
+ | |
+ | expected because this is `i128`
|
help: you can convert an `i64` to an `i128`
|
--> $DIR/numeric-cast-binop.rs:144:18
|
LL | x_i128 > x_isize;
- | ^^^^^^^ expected `i128`, found `isize`
+ | ------ ^^^^^^^ expected `i128`, found `isize`
+ | |
+ | expected because this is `i128`
|
help: you can convert an `isize` to an `i128` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:147:19
|
LL | x_isize > x_i8;
- | ^^^^ expected `isize`, found `i8`
+ | ------- ^^^^ expected `isize`, found `i8`
+ | |
+ | expected because this is `isize`
|
help: you can convert an `i8` to an `isize`
|
--> $DIR/numeric-cast-binop.rs:149:19
|
LL | x_isize > x_i16;
- | ^^^^^ expected `isize`, found `i16`
+ | ------- ^^^^^ expected `isize`, found `i16`
+ | |
+ | expected because this is `isize`
|
help: you can convert an `i16` to an `isize`
|
--> $DIR/numeric-cast-binop.rs:151:19
|
LL | x_isize > x_i32;
- | ^^^^^ expected `isize`, found `i32`
+ | ------- ^^^^^ expected `isize`, found `i32`
+ | |
+ | expected because this is `isize`
|
help: you can convert an `i32` to an `isize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:153:19
|
LL | x_isize > x_i64;
- | ^^^^^ expected `isize`, found `i64`
+ | ------- ^^^^^ expected `isize`, found `i64`
+ | |
+ | expected because this is `isize`
|
help: you can convert an `i64` to an `isize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:155:19
|
LL | x_isize > x_i128;
- | ^^^^^^ expected `isize`, found `i128`
+ | ------- ^^^^^^ expected `isize`, found `i128`
+ | |
+ | expected because this is `isize`
|
help: you can convert an `i128` to an `isize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:161:16
|
LL | x_u8 > x_i8;
- | ^^^^ expected `u8`, found `i8`
+ | ---- ^^^^ expected `u8`, found `i8`
+ | |
+ | expected because this is `u8`
|
help: you can convert an `i8` to a `u8` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:163:16
|
LL | x_u8 > x_i16;
- | ^^^^^ expected `u8`, found `i16`
+ | ---- ^^^^^ expected `u8`, found `i16`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `i16`, matching the type of `x_i16`
|
--> $DIR/numeric-cast-binop.rs:165:16
|
LL | x_u8 > x_i32;
- | ^^^^^ expected `u8`, found `i32`
+ | ---- ^^^^^ expected `u8`, found `i32`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `i32`, matching the type of `x_i32`
|
--> $DIR/numeric-cast-binop.rs:167:16
|
LL | x_u8 > x_i64;
- | ^^^^^ expected `u8`, found `i64`
+ | ---- ^^^^^ expected `u8`, found `i64`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `i64`, matching the type of `x_i64`
|
--> $DIR/numeric-cast-binop.rs:169:16
|
LL | x_u8 > x_i128;
- | ^^^^^^ expected `u8`, found `i128`
+ | ---- ^^^^^^ expected `u8`, found `i128`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `i128`, matching the type of `x_i128`
|
--> $DIR/numeric-cast-binop.rs:171:16
|
LL | x_u8 > x_isize;
- | ^^^^^^^ expected `u8`, found `isize`
+ | ---- ^^^^^^^ expected `u8`, found `isize`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `isize`, matching the type of `x_isize`
|
--> $DIR/numeric-cast-binop.rs:174:17
|
LL | x_u16 > x_i8;
- | ^^^^ expected `u16`, found `i8`
+ | ----- ^^^^ expected `u16`, found `i8`
+ | |
+ | expected because this is `u16`
|
help: you can convert an `i8` to a `u16` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:176:17
|
LL | x_u16 > x_i16;
- | ^^^^^ expected `u16`, found `i16`
+ | ----- ^^^^^ expected `u16`, found `i16`
+ | |
+ | expected because this is `u16`
|
help: you can convert an `i16` to a `u16` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:178:17
|
LL | x_u16 > x_i32;
- | ^^^^^ expected `u16`, found `i32`
+ | ----- ^^^^^ expected `u16`, found `i32`
+ | |
+ | expected because this is `u16`
|
help: you can convert `x_u16` from `u16` to `i32`, matching the type of `x_i32`
|
--> $DIR/numeric-cast-binop.rs:180:17
|
LL | x_u16 > x_i64;
- | ^^^^^ expected `u16`, found `i64`
+ | ----- ^^^^^ expected `u16`, found `i64`
+ | |
+ | expected because this is `u16`
|
help: you can convert `x_u16` from `u16` to `i64`, matching the type of `x_i64`
|
--> $DIR/numeric-cast-binop.rs:182:17
|
LL | x_u16 > x_i128;
- | ^^^^^^ expected `u16`, found `i128`
+ | ----- ^^^^^^ expected `u16`, found `i128`
+ | |
+ | expected because this is `u16`
|
help: you can convert `x_u16` from `u16` to `i128`, matching the type of `x_i128`
|
--> $DIR/numeric-cast-binop.rs:184:17
|
LL | x_u16 > x_isize;
- | ^^^^^^^ expected `u16`, found `isize`
+ | ----- ^^^^^^^ expected `u16`, found `isize`
+ | |
+ | expected because this is `u16`
|
help: you can convert an `isize` to a `u16` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:187:17
|
LL | x_u32 > x_i8;
- | ^^^^ expected `u32`, found `i8`
+ | ----- ^^^^ expected `u32`, found `i8`
+ | |
+ | expected because this is `u32`
|
help: you can convert an `i8` to a `u32` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:189:17
|
LL | x_u32 > x_i16;
- | ^^^^^ expected `u32`, found `i16`
+ | ----- ^^^^^ expected `u32`, found `i16`
+ | |
+ | expected because this is `u32`
|
help: you can convert an `i16` to a `u32` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:191:17
|
LL | x_u32 > x_i32;
- | ^^^^^ expected `u32`, found `i32`
+ | ----- ^^^^^ expected `u32`, found `i32`
+ | |
+ | expected because this is `u32`
|
help: you can convert an `i32` to a `u32` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:193:17
|
LL | x_u32 > x_i64;
- | ^^^^^ expected `u32`, found `i64`
+ | ----- ^^^^^ expected `u32`, found `i64`
+ | |
+ | expected because this is `u32`
|
help: you can convert `x_u32` from `u32` to `i64`, matching the type of `x_i64`
|
--> $DIR/numeric-cast-binop.rs:195:17
|
LL | x_u32 > x_i128;
- | ^^^^^^ expected `u32`, found `i128`
+ | ----- ^^^^^^ expected `u32`, found `i128`
+ | |
+ | expected because this is `u32`
|
help: you can convert `x_u32` from `u32` to `i128`, matching the type of `x_i128`
|
--> $DIR/numeric-cast-binop.rs:197:17
|
LL | x_u32 > x_isize;
- | ^^^^^^^ expected `u32`, found `isize`
+ | ----- ^^^^^^^ expected `u32`, found `isize`
+ | |
+ | expected because this is `u32`
|
help: you can convert an `isize` to a `u32` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:200:17
|
LL | x_u64 > x_i8;
- | ^^^^ expected `u64`, found `i8`
+ | ----- ^^^^ expected `u64`, found `i8`
+ | |
+ | expected because this is `u64`
|
help: you can convert an `i8` to a `u64` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:202:17
|
LL | x_u64 > x_i16;
- | ^^^^^ expected `u64`, found `i16`
+ | ----- ^^^^^ expected `u64`, found `i16`
+ | |
+ | expected because this is `u64`
|
help: you can convert an `i16` to a `u64` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:204:17
|
LL | x_u64 > x_i32;
- | ^^^^^ expected `u64`, found `i32`
+ | ----- ^^^^^ expected `u64`, found `i32`
+ | |
+ | expected because this is `u64`
|
help: you can convert an `i32` to a `u64` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:206:17
|
LL | x_u64 > x_i64;
- | ^^^^^ expected `u64`, found `i64`
+ | ----- ^^^^^ expected `u64`, found `i64`
+ | |
+ | expected because this is `u64`
|
help: you can convert an `i64` to a `u64` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:208:17
|
LL | x_u64 > x_i128;
- | ^^^^^^ expected `u64`, found `i128`
+ | ----- ^^^^^^ expected `u64`, found `i128`
+ | |
+ | expected because this is `u64`
|
help: you can convert `x_u64` from `u64` to `i128`, matching the type of `x_i128`
|
--> $DIR/numeric-cast-binop.rs:210:17
|
LL | x_u64 > x_isize;
- | ^^^^^^^ expected `u64`, found `isize`
+ | ----- ^^^^^^^ expected `u64`, found `isize`
+ | |
+ | expected because this is `u64`
|
help: you can convert an `isize` to a `u64` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:213:18
|
LL | x_u128 > x_i8;
- | ^^^^ expected `u128`, found `i8`
+ | ------ ^^^^ expected `u128`, found `i8`
+ | |
+ | expected because this is `u128`
|
help: you can convert an `i8` to a `u128` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:215:18
|
LL | x_u128 > x_i16;
- | ^^^^^ expected `u128`, found `i16`
+ | ------ ^^^^^ expected `u128`, found `i16`
+ | |
+ | expected because this is `u128`
|
help: you can convert an `i16` to a `u128` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:217:18
|
LL | x_u128 > x_i32;
- | ^^^^^ expected `u128`, found `i32`
+ | ------ ^^^^^ expected `u128`, found `i32`
+ | |
+ | expected because this is `u128`
|
help: you can convert an `i32` to a `u128` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:219:18
|
LL | x_u128 > x_i64;
- | ^^^^^ expected `u128`, found `i64`
+ | ------ ^^^^^ expected `u128`, found `i64`
+ | |
+ | expected because this is `u128`
|
help: you can convert an `i64` to a `u128` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:221:18
|
LL | x_u128 > x_i128;
- | ^^^^^^ expected `u128`, found `i128`
+ | ------ ^^^^^^ expected `u128`, found `i128`
+ | |
+ | expected because this is `u128`
|
help: you can convert an `i128` to a `u128` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:223:18
|
LL | x_u128 > x_isize;
- | ^^^^^^^ expected `u128`, found `isize`
+ | ------ ^^^^^^^ expected `u128`, found `isize`
+ | |
+ | expected because this is `u128`
|
help: you can convert an `isize` to a `u128` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:226:19
|
LL | x_usize > x_i8;
- | ^^^^ expected `usize`, found `i8`
+ | ------- ^^^^ expected `usize`, found `i8`
+ | |
+ | expected because this is `usize`
|
help: you can convert an `i8` to a `usize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:228:19
|
LL | x_usize > x_i16;
- | ^^^^^ expected `usize`, found `i16`
+ | ------- ^^^^^ expected `usize`, found `i16`
+ | |
+ | expected because this is `usize`
|
help: you can convert an `i16` to a `usize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:230:19
|
LL | x_usize > x_i32;
- | ^^^^^ expected `usize`, found `i32`
+ | ------- ^^^^^ expected `usize`, found `i32`
+ | |
+ | expected because this is `usize`
|
help: you can convert an `i32` to a `usize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:232:19
|
LL | x_usize > x_i64;
- | ^^^^^ expected `usize`, found `i64`
+ | ------- ^^^^^ expected `usize`, found `i64`
+ | |
+ | expected because this is `usize`
|
help: you can convert an `i64` to a `usize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:234:19
|
LL | x_usize > x_i128;
- | ^^^^^^ expected `usize`, found `i128`
+ | ------- ^^^^^^ expected `usize`, found `i128`
+ | |
+ | expected because this is `usize`
|
help: you can convert an `i128` to a `usize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:236:19
|
LL | x_usize > x_isize;
- | ^^^^^^^ expected `usize`, found `isize`
+ | ------- ^^^^^^^ expected `usize`, found `isize`
+ | |
+ | expected because this is `usize`
|
help: you can convert an `isize` to a `usize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:242:16
|
LL | x_i8 > x_u8;
- | ^^^^ expected `i8`, found `u8`
+ | ---- ^^^^ expected `i8`, found `u8`
+ | |
+ | expected because this is `i8`
|
help: you can convert a `u8` to an `i8` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:244:16
|
LL | x_i8 > x_u16;
- | ^^^^^ expected `i8`, found `u16`
+ | ---- ^^^^^ expected `i8`, found `u16`
+ | |
+ | expected because this is `i8`
|
help: you can convert a `u16` to an `i8` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:246:16
|
LL | x_i8 > x_u32;
- | ^^^^^ expected `i8`, found `u32`
+ | ---- ^^^^^ expected `i8`, found `u32`
+ | |
+ | expected because this is `i8`
|
help: you can convert a `u32` to an `i8` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:248:16
|
LL | x_i8 > x_u64;
- | ^^^^^ expected `i8`, found `u64`
+ | ---- ^^^^^ expected `i8`, found `u64`
+ | |
+ | expected because this is `i8`
|
help: you can convert a `u64` to an `i8` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:250:16
|
LL | x_i8 > x_u128;
- | ^^^^^^ expected `i8`, found `u128`
+ | ---- ^^^^^^ expected `i8`, found `u128`
+ | |
+ | expected because this is `i8`
|
help: you can convert a `u128` to an `i8` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:252:16
|
LL | x_i8 > x_usize;
- | ^^^^^^^ expected `i8`, found `usize`
+ | ---- ^^^^^^^ expected `i8`, found `usize`
+ | |
+ | expected because this is `i8`
|
help: you can convert a `usize` to an `i8` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:255:17
|
LL | x_i16 > x_u8;
- | ^^^^ expected `i16`, found `u8`
+ | ----- ^^^^ expected `i16`, found `u8`
+ | |
+ | expected because this is `i16`
|
help: you can convert a `u8` to an `i16`
|
--> $DIR/numeric-cast-binop.rs:257:17
|
LL | x_i16 > x_u16;
- | ^^^^^ expected `i16`, found `u16`
+ | ----- ^^^^^ expected `i16`, found `u16`
+ | |
+ | expected because this is `i16`
|
help: you can convert a `u16` to an `i16` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:259:17
|
LL | x_i16 > x_u32;
- | ^^^^^ expected `i16`, found `u32`
+ | ----- ^^^^^ expected `i16`, found `u32`
+ | |
+ | expected because this is `i16`
|
help: you can convert a `u32` to an `i16` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:261:17
|
LL | x_i16 > x_u64;
- | ^^^^^ expected `i16`, found `u64`
+ | ----- ^^^^^ expected `i16`, found `u64`
+ | |
+ | expected because this is `i16`
|
help: you can convert a `u64` to an `i16` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:263:17
|
LL | x_i16 > x_u128;
- | ^^^^^^ expected `i16`, found `u128`
+ | ----- ^^^^^^ expected `i16`, found `u128`
+ | |
+ | expected because this is `i16`
|
help: you can convert a `u128` to an `i16` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:265:17
|
LL | x_i16 > x_usize;
- | ^^^^^^^ expected `i16`, found `usize`
+ | ----- ^^^^^^^ expected `i16`, found `usize`
+ | |
+ | expected because this is `i16`
|
help: you can convert a `usize` to an `i16` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:268:17
|
LL | x_i32 > x_u8;
- | ^^^^ expected `i32`, found `u8`
+ | ----- ^^^^ expected `i32`, found `u8`
+ | |
+ | expected because this is `i32`
|
help: you can convert a `u8` to an `i32`
|
--> $DIR/numeric-cast-binop.rs:270:17
|
LL | x_i32 > x_u16;
- | ^^^^^ expected `i32`, found `u16`
+ | ----- ^^^^^ expected `i32`, found `u16`
+ | |
+ | expected because this is `i32`
|
help: you can convert a `u16` to an `i32`
|
--> $DIR/numeric-cast-binop.rs:272:17
|
LL | x_i32 > x_u32;
- | ^^^^^ expected `i32`, found `u32`
+ | ----- ^^^^^ expected `i32`, found `u32`
+ | |
+ | expected because this is `i32`
|
help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:274:17
|
LL | x_i32 > x_u64;
- | ^^^^^ expected `i32`, found `u64`
+ | ----- ^^^^^ expected `i32`, found `u64`
+ | |
+ | expected because this is `i32`
|
help: you can convert a `u64` to an `i32` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:276:17
|
LL | x_i32 > x_u128;
- | ^^^^^^ expected `i32`, found `u128`
+ | ----- ^^^^^^ expected `i32`, found `u128`
+ | |
+ | expected because this is `i32`
|
help: you can convert a `u128` to an `i32` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:278:17
|
LL | x_i32 > x_usize;
- | ^^^^^^^ expected `i32`, found `usize`
+ | ----- ^^^^^^^ expected `i32`, found `usize`
+ | |
+ | expected because this is `i32`
|
help: you can convert a `usize` to an `i32` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:281:17
|
LL | x_i64 > x_u8;
- | ^^^^ expected `i64`, found `u8`
+ | ----- ^^^^ expected `i64`, found `u8`
+ | |
+ | expected because this is `i64`
|
help: you can convert a `u8` to an `i64`
|
--> $DIR/numeric-cast-binop.rs:283:17
|
LL | x_i64 > x_u16;
- | ^^^^^ expected `i64`, found `u16`
+ | ----- ^^^^^ expected `i64`, found `u16`
+ | |
+ | expected because this is `i64`
|
help: you can convert a `u16` to an `i64`
|
--> $DIR/numeric-cast-binop.rs:285:17
|
LL | x_i64 > x_u32;
- | ^^^^^ expected `i64`, found `u32`
+ | ----- ^^^^^ expected `i64`, found `u32`
+ | |
+ | expected because this is `i64`
|
help: you can convert a `u32` to an `i64`
|
--> $DIR/numeric-cast-binop.rs:287:17
|
LL | x_i64 > x_u64;
- | ^^^^^ expected `i64`, found `u64`
+ | ----- ^^^^^ expected `i64`, found `u64`
+ | |
+ | expected because this is `i64`
|
help: you can convert a `u64` to an `i64` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:289:17
|
LL | x_i64 > x_u128;
- | ^^^^^^ expected `i64`, found `u128`
+ | ----- ^^^^^^ expected `i64`, found `u128`
+ | |
+ | expected because this is `i64`
|
help: you can convert a `u128` to an `i64` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:291:17
|
LL | x_i64 > x_usize;
- | ^^^^^^^ expected `i64`, found `usize`
+ | ----- ^^^^^^^ expected `i64`, found `usize`
+ | |
+ | expected because this is `i64`
|
help: you can convert a `usize` to an `i64` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:294:18
|
LL | x_i128 > x_u8;
- | ^^^^ expected `i128`, found `u8`
+ | ------ ^^^^ expected `i128`, found `u8`
+ | |
+ | expected because this is `i128`
|
help: you can convert a `u8` to an `i128`
|
--> $DIR/numeric-cast-binop.rs:296:18
|
LL | x_i128 > x_u16;
- | ^^^^^ expected `i128`, found `u16`
+ | ------ ^^^^^ expected `i128`, found `u16`
+ | |
+ | expected because this is `i128`
|
help: you can convert a `u16` to an `i128`
|
--> $DIR/numeric-cast-binop.rs:298:18
|
LL | x_i128 > x_u32;
- | ^^^^^ expected `i128`, found `u32`
+ | ------ ^^^^^ expected `i128`, found `u32`
+ | |
+ | expected because this is `i128`
|
help: you can convert a `u32` to an `i128`
|
--> $DIR/numeric-cast-binop.rs:300:18
|
LL | x_i128 > x_u64;
- | ^^^^^ expected `i128`, found `u64`
+ | ------ ^^^^^ expected `i128`, found `u64`
+ | |
+ | expected because this is `i128`
|
help: you can convert a `u64` to an `i128`
|
--> $DIR/numeric-cast-binop.rs:302:18
|
LL | x_i128 > x_u128;
- | ^^^^^^ expected `i128`, found `u128`
+ | ------ ^^^^^^ expected `i128`, found `u128`
+ | |
+ | expected because this is `i128`
|
help: you can convert a `u128` to an `i128` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:304:18
|
LL | x_i128 > x_usize;
- | ^^^^^^^ expected `i128`, found `usize`
+ | ------ ^^^^^^^ expected `i128`, found `usize`
+ | |
+ | expected because this is `i128`
|
help: you can convert a `usize` to an `i128` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:307:19
|
LL | x_isize > x_u8;
- | ^^^^ expected `isize`, found `u8`
+ | ------- ^^^^ expected `isize`, found `u8`
+ | |
+ | expected because this is `isize`
|
help: you can convert a `u8` to an `isize`
|
--> $DIR/numeric-cast-binop.rs:309:19
|
LL | x_isize > x_u16;
- | ^^^^^ expected `isize`, found `u16`
+ | ------- ^^^^^ expected `isize`, found `u16`
+ | |
+ | expected because this is `isize`
|
help: you can convert a `u16` to an `isize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:311:19
|
LL | x_isize > x_u32;
- | ^^^^^ expected `isize`, found `u32`
+ | ------- ^^^^^ expected `isize`, found `u32`
+ | |
+ | expected because this is `isize`
|
help: you can convert a `u32` to an `isize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:313:19
|
LL | x_isize > x_u64;
- | ^^^^^ expected `isize`, found `u64`
+ | ------- ^^^^^ expected `isize`, found `u64`
+ | |
+ | expected because this is `isize`
|
help: you can convert a `u64` to an `isize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:315:19
|
LL | x_isize > x_u128;
- | ^^^^^^ expected `isize`, found `u128`
+ | ------- ^^^^^^ expected `isize`, found `u128`
+ | |
+ | expected because this is `isize`
|
help: you can convert a `u128` to an `isize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-binop.rs:317:19
|
LL | x_isize > x_usize;
- | ^^^^^^^ expected `isize`, found `usize`
+ | ------- ^^^^^^^ expected `isize`, found `usize`
+ | |
+ | expected because this is `isize`
|
help: you can convert a `usize` to an `isize` and panic if the converted value doesn't fit
|
--> $DIR/numeric-cast-no-fix.rs:10:15
|
LL | x_usize > -1_isize;
- | ^^^^^^^^ expected `usize`, found `isize`
+ | ------- ^^^^^^^^ expected `usize`, found `isize`
+ | |
+ | expected because this is `usize`
|
= note: `-1_isize` cannot fit into type `usize`
--> $DIR/numeric-cast-no-fix.rs:12:14
|
LL | x_u128 > -1_isize;
- | ^^^^^^^^ expected `u128`, found `isize`
+ | ------ ^^^^^^^^ expected `u128`, found `isize`
+ | |
+ | expected because this is `u128`
|
= note: `-1_isize` cannot fit into type `u128`
--> $DIR/numeric-cast-no-fix.rs:14:13
|
LL | x_u64 > -1_isize;
- | ^^^^^^^^ expected `u64`, found `isize`
+ | ----- ^^^^^^^^ expected `u64`, found `isize`
+ | |
+ | expected because this is `u64`
|
= note: `-1_isize` cannot fit into type `u64`
--> $DIR/numeric-cast-no-fix.rs:16:13
|
LL | x_u32 > -1_isize;
- | ^^^^^^^^ expected `u32`, found `isize`
+ | ----- ^^^^^^^^ expected `u32`, found `isize`
+ | |
+ | expected because this is `u32`
|
= note: `-1_isize` cannot fit into type `u32`
--> $DIR/numeric-cast-no-fix.rs:18:13
|
LL | x_u16 > -1_isize;
- | ^^^^^^^^ expected `u16`, found `isize`
+ | ----- ^^^^^^^^ expected `u16`, found `isize`
+ | |
+ | expected because this is `u16`
|
= note: `-1_isize` cannot fit into type `u16`
--> $DIR/numeric-cast-no-fix.rs:20:12
|
LL | x_u8 > -1_isize;
- | ^^^^^^^^ expected `u8`, found `isize`
+ | ---- ^^^^^^^^ expected `u8`, found `isize`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `isize`, matching the type of `-1_isize`
|
--> $DIR/numeric-cast-no-fix.rs:23:15
|
LL | x_usize > -1_i128;
- | ^^^^^^^ expected `usize`, found `i128`
+ | ------- ^^^^^^^ expected `usize`, found `i128`
+ | |
+ | expected because this is `usize`
|
= note: `-1_i128` cannot fit into type `usize`
--> $DIR/numeric-cast-no-fix.rs:25:14
|
LL | x_u128 > -1_i128;
- | ^^^^^^^ expected `u128`, found `i128`
+ | ------ ^^^^^^^ expected `u128`, found `i128`
+ | |
+ | expected because this is `u128`
|
= note: `-1_i128` cannot fit into type `u128`
--> $DIR/numeric-cast-no-fix.rs:27:13
|
LL | x_u64 > -1_i128;
- | ^^^^^^^ expected `u64`, found `i128`
+ | ----- ^^^^^^^ expected `u64`, found `i128`
+ | |
+ | expected because this is `u64`
|
help: you can convert `x_u64` from `u64` to `i128`, matching the type of `-1_i128`
|
--> $DIR/numeric-cast-no-fix.rs:29:13
|
LL | x_u32 > -1_i128;
- | ^^^^^^^ expected `u32`, found `i128`
+ | ----- ^^^^^^^ expected `u32`, found `i128`
+ | |
+ | expected because this is `u32`
|
help: you can convert `x_u32` from `u32` to `i128`, matching the type of `-1_i128`
|
--> $DIR/numeric-cast-no-fix.rs:31:13
|
LL | x_u16 > -1_i128;
- | ^^^^^^^ expected `u16`, found `i128`
+ | ----- ^^^^^^^ expected `u16`, found `i128`
+ | |
+ | expected because this is `u16`
|
help: you can convert `x_u16` from `u16` to `i128`, matching the type of `-1_i128`
|
--> $DIR/numeric-cast-no-fix.rs:33:12
|
LL | x_u8 > -1_i128;
- | ^^^^^^^ expected `u8`, found `i128`
+ | ---- ^^^^^^^ expected `u8`, found `i128`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `i128`, matching the type of `-1_i128`
|
--> $DIR/numeric-cast-no-fix.rs:36:15
|
LL | x_usize > -1_i64;
- | ^^^^^^ expected `usize`, found `i64`
+ | ------- ^^^^^^ expected `usize`, found `i64`
+ | |
+ | expected because this is `usize`
|
= note: `-1_i64` cannot fit into type `usize`
--> $DIR/numeric-cast-no-fix.rs:38:14
|
LL | x_u128 > -1_i64;
- | ^^^^^^ expected `u128`, found `i64`
+ | ------ ^^^^^^ expected `u128`, found `i64`
+ | |
+ | expected because this is `u128`
|
= note: `-1_i64` cannot fit into type `u128`
--> $DIR/numeric-cast-no-fix.rs:40:13
|
LL | x_u64 > -1_i64;
- | ^^^^^^ expected `u64`, found `i64`
+ | ----- ^^^^^^ expected `u64`, found `i64`
+ | |
+ | expected because this is `u64`
|
= note: `-1_i64` cannot fit into type `u64`
--> $DIR/numeric-cast-no-fix.rs:42:13
|
LL | x_u32 > -1_i64;
- | ^^^^^^ expected `u32`, found `i64`
+ | ----- ^^^^^^ expected `u32`, found `i64`
+ | |
+ | expected because this is `u32`
|
help: you can convert `x_u32` from `u32` to `i64`, matching the type of `-1_i64`
|
--> $DIR/numeric-cast-no-fix.rs:44:13
|
LL | x_u16 > -1_i64;
- | ^^^^^^ expected `u16`, found `i64`
+ | ----- ^^^^^^ expected `u16`, found `i64`
+ | |
+ | expected because this is `u16`
|
help: you can convert `x_u16` from `u16` to `i64`, matching the type of `-1_i64`
|
--> $DIR/numeric-cast-no-fix.rs:46:12
|
LL | x_u8 > -1_i64;
- | ^^^^^^ expected `u8`, found `i64`
+ | ---- ^^^^^^ expected `u8`, found `i64`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `i64`, matching the type of `-1_i64`
|
--> $DIR/numeric-cast-no-fix.rs:49:15
|
LL | x_usize > -1_i32;
- | ^^^^^^ expected `usize`, found `i32`
+ | ------- ^^^^^^ expected `usize`, found `i32`
+ | |
+ | expected because this is `usize`
|
= note: `-1_i32` cannot fit into type `usize`
--> $DIR/numeric-cast-no-fix.rs:51:14
|
LL | x_u128 > -1_i32;
- | ^^^^^^ expected `u128`, found `i32`
+ | ------ ^^^^^^ expected `u128`, found `i32`
+ | |
+ | expected because this is `u128`
|
= note: `-1_i32` cannot fit into type `u128`
--> $DIR/numeric-cast-no-fix.rs:53:13
|
LL | x_u64 > -1_i32;
- | ^^^^^^ expected `u64`, found `i32`
+ | ----- ^^^^^^ expected `u64`, found `i32`
+ | |
+ | expected because this is `u64`
|
= note: `-1_i32` cannot fit into type `u64`
--> $DIR/numeric-cast-no-fix.rs:55:13
|
LL | x_u32 > -1_i32;
- | ^^^^^^ expected `u32`, found `i32`
+ | ----- ^^^^^^ expected `u32`, found `i32`
+ | |
+ | expected because this is `u32`
|
= note: `-1_i32` cannot fit into type `u32`
--> $DIR/numeric-cast-no-fix.rs:57:13
|
LL | x_u16 > -1_i32;
- | ^^^^^^ expected `u16`, found `i32`
+ | ----- ^^^^^^ expected `u16`, found `i32`
+ | |
+ | expected because this is `u16`
|
help: you can convert `x_u16` from `u16` to `i32`, matching the type of `-1_i32`
|
--> $DIR/numeric-cast-no-fix.rs:59:12
|
LL | x_u8 > -1_i32;
- | ^^^^^^ expected `u8`, found `i32`
+ | ---- ^^^^^^ expected `u8`, found `i32`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `i32`, matching the type of `-1_i32`
|
--> $DIR/numeric-cast-no-fix.rs:62:15
|
LL | x_usize > -1_i16;
- | ^^^^^^ expected `usize`, found `i16`
+ | ------- ^^^^^^ expected `usize`, found `i16`
+ | |
+ | expected because this is `usize`
|
= note: `-1_i16` cannot fit into type `usize`
--> $DIR/numeric-cast-no-fix.rs:64:14
|
LL | x_u128 > -1_i16;
- | ^^^^^^ expected `u128`, found `i16`
+ | ------ ^^^^^^ expected `u128`, found `i16`
+ | |
+ | expected because this is `u128`
|
= note: `-1_i16` cannot fit into type `u128`
--> $DIR/numeric-cast-no-fix.rs:66:13
|
LL | x_u64 > -1_i16;
- | ^^^^^^ expected `u64`, found `i16`
+ | ----- ^^^^^^ expected `u64`, found `i16`
+ | |
+ | expected because this is `u64`
|
= note: `-1_i16` cannot fit into type `u64`
--> $DIR/numeric-cast-no-fix.rs:68:13
|
LL | x_u32 > -1_i16;
- | ^^^^^^ expected `u32`, found `i16`
+ | ----- ^^^^^^ expected `u32`, found `i16`
+ | |
+ | expected because this is `u32`
|
= note: `-1_i16` cannot fit into type `u32`
--> $DIR/numeric-cast-no-fix.rs:70:13
|
LL | x_u16 > -1_i16;
- | ^^^^^^ expected `u16`, found `i16`
+ | ----- ^^^^^^ expected `u16`, found `i16`
+ | |
+ | expected because this is `u16`
|
= note: `-1_i16` cannot fit into type `u16`
--> $DIR/numeric-cast-no-fix.rs:72:12
|
LL | x_u8 > -1_i16;
- | ^^^^^^ expected `u8`, found `i16`
+ | ---- ^^^^^^ expected `u8`, found `i16`
+ | |
+ | expected because this is `u8`
|
help: you can convert `x_u8` from `u8` to `i16`, matching the type of `-1_i16`
|
--> $DIR/numeric-cast-no-fix.rs:75:15
|
LL | x_usize > -1_i8;
- | ^^^^^ expected `usize`, found `i8`
+ | ------- ^^^^^ expected `usize`, found `i8`
+ | |
+ | expected because this is `usize`
|
= note: `-1_i8` cannot fit into type `usize`
--> $DIR/numeric-cast-no-fix.rs:77:14
|
LL | x_u128 > -1_i8;
- | ^^^^^ expected `u128`, found `i8`
+ | ------ ^^^^^ expected `u128`, found `i8`
+ | |
+ | expected because this is `u128`
|
= note: `-1_i8` cannot fit into type `u128`
--> $DIR/numeric-cast-no-fix.rs:79:13
|
LL | x_u64 > -1_i8;
- | ^^^^^ expected `u64`, found `i8`
+ | ----- ^^^^^ expected `u64`, found `i8`
+ | |
+ | expected because this is `u64`
|
= note: `-1_i8` cannot fit into type `u64`
--> $DIR/numeric-cast-no-fix.rs:81:13
|
LL | x_u32 > -1_i8;
- | ^^^^^ expected `u32`, found `i8`
+ | ----- ^^^^^ expected `u32`, found `i8`
+ | |
+ | expected because this is `u32`
|
= note: `-1_i8` cannot fit into type `u32`
--> $DIR/numeric-cast-no-fix.rs:83:13
|
LL | x_u16 > -1_i8;
- | ^^^^^ expected `u16`, found `i8`
+ | ----- ^^^^^ expected `u16`, found `i8`
+ | |
+ | expected because this is `u16`
|
= note: `-1_i8` cannot fit into type `u16`
--> $DIR/numeric-cast-no-fix.rs:85:12
|
LL | x_u8 > -1_i8;
- | ^^^^^ expected `u8`, found `i8`
+ | ---- ^^^^^ expected `u8`, found `i8`
+ | |
+ | expected because this is `u8`
|
= note: `-1_i8` cannot fit into type `u8`
= help: consider adding an explicit lifetime bound `Self: 'a`...
= note: ...so that the type `Self` will meet its required lifetime bounds...
note: ...that is required by this bound
- --> $DIR/object-safety-supertrait-mentions-GAT.rs:9:39
+ --> $DIR/object-safety-supertrait-mentions-GAT.rs:6:15
|
-LL | trait SuperTrait<T>: for<'a> GatTrait<Gat<'a> = T> {
- | ^^^^^^^^^^^
+LL | Self: 'a;
+ | ^^
error: associated item referring to unboxed trait object for its own trait
--> $DIR/object-safety-supertrait-mentions-GAT.rs:10:20
--- /dev/null
+// <https://github.com/rust-lang/rust/issues/105184>
+
+fn main() {
+ vec![(), ()].iter().sum::<i32>();
+ //~^ ERROR
+
+ vec![(), ()].iter().product::<i32>();
+ //~^ ERROR
+}
--- /dev/null
+error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `&()`
+ --> $DIR/sum.rs:4:5
+ |
+LL | vec![(), ()].iter().sum::<i32>();
+ | ^^^^^^^^^^^^^^^^^^^ --- required by a bound introduced by this call
+ | |
+ | value of type `i32` cannot be made by summing a `std::iter::Iterator<Item=&()>`
+ |
+ = help: the trait `Sum<&()>` is not implemented for `i32`
+ = help: the following other types implement trait `Sum<A>`:
+ <i32 as Sum<&'a i32>>
+ <i32 as Sum>
+note: required by a bound in `std::iter::Iterator::sum`
+ --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+ |
+LL | S: Sum<Self::Item>,
+ | ^^^^^^^^^^^^^^^ required by this bound in `Iterator::sum`
+
+error[E0277]: a value of type `i32` cannot be made by multiplying all elements of type `&()` from an iterator
+ --> $DIR/sum.rs:7:5
+ |
+LL | vec![(), ()].iter().product::<i32>();
+ | ^^^^^^^^^^^^^^^^^^^ ------- required by a bound introduced by this call
+ | |
+ | value of type `i32` cannot be made by multiplying all elements from a `std::iter::Iterator<Item=&()>`
+ |
+ = help: the trait `Product<&()>` is not implemented for `i32`
+ = help: the following other types implement trait `Product<A>`:
+ <i32 as Product<&'a i32>>
+ <i32 as Product>
+note: required by a bound in `std::iter::Iterator::product`
+ --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+ |
+LL | P: Product<Self::Item>,
+ | ^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::product`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--> $DIR/bare-struct-body.rs:11:14
|
LL | x.val == 42;
- | ^^ expected `()`, found integer
+ | ----- ^^ expected `()`, found integer
+ | |
+ | expected because this is `()`
error: aborting due to 3 previous errors
--> $DIR/chained-comparison-suggestion.rs:4:14
|
LL | 1 < 2 <= 3;
- | ^ expected `bool`, found integer
+ | ----- ^ expected `bool`, found integer
+ | |
+ | expected because this is `bool`
error[E0308]: mismatched types
--> $DIR/chained-comparison-suggestion.rs:13:14
|
LL | 1 <= 2 < 3;
- | ^ expected `bool`, found integer
+ | ------ ^ expected `bool`, found integer
+ | |
+ | expected because this is `bool`
error[E0308]: mismatched types
--> $DIR/chained-comparison-suggestion.rs:18:15
|
LL | 1 <= 2 <= 3;
- | ^ expected `bool`, found integer
+ | ------ ^ expected `bool`, found integer
+ | |
+ | expected because this is `bool`
error[E0308]: mismatched types
--> $DIR/chained-comparison-suggestion.rs:23:14
|
LL | 1 > 2 >= 3;
- | ^ expected `bool`, found integer
+ | ----- ^ expected `bool`, found integer
+ | |
+ | expected because this is `bool`
error[E0308]: mismatched types
--> $DIR/chained-comparison-suggestion.rs:36:15
|
LL | 1 >= 2 >= 3;
- | ^ expected `bool`, found integer
+ | ------ ^ expected `bool`, found integer
+ | |
+ | expected because this is `bool`
error[E0308]: mismatched types
--> $DIR/chained-comparison-suggestion.rs:49:15
|
LL | 1 == 2 == 3;
- | ^ expected `bool`, found integer
+ | ------ ^ expected `bool`, found integer
+ | |
+ | expected because this is `bool`
error: aborting due to 17 previous errors
{ foo(); } || { true } //~ ERROR E0308
}
+// https://github.com/rust-lang/rust/issues/105179
+fn r#match() -> i32 {
+ (match () { () => 1 }) + match () { () => 1 } //~ ERROR expected expression, found `+`
+ //~^ ERROR mismatched types
+}
+
+// https://github.com/rust-lang/rust/issues/102171
+fn r#unsafe() -> i32 {
+ (unsafe { 1 }) + unsafe { 1 } //~ ERROR expected expression, found `+`
+ //~^ ERROR mismatched types
+}
+
fn main() {}
{ foo() } || { true } //~ ERROR E0308
}
+// https://github.com/rust-lang/rust/issues/105179
+fn r#match() -> i32 {
+ match () { () => 1 } + match () { () => 1 } //~ ERROR expected expression, found `+`
+ //~^ ERROR mismatched types
+}
+
+// https://github.com/rust-lang/rust/issues/102171
+fn r#unsafe() -> i32 {
+ unsafe { 1 } + unsafe { 1 } //~ ERROR expected expression, found `+`
+ //~^ ERROR mismatched types
+}
+
fn main() {}
LL | ({ true }) | { true }
| + +
+error: expected expression, found `+`
+ --> $DIR/expr-as-stmt.rs:69:26
+ |
+LL | match () { () => 1 } + match () { () => 1 }
+ | ^ expected expression
+ |
+help: parentheses are required to parse this as an expression
+ |
+LL | (match () { () => 1 }) + match () { () => 1 }
+ | + +
+
+error: expected expression, found `+`
+ --> $DIR/expr-as-stmt.rs:75:18
+ |
+LL | unsafe { 1 } + unsafe { 1 }
+ | ^ expected expression
+ |
+help: parentheses are required to parse this as an expression
+ |
+LL | (unsafe { 1 }) + unsafe { 1 }
+ | + +
+
error[E0308]: mismatched types
--> $DIR/expr-as-stmt.rs:64:7
|
LL | ({ true }) || { true }
| + +
-error: aborting due to 18 previous errors
+error[E0308]: mismatched types
+ --> $DIR/expr-as-stmt.rs:69:5
+ |
+LL | match () { () => 1 } + match () { () => 1 }
+ | ^^^^^^^^^^^^^^^^^^^^- help: consider using a semicolon here
+ | |
+ | expected `()`, found integer
+
+error[E0308]: mismatched types
+ --> $DIR/expr-as-stmt.rs:75:14
+ |
+LL | unsafe { 1 } + unsafe { 1 }
+ | ^ expected `()`, found integer
+ |
+help: you might have meant to return this value
+ |
+LL | unsafe { return 1; } + unsafe { 1 }
+ | ++++++ +
+
+error: aborting due to 22 previous errors
Some errors have detailed explanations: E0308, E0600, E0614.
For more information about an error, try `rustc --explain E0308`.
|
LL | B == 2
| ^^ help: try using `=` instead
+ |
+ = help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
error: expected item, found `==`
--> $DIR/issue-101477-enum.rs:6:7
--- /dev/null
+enum VecOrMap{
+ vec: Vec<usize>,
+ //~^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `:`
+ //~| HELP: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
+ //~| ERROR expected item, found `:`
+ map: HashMap<String,usize>
+}
+
+fn main() {}
--- /dev/null
+error: expected one of `(`, `,`, `=`, `{`, or `}`, found `:`
+ --> $DIR/issue-103869.rs:2:8
+ |
+LL | vec: Vec<usize>,
+ | ^ expected one of `(`, `,`, `=`, `{`, or `}`
+ |
+ = help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
+
+error: expected item, found `:`
+ --> $DIR/issue-103869.rs:2:8
+ |
+LL | vec: Vec<usize>,
+ | ^ expected item
+
+error: aborting due to 2 previous errors
+
LL | test_macro!(String,);
| -------------------- in this macro invocation
|
+ = help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
= note: this error originates in the macro `test_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
--> $DIR/pptypedef.rs:4:37
|
LL | let_in(3u32, |i| { assert!(i == 3i32); });
- | ^^^^ expected `u32`, found `i32`
+ | - ^^^^ expected `u32`, found `i32`
+ | |
+ | expected because this is `u32`
|
help: change the type of the numeric literal from `i32` to `u32`
|
--> $DIR/pptypedef.rs:8:37
|
LL | let_in(3i32, |i| { assert!(i == 3u32); });
- | ^^^^ expected `i32`, found `u32`
+ | - ^^^^ expected `i32`, found `u32`
+ | |
+ | expected because this is `i32`
|
help: change the type of the numeric literal from `u32` to `i32`
|
--- /dev/null
+// compile-flags: -Z print-type-sizes
+// edition:2021
+// build-pass
+// ignore-pass
+
+#![feature(start)]
+
+async fn wait() {}
+
+async fn test(arg: [u8; 8192]) {
+ wait().await;
+ drop(arg);
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _ = test([0; 8192]);
+ 0
+}
--- /dev/null
+print-type-size type: `[async fn body@$DIR/async.rs:10:32: 13:2]`: 16386 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Suspend0`: 16385 bytes
+print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size field `.arg`: 8192 bytes
+print-type-size field `.__awaitee`: 1 bytes
+print-type-size variant `Unresumed`: 8192 bytes
+print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Returned`: 8192 bytes
+print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Panicked`: 8192 bytes
+print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::mem::ManuallyDrop<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes
+print-type-size field `.value`: 8192 bytes
+print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes
+print-type-size variant `MaybeUninit`: 8192 bytes
+print-type-size field `.uninit`: 0 bytes
+print-type-size field `.value`: 8192 bytes
+print-type-size type: `[async fn body@$DIR/async.rs:8:17: 8:19]`: 1 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Unresumed`: 0 bytes
+print-type-size variant `Returned`: 0 bytes
+print-type-size variant `Panicked`: 0 bytes
+print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes
+print-type-size field `.value`: 1 bytes
+print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes
+print-type-size variant `MaybeUninit`: 1 bytes
+print-type-size field `.uninit`: 0 bytes
+print-type-size field `.value`: 1 bytes
+print-type-size type: `std::task::Poll<()>`: 1 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Ready`: 0 bytes
+print-type-size field `.0`: 0 bytes
+print-type-size variant `Pending`: 0 bytes
--- /dev/null
+// compile-flags: -Z print-type-sizes
+// build-pass
+// ignore-pass
+
+#![feature(start, generators, generator_trait)]
+
+use std::ops::Generator;
+
+fn generator<const C: usize>(array: [u8; C]) -> impl Generator<Yield = (), Return = ()> {
+ move |()| {
+ yield ();
+ let _ = array;
+ }
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+ let _ = generator([0; 8192]);
+ 0
+}
--- /dev/null
+print-type-size type: `[generator@$DIR/generator.rs:10:5: 10:14]`: 8193 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Unresumed`: 8192 bytes
+print-type-size field `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Returned`: 8192 bytes
+print-type-size field `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Panicked`: 8192 bytes
+print-type-size field `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Suspend0`: 8192 bytes
+print-type-size field `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
#[rustc_effective_visibility]
pub use half_public_import::HalfPublicImport; //~ ERROR Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
- //~^ ERROR Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
fn main() {}
LL | pub use half_public_import::HalfPublicImport;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
- --> $DIR/effective_visibilities.rs:74:9
- |
-LL | pub use half_public_import::HalfPublicImport;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
--> $DIR/effective_visibilities.rs:14:13
|
LL | type B;
| ^^^^^^
-error: aborting due to 24 previous errors
+error: aborting due to 23 previous errors
--> $DIR/attr-invalid-exprs.rs:15:13
|
LL | let _ = #[duplicate] "Hello, world!";
- | ^^^^^^^^^^^^- help: you might be missing a semicolon here: `;`
- | |
- | caused by the macro expansion here
+ | ^^^^^^^^^^^^ caused by the macro expansion here
|
= note: the usage of `duplicate!` is likely invalid in expression context
+help: you might be missing a semicolon here
+ |
+LL | let _ = #[duplicate]; "Hello, world!";
+ | +
error: macro expansion ignores token `,` and any following
--> $DIR/attr-invalid-exprs.rs:24:9
|
LL | #[duplicate]
- | ^^^^^^^^^^^^- help: you might be missing a semicolon here: `;`
- | |
- | caused by the macro expansion here
+ | ^^^^^^^^^^^^ caused by the macro expansion here
|
= note: the usage of `duplicate!` is likely invalid in expression context
+help: you might be missing a semicolon here
+ |
+LL | #[duplicate];
+ | +
error: aborting due to 3 previous errors
pub fn foo12(input: TokenStream) -> TokenStream { input }
#[proc_macro_derive(d13, attributes("a"))]
-//~^ ERROR: not a meta item
+//~^ ERROR: attribute must be a meta item, not a literal
pub fn foo13(input: TokenStream) -> TokenStream { input }
#[proc_macro_derive(d14, attributes(a = ""))]
-//~^ ERROR: must only be one word
+//~^ ERROR: attribute must only be a single word
pub fn foo14(input: TokenStream) -> TokenStream { input }
#[proc_macro_derive(d15, attributes(m::a))]
-//~^ ERROR: must only be one word
+//~^ ERROR: attribute must only be a single word
pub fn foo15(input: TokenStream) -> TokenStream { input }
#[proc_macro_derive(d16, attributes(a(b)))]
-//~^ ERROR: must only be one word
+//~^ ERROR: attribute must only be a single word
pub fn foo16(input: TokenStream) -> TokenStream { input }
#[proc_macro_derive(d17, attributes(self))]
LL | #[proc_macro_derive(d12, attributes)]
| ^^^^^^^^^^
-error: not a meta item
+error: attribute must be a meta item, not a literal
--> $DIR/attribute.rs:55:37
|
LL | #[proc_macro_derive(d13, attributes("a"))]
| ^^^
-error: must only be one word
+error: attribute must only be a single word
--> $DIR/attribute.rs:59:37
|
LL | #[proc_macro_derive(d14, attributes(a = ""))]
| ^^^^^^
-error: must only be one word
+error: attribute must only be a single word
--> $DIR/attribute.rs:63:37
|
LL | #[proc_macro_derive(d15, attributes(m::a))]
| ^^^^
-error: must only be one word
+error: attribute must only be a single word
--> $DIR/attribute.rs:67:37
|
LL | #[proc_macro_derive(d16, attributes(a(b)))]
--> $DIR/expand-expr.rs:115:47
|
LL | expand_expr_is!("string", echo_tts!("string"; hello));
- | --------------------^^^^^-- help: you might be missing a semicolon here: `;`
- | |
- | caused by the macro expansion here
+ | --------------------^^^^^- caused by the macro expansion here
|
= note: the usage of `echo_tts!` is likely invalid in expression context
+help: you might be missing a semicolon here
+ |
+LL | expand_expr_is!("string", echo_tts!("string"; hello););
+ | +
error: macro expansion ignores token `;` and any following
--> $DIR/expand-expr.rs:116:44
|
LL | expand_expr_is!("string", echo_pm!("string"; hello));
- | -----------------^-------- help: you might be missing a semicolon here: `;`
- | |
- | caused by the macro expansion here
+ | -----------------^------- caused by the macro expansion here
|
= note: the usage of `echo_pm!` is likely invalid in expression context
+help: you might be missing a semicolon here
+ |
+LL | expand_expr_is!("string", echo_pm!("string"; hello););
+ | +
error: recursion limit reached while expanding `recursive_expand!`
--> $DIR/expand-expr.rs:124:16
--- /dev/null
+trait Dancer {
+ fn dance(&self) -> _ {
+ //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
+ self.dance()
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
+ --> $DIR/fn-sig-cycle-arity.rs:2:24
+ |
+LL | fn dance(&self) -> _ {
+ | ^ not allowed in type signatures
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0121`.
LL | take_range(std::ops::Range { start: 0, end: 1 });
| ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
- | | expected reference, found struct `std::ops::Range`
+ | | expected reference, found struct `Range`
| | help: consider borrowing here: `&std::ops::Range { start: 0, end: 1 }`
| arguments to this function are incorrect
|
LL | take_range(::std::ops::Range { start: 0, end: 1 });
| ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
- | | expected reference, found struct `std::ops::Range`
+ | | expected reference, found struct `Range`
| | help: consider borrowing here: `&::std::ops::Range { start: 0, end: 1 }`
| arguments to this function are incorrect
|
LL | take_range(0..1);
| ---------- ^^^^
| | |
- | | expected reference, found struct `std::ops::Range`
+ | | expected reference, found struct `Range`
| | help: consider borrowing here: `&(0..1)`
| arguments to this function are incorrect
|
LL | demo(tell(1)..tell(10));
| ---- ^^^^^^^^^^^^^^^^^
| | |
- | | expected reference, found struct `std::ops::Range`
+ | | expected `&Range<usize>`, found struct `Range`
| | help: consider borrowing here: `&(tell(1)..tell(10))`
| arguments to this function are incorrect
|
LL | demo(1..10);
| ---- ^^^^^
| | |
- | | expected reference, found struct `std::ops::Range`
+ | | expected `&Range<usize>`, found struct `Range`
| | help: consider borrowing here: `&(1..10)`
| arguments to this function are incorrect
|
let index_deref_ref = &raw const SLICE_REF[1];
let x = 0;
- let ascribe_ref = &raw const (x: i32);
- let ascribe_deref = &raw const (*ARRAY_REF: [i32; 2]);
- let ascribe_index_deref = &raw const (ARRAY_REF[0]: i32);
+ let ascribe_ref = &raw const type_ascribe!(x, i32);
+ let ascribe_deref = &raw const type_ascribe!(*ARRAY_REF, [i32; 2]);
+ let ascribe_index_deref = &raw const type_ascribe!(ARRAY_REF[0], i32);
}
const ARRAY: [i32; 2] = [1, 2];
fn main() {
- let ref_expr = &raw const 2; //~ ERROR cannot take address
- let mut_ref_expr = &raw mut 3; //~ ERROR cannot take address
- let ref_const = &raw const FOUR; //~ ERROR cannot take address
- let mut_ref_const = &raw mut FOUR; //~ ERROR cannot take address
-
- let field_ref_expr = &raw const (1, 2).0; //~ ERROR cannot take address
- let mut_field_ref_expr = &raw mut (1, 2).0; //~ ERROR cannot take address
- let field_ref = &raw const PAIR.0; //~ ERROR cannot take address
- let mut_field_ref = &raw mut PAIR.0; //~ ERROR cannot take address
-
- let index_ref_expr = &raw const [1, 2][0]; //~ ERROR cannot take address
- let mut_index_ref_expr = &raw mut [1, 2][0]; //~ ERROR cannot take address
- let index_ref = &raw const ARRAY[0]; //~ ERROR cannot take address
- let mut_index_ref = &raw mut ARRAY[1]; //~ ERROR cannot take address
-
- let ref_ascribe = &raw const (2: i32); //~ ERROR cannot take address
- let mut_ref_ascribe = &raw mut (3: i32); //~ ERROR cannot take address
-
- let ascribe_field_ref = &raw const (PAIR.0: i32); //~ ERROR cannot take address
- let ascribe_index_ref = &raw mut (ARRAY[0]: i32); //~ ERROR cannot take address
+ let ref_expr = &raw const 2; //~ ERROR cannot take address
+ let mut_ref_expr = &raw mut 3; //~ ERROR cannot take address
+ let ref_const = &raw const FOUR; //~ ERROR cannot take address
+ let mut_ref_const = &raw mut FOUR; //~ ERROR cannot take address
+
+ let field_ref_expr = &raw const (1, 2).0; //~ ERROR cannot take address
+ let mut_field_ref_expr = &raw mut (1, 2).0; //~ ERROR cannot take address
+ let field_ref = &raw const PAIR.0; //~ ERROR cannot take address
+ let mut_field_ref = &raw mut PAIR.0; //~ ERROR cannot take address
+
+ let index_ref_expr = &raw const [1, 2][0]; //~ ERROR cannot take address
+ let mut_index_ref_expr = &raw mut [1, 2][0]; //~ ERROR cannot take address
+ let index_ref = &raw const ARRAY[0]; //~ ERROR cannot take address
+ let mut_index_ref = &raw mut ARRAY[1]; //~ ERROR cannot take address
+
+ let ref_ascribe = &raw const type_ascribe!(2, i32); //~ ERROR cannot take address
+ let mut_ref_ascribe = &raw mut type_ascribe!(3, i32); //~ ERROR cannot take address
+
+ let ascribe_field_ref = &raw const type_ascribe!(PAIR.0, i32); //~ ERROR cannot take address
+ let ascribe_index_ref = &raw mut type_ascribe!(ARRAY[0], i32); //~ ERROR cannot take address
}
error[E0745]: cannot take address of a temporary
--> $DIR/raw-ref-temp.rs:26:34
|
-LL | let ref_ascribe = &raw const (2: i32);
- | ^^^^^^^^ temporary value
+LL | let ref_ascribe = &raw const type_ascribe!(2, i32);
+ | ^^^^^^^^^^^^^^^^^^^^^ temporary value
error[E0745]: cannot take address of a temporary
--> $DIR/raw-ref-temp.rs:27:36
|
-LL | let mut_ref_ascribe = &raw mut (3: i32);
- | ^^^^^^^^ temporary value
+LL | let mut_ref_ascribe = &raw mut type_ascribe!(3, i32);
+ | ^^^^^^^^^^^^^^^^^^^^^ temporary value
error[E0745]: cannot take address of a temporary
--> $DIR/raw-ref-temp.rs:29:40
|
-LL | let ascribe_field_ref = &raw const (PAIR.0: i32);
- | ^^^^^^^^^^^^^ temporary value
+LL | let ascribe_field_ref = &raw const type_ascribe!(PAIR.0, i32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value
error[E0745]: cannot take address of a temporary
--> $DIR/raw-ref-temp.rs:30:38
|
-LL | let ascribe_index_ref = &raw mut (ARRAY[0]: i32);
- | ^^^^^^^^^^^^^^^ temporary value
+LL | let ascribe_index_ref = &raw mut type_ascribe!(ARRAY[0], i32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value
error: aborting due to 16 previous errors
fn a() {
// the cast is unreachable:
- let x = {return}: !; //~ ERROR unreachable
+ let x = type_ascribe!({return}, !); //~ ERROR unreachable
}
fn main() { }
error: unreachable expression
--> $DIR/expr_type.rs:9:13
|
-LL | let x = {return}: !;
- | ^------^^^^
- | ||
- | |any code following this expression is unreachable
+LL | let x = type_ascribe!({return}, !);
+ | ^^^^^^^^^^^^^^^------^^^^^
+ | | |
+ | | any code following this expression is unreachable
| unreachable expression
|
note: the lint level is defined here
error[E0275]: overflow evaluating the requirement `Map<&mut Map<&mut Map<&mut Map<..., ...>, ...>, ...>, ...>: Iterator`
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_83150`)
- = note: required for `&mut Map<&mut Map<&mut Map<..., ...>, ...>, ...>` to implement `Iterator`
+ = note: required for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<..., ...>, ...>, ...>, ...>, ...>, ...>, ...>` to implement `Iterator`
= note: the full type name has been written to '$TEST_BUILD_DIR/recursion/issue-83150/issue-83150.long-type-hash.txt'
error: aborting due to previous error; 1 warning emitted
// This should fail because `T` ends up in the upvars of the closure.
fn bad_generic_fn<T: Copy>(t: T) {
assert_static(opaque(async move { t; }).next());
- //~^ ERROR the associated type `<impl Iterator as Iterator>::Item` may not live long enough
+ //~^ ERROR the parameter type `T` may not live long enough
assert_static(opaque(move || { t; }).next());
//~^ ERROR the associated type `<impl Iterator as Iterator>::Item` may not live long enough
assert_static(opaque(opaque(async move { t; }).next()).next());
- //~^ ERROR the associated type `<impl Iterator as Iterator>::Item` may not live long enough
+ //~^ ERROR the parameter type `T` may not live long enough
}
fn main() {}
-error[E0310]: the associated type `<impl Iterator as Iterator>::Item` may not live long enough
+error[E0310]: the parameter type `T` may not live long enough
--> $DIR/closure-in-projection-issue-97405.rs:24:5
|
LL | assert_static(opaque(async move { t; }).next());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `<impl Iterator as Iterator>::Item: 'static`...
- = note: ...so that the type `<impl Iterator as Iterator>::Item` will meet its required lifetime bounds
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn bad_generic_fn<T: Copy + 'static>(t: T) {
+ | +++++++++
error[E0310]: the associated type `<impl Iterator as Iterator>::Item` may not live long enough
--> $DIR/closure-in-projection-issue-97405.rs:26:5
= help: consider adding an explicit lifetime bound `<impl Iterator as Iterator>::Item: 'static`...
= note: ...so that the type `<impl Iterator as Iterator>::Item` will meet its required lifetime bounds
-error[E0310]: the associated type `<impl Iterator as Iterator>::Item` may not live long enough
+error[E0310]: the parameter type `T` may not live long enough
--> $DIR/closure-in-projection-issue-97405.rs:28:5
|
LL | assert_static(opaque(opaque(async move { t; }).next()).next());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `<impl Iterator as Iterator>::Item: 'static`...
- = note: ...so that the type `<impl Iterator as Iterator>::Item` will meet its required lifetime bounds
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn bad_generic_fn<T: Copy + 'static>(t: T) {
+ | +++++++++
error: aborting due to 3 previous errors
+// normalize-stderr-test: "long-type-\d+" -> "long-type-hash"
use std::cell::Cell;
#[rustfmt::skip]
error[E0308]: mismatched types
- --> $DIR/issue-102374.rs:16:5
+ --> $DIR/issue-102374.rs:17:5
|
LL | ) -> i32 {
| --- expected `i32` because of return type
| ^ expected `i32`, found fn pointer
|
= note: expected type `i32`
- found fn pointer `for<'z1, 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x, 'y, 'z, 'z0> fn(Cell<(&'z1 i32, &'a i32, &'b i32, &'c i32, &'d i32, &'e i32, &'f i32, &'g i32, &'h i32, &'i i32, &'j i32, &'k i32, &'l i32, &'m i32, &'n i32, &'o i32, &'p i32, &'q i32, &'r i32, &'s i32, &'t i32, &'u i32, &'v i32, &'w i32, &'x i32, &'y i32, &'z i32, &'z0 i32)>)`
+ found fn pointer `for<'z1, 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x, 'y, 'z, 'z0> fn(Cell<...>)`
+ the full type name has been written to '$TEST_BUILD_DIR/regions/issue-102374/issue-102374.long-type-hash.txt'
error: aborting due to previous error
-error[E0433]: failed to resolve: use of undeclared crate or module `thing`
- --> $DIR/bad-module.rs:2:15
- |
-LL | let foo = thing::len(Vec::new());
- | ^^^^^ use of undeclared crate or module `thing`
-
error[E0433]: failed to resolve: use of undeclared crate or module `foo`
--> $DIR/bad-module.rs:5:15
|
LL | let foo = foo::bar::baz();
| ^^^ use of undeclared crate or module `foo`
+error[E0433]: failed to resolve: use of undeclared crate or module `thing`
+ --> $DIR/bad-module.rs:2:15
+ |
+LL | let foo = thing::len(Vec::new());
+ | ^^^^^ use of undeclared crate or module `thing`
+
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0433`.
--- /dev/null
+struct Rectangle {
+ width: i32,
+ height: i32,
+}
+impl Rectangle {
+ fn new(width: i32, height: i32) -> Self {
+ Self { width, height }
+ }
+}
+
+fn main() {
+ let rect = Rectangle::new(3, 4);
+ // `area` is not implemented for `Rectangle`, so this should not suggest
+ let _ = rect::area();
+ //~^ ERROR failed to resolve: use of undeclared crate or module `rect`
+}
--- /dev/null
+error[E0433]: failed to resolve: use of undeclared crate or module `rect`
+ --> $DIR/issue-101749-2.rs:14:13
+ |
+LL | let _ = rect::area();
+ | ^^^^ use of undeclared crate or module `rect`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0433`.
--- /dev/null
+// run-rustfix
+struct Rectangle {
+ width: i32,
+ height: i32,
+}
+impl Rectangle {
+ fn new(width: i32, height: i32) -> Self {
+ Self { width, height }
+ }
+ fn area(&self) -> i32 {
+ self.height * self.width
+ }
+}
+
+fn main() {
+ let rect = Rectangle::new(3, 4);
+ let _ = rect.area();
+ //~^ ERROR failed to resolve: use of undeclared crate or module `rect`
+}
--- /dev/null
+// run-rustfix
+struct Rectangle {
+ width: i32,
+ height: i32,
+}
+impl Rectangle {
+ fn new(width: i32, height: i32) -> Self {
+ Self { width, height }
+ }
+ fn area(&self) -> i32 {
+ self.height * self.width
+ }
+}
+
+fn main() {
+ let rect = Rectangle::new(3, 4);
+ let _ = rect::area();
+ //~^ ERROR failed to resolve: use of undeclared crate or module `rect`
+}
--- /dev/null
+error[E0433]: failed to resolve: use of undeclared crate or module `rect`
+ --> $DIR/issue-101749.rs:17:13
+ |
+LL | let _ = rect::area();
+ | ^^^^ use of undeclared crate or module `rect`
+ |
+help: you may have meant to call an instance method
+ |
+LL | let _ = rect.area();
+ | ~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0433`.
--- /dev/null
+use self::A::*;
+use V; //~ ERROR `V` is ambiguous
+use self::B::*;
+enum A {
+ V
+}
+enum B {
+ V
+}
+
+fn main() {}
--- /dev/null
+error[E0659]: `V` is ambiguous
+ --> $DIR/issue-105069.rs:2:5
+ |
+LL | use V;
+ | ^ ambiguous name
+ |
+ = note: ambiguous because of multiple potential import sources
+note: `V` could refer to the variant imported here
+ --> $DIR/issue-105069.rs:1:5
+ |
+LL | use self::A::*;
+ | ^^^^^^^^^^
+note: `V` could also refer to the variant imported here
+ --> $DIR/issue-105069.rs:3:5
+ |
+LL | use self::B::*;
+ | ^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0659`.
-error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions
- --> $DIR/issue-24968.rs:21:19
- |
-LL | const FOO2: u32 = Self::bar();
- | ^^^^ `Self` is only available in impls, traits, and type definitions
-
-error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions
- --> $DIR/issue-24968.rs:27:22
- |
-LL | static FOO_S2: u32 = Self::bar();
- | ^^^^ `Self` is only available in impls, traits, and type definitions
-
error[E0411]: cannot find type `Self` in this scope
--> $DIR/issue-24968.rs:3:11
|
| |
| `Self` not allowed in a static item
+error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions
+ --> $DIR/issue-24968.rs:21:19
+ |
+LL | const FOO2: u32 = Self::bar();
+ | ^^^^ `Self` is only available in impls, traits, and type definitions
+
+error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions
+ --> $DIR/issue-24968.rs:27:22
+ |
+LL | static FOO_S2: u32 = Self::bar();
+ | ^^^^ `Self` is only available in impls, traits, and type definitions
+
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0411, E0433.
+error[E0433]: failed to resolve: could not find `Struc` in `module`
+ --> $DIR/typo-suggestion-mistyped-in-path.rs:35:13
+ |
+LL | module::Struc::foo();
+ | ^^^^^
+ | |
+ | could not find `Struc` in `module`
+ | help: a struct with a similar name exists: `Struct`
+
+error[E0599]: no function or associated item named `fob` found for struct `Struct` in the current scope
+ --> $DIR/typo-suggestion-mistyped-in-path.rs:23:13
+ |
+LL | struct Struct;
+ | ------------- function or associated item `fob` not found for this struct
+...
+LL | Struct::fob();
+ | ^^^
+ | |
+ | function or associated item not found in `Struct`
+ | help: there is an associated function with a similar name: `foo`
+
error[E0433]: failed to resolve: use of undeclared type `Struc`
--> $DIR/typo-suggestion-mistyped-in-path.rs:27:5
|
LL | module::foo();
| ~~~~~~
-error[E0433]: failed to resolve: could not find `Struc` in `module`
- --> $DIR/typo-suggestion-mistyped-in-path.rs:35:13
- |
-LL | module::Struc::foo();
- | ^^^^^
- | |
- | could not find `Struc` in `module`
- | help: a struct with a similar name exists: `Struct`
-
error[E0433]: failed to resolve: use of undeclared type `Trai`
--> $DIR/typo-suggestion-mistyped-in-path.rs:39:5
|
| use of undeclared type `Trai`
| help: a trait with a similar name exists: `Trait`
-error[E0599]: no function or associated item named `fob` found for struct `Struct` in the current scope
- --> $DIR/typo-suggestion-mistyped-in-path.rs:23:13
- |
-LL | struct Struct;
- | ------------- function or associated item `fob` not found for this struct
-...
-LL | Struct::fob();
- | ^^^
- | |
- | function or associated item not found in `Struct`
- | help: there is an associated function with a similar name: `foo`
-
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0433, E0599.
-error[E0433]: failed to resolve: use of undeclared type `GooMap`
- --> $DIR/use_suggestion.rs:3:14
- |
-LL | let x2 = GooMap::new();
- | ^^^^^^ use of undeclared type `GooMap`
-
error[E0433]: failed to resolve: use of undeclared type `HashMap`
--> $DIR/use_suggestion.rs:2:14
|
LL | let y2: GooMap;
| ^^^^^^ not found in this scope
+error[E0433]: failed to resolve: use of undeclared type `GooMap`
+ --> $DIR/use_suggestion.rs:3:14
+ |
+LL | let x2 = GooMap::new();
+ | ^^^^^^ use of undeclared type `GooMap`
+
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0412, E0433.
// edition:2018
fn main() {
- let _ = foo(true);
}
fn foo(x: bool) -> Result<f64, i32> {
}
Ok(42.0)
}
+
+trait Identity {
+ type Out;
+}
+
+impl<T> Identity for T {
+ type Out = T;
+}
+
+async fn foo2() -> i32 {
+ if true {
+ 1i32 //~ ERROR mismatched types
+ //| HELP you might have meant to return this value
+ }
+ 0
+}
error[E0308]: mismatched types
- --> $DIR/tail-expr-as-potential-return.rs:28:9
+ --> $DIR/tail-expr-as-potential-return.rs:27:9
|
LL | / if x {
LL | | Err(42)
| ++++++ +
error[E0308]: mismatched types
- --> $DIR/tail-expr-as-potential-return.rs:20:9
+ --> $DIR/tail-expr-as-potential-return.rs:43:9
+ |
+LL | / if true {
+LL | | 1i32
+ | | ^^^^ expected `()`, found `i32`
+LL | | //| HELP you might have meant to return this value
+LL | | }
+ | |_____- expected this to be `()`
+ |
+help: you might have meant to return this value
+ |
+LL | return 1i32;
+ | ++++++ +
+
+error[E0308]: mismatched types
+ --> $DIR/tail-expr-as-potential-return.rs:19:9
|
LL | / if x {
LL | | Err(42)
LL | return Err(42);
| ++++++ +
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0308`.
impl PartialEq for Foo {
fn eq(&self, _: &Foo) -> bool {
- false // ha ha sucker!
+ false // ha ha!
}
}
--> $DIR/disallowed-positions.rs:157:8
|
LL | if true..(let 0 = 0) {}
- | ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<bool>`
LL | if let Range { start: _, end: _ } = true..true && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool`
| |
- | expected `bool`, found struct `std::ops::Range`
+ | expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<_>`
--> $DIR/disallowed-positions.rs:171:8
|
LL | if let Range { start: _, end: _ } = true..true && false {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<bool>`
LL | if let Range { start: _, end: _ } = true..true || false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool`
| |
- | expected `bool`, found struct `std::ops::Range`
+ | expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<_>`
--> $DIR/disallowed-positions.rs:175:8
|
LL | if let Range { start: _, end: _ } = true..true || false {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<bool>`
LL | if let Range { start: F, end } = F..|| true {}
| ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool`
| |
- | expected fn pointer, found struct `std::ops::Range`
+ | expected fn pointer, found struct `Range`
|
= note: expected fn pointer `fn() -> bool`
found struct `std::ops::Range<_>`
--> $DIR/disallowed-positions.rs:182:8
|
LL | if let Range { start: F, end } = F..|| true {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<bool>`
LL | if let Range { start: true, end } = t..&&false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool`
| |
- | expected `bool`, found struct `std::ops::Range`
+ | expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<_>`
--> $DIR/disallowed-positions.rs:190:8
|
LL | if let Range { start: true, end } = t..&&false {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<bool>`
--> $DIR/disallowed-positions.rs:249:11
|
LL | while true..(let 0 = 0) {}
- | ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<bool>`
LL | while let Range { start: _, end: _ } = true..true && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool`
| |
- | expected `bool`, found struct `std::ops::Range`
+ | expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<_>`
--> $DIR/disallowed-positions.rs:263:11
|
LL | while let Range { start: _, end: _ } = true..true && false {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<bool>`
LL | while let Range { start: _, end: _ } = true..true || false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool`
| |
- | expected `bool`, found struct `std::ops::Range`
+ | expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<_>`
--> $DIR/disallowed-positions.rs:267:11
|
LL | while let Range { start: _, end: _ } = true..true || false {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<bool>`
LL | while let Range { start: F, end } = F..|| true {}
| ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool`
| |
- | expected fn pointer, found struct `std::ops::Range`
+ | expected fn pointer, found struct `Range`
|
= note: expected fn pointer `fn() -> bool`
found struct `std::ops::Range<_>`
--> $DIR/disallowed-positions.rs:274:11
|
LL | while let Range { start: F, end } = F..|| true {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<bool>`
LL | while let Range { start: true, end } = t..&&false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool`
| |
- | expected `bool`, found struct `std::ops::Range`
+ | expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<_>`
--> $DIR/disallowed-positions.rs:282:11
|
LL | while let Range { start: true, end } = t..&&false {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<bool>`
LL | (let Range { start: _, end: _ } = true..true || false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool`
| |
- | expected `bool`, found struct `std::ops::Range`
+ | expected `bool`, found struct `Range`
|
= note: expected type `bool`
found struct `std::ops::Range<_>`
--- /dev/null
+#![feature(const_fmt_arguments_new)]
+#![feature(const_trait_impl)]
+
+#[const_trait]
+trait Tr {
+ fn req(&self);
+
+ fn prov(&self) {
+ println!("lul"); //~ ERROR: cannot call non-const fn `_print` in constant functions
+ self.req();
+ }
+}
+
+struct S;
+
+impl const Tr for S {
+ fn req(&self) {}
+}
+
+fn main() {}
--- /dev/null
+error[E0015]: cannot call non-const fn `_print` in constant functions
+ --> $DIR/issue-79450.rs:9:9
+ |
+LL | println!("lul");
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0015`.
-// compile-flags: -Z simulate-remapped-rust-src-base=/rustc/xyz -Z translate-remapped-path-to-local-path=no -Z ui-testing=no
+// compile-flags: -Z simulate-remapped-rust-src-base=/rustc/FAKE_PREFIX -Z translate-remapped-path-to-local-path=no -Z ui-testing=no
struct MyError;
impl std::error::Error for MyError {}
= help: the trait `std::fmt::Display` is not implemented for `MyError`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
note: required by a bound in `std::error::Error`
+ --> $SRC_DIR/core/src/error.rs:LL:COL
error[E0277]: `MyError` doesn't implement `Debug`
--> $DIR/issue-71363.rs:4:6
= help: the trait `Debug` is not implemented for `MyError`
= note: add `#[derive(Debug)]` to `MyError` or manually `impl Debug for MyError`
note: required by a bound in `std::error::Error`
+ --> $SRC_DIR/core/src/error.rs:LL:COL
help: consider annotating `MyError` with `#[derive(Debug)]`
|
3 | #[derive(Debug)]
|
= help: the trait `Add<()>` is not implemented for `u32`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&'a u32 as Add<u32>>
+ <&u32 as Add<&u32>>
+ <u32 as Add<&u32>>
+ <u32 as Add>
error: aborting due to previous error
hir-stats ----------------------------------------------------------------
hir-stats ForeignItemRef 24 ( 0.3%) 1 24
hir-stats Lifetime 24 ( 0.3%) 1 24
-hir-stats Mod 32 ( 0.3%) 1 32
+hir-stats Mod 32 ( 0.4%) 1 32
hir-stats ExprField 40 ( 0.4%) 1 40
hir-stats TraitItemRef 56 ( 0.6%) 2 28
hir-stats Local 64 ( 0.7%) 1 64
hir-stats Param 64 ( 0.7%) 2 32
hir-stats InlineAsm 72 ( 0.8%) 1 72
hir-stats ImplItemRef 72 ( 0.8%) 2 36
-hir-stats Body 96 ( 1.0%) 3 32
-hir-stats FieldDef 96 ( 1.0%) 2 48
-hir-stats Arm 96 ( 1.0%) 2 48
-hir-stats Stmt 96 ( 1.0%) 3 32
-hir-stats - Local 32 ( 0.3%) 1
-hir-stats - Semi 32 ( 0.3%) 1
-hir-stats - Expr 32 ( 0.3%) 1
+hir-stats Body 96 ( 1.1%) 3 32
+hir-stats FieldDef 96 ( 1.1%) 2 48
+hir-stats Arm 96 ( 1.1%) 2 48
+hir-stats Stmt 96 ( 1.1%) 3 32
+hir-stats - Local 32 ( 0.4%) 1
+hir-stats - Semi 32 ( 0.4%) 1
+hir-stats - Expr 32 ( 0.4%) 1
hir-stats FnDecl 120 ( 1.3%) 3 40
hir-stats Attribute 128 ( 1.4%) 4 32
hir-stats GenericArg 128 ( 1.4%) 4 32
-hir-stats - Type 32 ( 0.3%) 1
-hir-stats - Lifetime 96 ( 1.0%) 3
+hir-stats - Type 32 ( 0.4%) 1
+hir-stats - Lifetime 96 ( 1.1%) 3
hir-stats GenericArgs 144 ( 1.6%) 3 48
hir-stats Variant 176 ( 1.9%) 2 88
hir-stats GenericBound 192 ( 2.1%) 4 48
hir-stats - Trait 192 ( 2.1%) 4
hir-stats WherePredicate 192 ( 2.1%) 3 64
hir-stats - BoundPredicate 192 ( 2.1%) 3
-hir-stats Block 288 ( 3.1%) 6 48
-hir-stats Pat 360 ( 3.9%) 5 72
+hir-stats Block 288 ( 3.2%) 6 48
+hir-stats Pat 360 ( 4.0%) 5 72
hir-stats - Wild 72 ( 0.8%) 1
hir-stats - Struct 72 ( 0.8%) 1
hir-stats - Binding 216 ( 2.4%) 3
hir-stats GenericParam 400 ( 4.4%) 5 80
-hir-stats Generics 560 ( 6.1%) 10 56
-hir-stats Ty 720 ( 7.9%) 15 48
+hir-stats Generics 560 ( 6.2%) 10 56
+hir-stats Ty 720 ( 8.0%) 15 48
hir-stats - Ptr 48 ( 0.5%) 1
hir-stats - Rptr 48 ( 0.5%) 1
-hir-stats - Path 624 ( 6.8%) 13
-hir-stats Expr 768 ( 8.4%) 12 64
+hir-stats - Path 624 ( 6.9%) 13
+hir-stats Expr 768 ( 8.5%) 12 64
hir-stats - Path 64 ( 0.7%) 1
hir-stats - Struct 64 ( 0.7%) 1
hir-stats - Match 64 ( 0.7%) 1
hir-stats - InlineAsm 64 ( 0.7%) 1
hir-stats - Lit 128 ( 1.4%) 2
hir-stats - Block 384 ( 4.2%) 6
-hir-stats Item 960 (10.5%) 12 80
+hir-stats Item 880 ( 9.7%) 11 80
hir-stats - Trait 80 ( 0.9%) 1
hir-stats - Enum 80 ( 0.9%) 1
hir-stats - ExternCrate 80 ( 0.9%) 1
hir-stats - ForeignMod 80 ( 0.9%) 1
hir-stats - Impl 80 ( 0.9%) 1
-hir-stats - Fn 160 ( 1.7%) 2
-hir-stats - Use 400 ( 4.4%) 5
-hir-stats Path 1_280 (14.0%) 32 40
-hir-stats PathSegment 1_920 (20.9%) 40 48
+hir-stats - Fn 160 ( 1.8%) 2
+hir-stats - Use 320 ( 3.5%) 4
+hir-stats Path 1_240 (13.7%) 31 40
+hir-stats PathSegment 1_920 (21.2%) 40 48
hir-stats ----------------------------------------------------------------
-hir-stats Total 9_168
+hir-stats Total 9_048
hir-stats
//~^ ERROR functions are not allowed in enum definitions
//~| HELP unlike in C++, Java, and C#, functions are declared in `impl` blocks
//~| HELP see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
+ //~| HELP enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
}
fn main() {}
|
= help: unlike in C++, Java, and C#, functions are declared in `impl` blocks
= help: see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
+ = help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
error: aborting due to 3 previous errors
--> $DIR/struct-record-suggestion.rs:23:20
|
LL | let q = B { b: 1..Default::default() };
- | ^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found struct `std::ops::Range`
+ | ^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found struct `Range`
|
= note: expected type `u32`
found struct `std::ops::Range<{integer}>`
--- /dev/null
+struct S {
+ a: u32,
+}
+
+fn main() {
+ let s1 = S { a: 1 };
+
+ let _ = || {
+ let s2 = Oops { a: 2, ..s1 };
+ //~^ ERROR cannot find struct, variant or union type `Oops` in this scope
+ };
+}
--- /dev/null
+error[E0422]: cannot find struct, variant or union type `Oops` in this scope
+ --> $DIR/unresolved-struct-with-fru.rs:9:18
+ |
+LL | let s2 = Oops { a: 2, ..s1 };
+ | ^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0422`.
--- /dev/null
+unsafe fn pointer(v: usize, w: u32) {}
+
+pub trait UniformScalar {}
+impl UniformScalar for u32 {}
+
+pub trait GlUniformScalar: UniformScalar {
+ const FACTORY: unsafe fn(usize, Self) -> ();
+}
+impl GlUniformScalar for u32 {
+ const FACTORY: unsafe fn(usize, Self) -> () = pointer;
+}
+
+pub fn foo<T: UniformScalar>(value: T) {
+ <T as GlUniformScalar>::FACTORY(1, value);
+ //~^ ERROR the trait bound `T: GlUniformScalar` is not satisfied
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `T: GlUniformScalar` is not satisfied
+ --> $DIR/assoc-const-as-fn.rs:14:40
+ |
+LL | <T as GlUniformScalar>::FACTORY(1, value);
+ | ------------------------------- ^^^^^ the trait `GlUniformScalar` is not implemented for `T`
+ | |
+ | required by a bound introduced by this call
+ |
+help: consider further restricting this bound
+ |
+LL | pub fn foo<T: UniformScalar + GlUniformScalar>(value: T) {
+ | +++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
- | ^ required by this bound in `std::mem::size_of`
+ | ^ required by this bound in `size_of`
help: consider further restricting `Self`
|
LL | trait Foo<T>: Sized {
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
- | ^ required by this bound in `std::mem::size_of`
+ | ^ required by this bound in `size_of`
help: consider further restricting `Self`
|
LL | trait Bar: std::fmt::Display + Sized {
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
- | ^ required by this bound in `std::mem::size_of`
+ | ^ required by this bound in `size_of`
help: consider further restricting `Self`
|
LL | trait Baz: Sized where Self: std::fmt::Display {
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
- | ^ required by this bound in `std::mem::size_of`
+ | ^ required by this bound in `size_of`
help: consider further restricting `Self`
|
LL | trait Qux<T>: Sized where Self: std::fmt::Display {
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
- | ^ required by this bound in `std::mem::size_of`
+ | ^ required by this bound in `size_of`
help: consider further restricting `Self`
|
LL | trait Bat<T>: std::fmt::Display + Sized {
LL | use bar::bar;
| ~~~
-error[E0433]: failed to resolve: use of undeclared crate or module `bar`
- --> $DIR/crate-or-module-typo.rs:6:20
- |
-LL | pub fn bar() { bar::baz(); }
- | ^^^ use of undeclared crate or module `bar`
-
error[E0433]: failed to resolve: use of undeclared crate or module `st`
--> $DIR/crate-or-module-typo.rs:14:10
|
LL | bar: std::cell::Cell<bool>
| ~~~
+error[E0433]: failed to resolve: use of undeclared crate or module `bar`
+ --> $DIR/crate-or-module-typo.rs:6:20
+ |
+LL | pub fn bar() { bar::baz(); }
+ | ^^^ use of undeclared crate or module `bar`
+
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0432, E0433.
--> $DIR/dont-suggest-try_into-in-macros.rs:2:5
|
LL | assert_eq!(10u64, 10usize);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found `usize`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | expected `u64`, found `usize`
+ | expected because this is `u64`
|
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
--> $DIR/dont-suggest-ufcs-for-const.rs:2:11
|
LL | 1_u32.MAX();
- | ------^^^--
- | | |
- | | this is an associated function, not a method
- | help: use associated function syntax instead: `u32::MAX()`
- |
- = note: found the following associated functions; to be used as methods, functions must have a `self` parameter
- = note: the candidate is defined in an impl for the type `u32`
+ | ^^^ method not found in `u32`
error: aborting due to previous error
--- /dev/null
+use std::fmt;
+
+struct S {
+}
+
+impl S {
+ fn hello<P>(&self, val: &P) where P: fmt::Display; {
+ //~^ ERROR non-item in item list
+ //~| ERROR associated function in `impl` without body
+ println!("val: {}", val);
+ }
+}
+
+impl S {
+ fn hello_empty<P>(&self, val: &P) where P: fmt::Display;
+ //~^ ERROR associated function in `impl` without body
+}
+
+fn main() {
+ let s = S{};
+ s.hello(&32);
+}
--- /dev/null
+error: non-item in item list
+ --> $DIR/issue-105226.rs:7:56
+ |
+LL | impl S {
+ | - item list starts here
+LL | fn hello<P>(&self, val: &P) where P: fmt::Display; {
+ | - ^ non-item starts here
+ | |
+ | help: consider removing this semicolon
+...
+LL | }
+ | - item list ends here
+
+error: associated function in `impl` without body
+ --> $DIR/issue-105226.rs:7:5
+ |
+LL | fn hello<P>(&self, val: &P) where P: fmt::Display; {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: provide a definition for the function: `{ <body> }`
+
+error: associated function in `impl` without body
+ --> $DIR/issue-105226.rs:15:5
+ |
+LL | fn hello_empty<P>(&self, val: &P) where P: fmt::Display;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: provide a definition for the function: `{ <body> }`
+
+error: aborting due to 3 previous errors
+
--> $DIR/option-to-bool.rs:4:16
|
LL | if true && x {}
- | ^ expected `bool`, found enum `Option`
+ | ---- ^ expected `bool`, found enum `Option`
+ | |
+ | expected because this is `bool`
|
= note: expected type `bool`
found enum `Option<i32>`
return foo;
}
+// issue #105028, suggest removing the field only for shorthand
+fn use_match(x: Foo) {
+ match x {
+ Foo { foo: unused, .. } => { //~ WARNING unused variable
+ //~| help: if this is intentional, prefix it with an underscore
+ }
+ }
+
+ match x {
+ Foo { foo, .. } => { //~ WARNING unused variable
+ //~| help: try removing the field
+ }
+ }
+}
+
fn main() {}
|
= note: `#[warn(unused_variables)]` on by default
-warning: 1 warning emitted
+warning: unused variable: `unused`
+ --> $DIR/try-removing-the-field.rs:20:20
+ |
+LL | Foo { foo: unused, .. } => {
+ | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused`
+
+warning: unused variable: `foo`
+ --> $DIR/try-removing-the-field.rs:26:15
+ |
+LL | Foo { foo, .. } => {
+ | ^^^-
+ | |
+ | help: try removing the field
+
+warning: 3 warnings emitted
--> $DIR/unnecessary_dot_for_floating_point_literal.rs:2:18
|
LL | let _: f64 = 0..10;
- | --- ^^^^^ expected `f64`, found struct `std::ops::Range`
+ | --- ^^^^^ expected `f64`, found struct `Range`
| |
| expected due to this
|
--> $DIR/unnecessary_dot_for_floating_point_literal.rs:5:18
|
LL | let _: f64 = std::ops::Range { start: 0, end: 1 };
- | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `f64`, found struct `std::ops::Range`
+ | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `f64`, found struct `Range`
| |
| expected due to this
|
--- /dev/null
+// Regression test for #105138.
+// This test ensures that the compiler does not add note
+// for implementation of trait whose inner type is erroneous.
+
+pub enum LabelText {
+ Plain,
+}
+
+impl<T> From<T> for LabelText
+//~^ ERROR conflicting implementations of trait `From<LabelText>` for type `LabelText` [E0119]
+where
+ T: Into<Cow<'static, str>>,
+ //~^ ERROR cannot find type `Cow` in this scope [E0412]
+{
+ fn from(text: T) -> Self {
+ LabelText::Plain(text.into())
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0412]: cannot find type `Cow` in this scope
+ --> $DIR/impl-bound-with-references-error.rs:12:13
+ |
+LL | T: Into<Cow<'static, str>>,
+ | ^^^ not found in this scope
+ |
+help: consider importing this enum
+ |
+LL | use std::borrow::Cow;
+ |
+
+error[E0119]: conflicting implementations of trait `From<LabelText>` for type `LabelText`
+ --> $DIR/impl-bound-with-references-error.rs:9:1
+ |
+LL | impl<T> From<T> for LabelText
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: conflicting implementation in crate `core`:
+ - impl<T> From<T> for T;
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0119, E0412.
+For more information about an error, try `rustc --explain E0119`.
--- /dev/null
+trait Trait<T> {
+ fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a;
+}
+
+impl Trait<()> for () {
+ fn foo<'a, K>(self, _: (), _: K) where { //~ ERROR E0195
+ todo!();
+ }
+}
+
+struct State;
+
+trait Foo<T> {
+ fn foo<'a>(&self, state: &'a State) -> &'a T
+ where
+ T: 'a;
+}
+
+impl<F, T> Foo<T> for F
+where
+ F: Fn(&State) -> &T,
+{
+ fn foo<'a>(&self, state: &'a State) -> &'a T { //~ ERROR E0195
+ self(state)
+ }
+}
+
+trait Bar {
+ fn foo<'a>(&'a self) {}
+}
+
+impl Bar for () {
+ fn foo<'a: 'a>(&'a self) {} //~ ERROR E0195
+}
+
+fn main() {
+ ().foo((), ());
+}
--- /dev/null
+error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
+ --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:6:11
+ |
+LL | fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a;
+ | ------- -- -- this bound might be missing in the impl
+ | | |
+ | | this bound might be missing in the impl
+ | lifetimes in impl do not match this method in trait
+...
+LL | fn foo<'a, K>(self, _: (), _: K) where {
+ | ^^^^^^^ lifetimes do not match method in trait
+
+error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
+ --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:23:11
+ |
+LL | fn foo<'a>(&self, state: &'a State) -> &'a T
+ | ---- lifetimes in impl do not match this method in trait
+LL | where
+LL | T: 'a;
+ | -- this bound might be missing in the impl
+...
+LL | fn foo<'a>(&self, state: &'a State) -> &'a T {
+ | ^^^^ lifetimes do not match method in trait
+
+error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
+ --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:33:11
+ |
+LL | fn foo<'a>(&'a self) {}
+ | ---- lifetimes in impl do not match this method in trait
+...
+LL | fn foo<'a: 'a>(&'a self) {}
+ | ^^^^^^^^ lifetimes do not match method in trait
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0195`.
#![feature(rustc_attrs)]
#[rustc_must_implement_one_of(a, a)]
-//~^ Functions names are duplicated
+//~^ functions names are duplicated
trait Trait {
fn a() {}
}
#[rustc_must_implement_one_of(b, a, a, c, b, c)]
-//~^ Functions names are duplicated
-//~| Functions names are duplicated
-//~| Functions names are duplicated
+//~^ functions names are duplicated
+//~| functions names are duplicated
+//~| functions names are duplicated
trait Trait1 {
fn a() {}
fn b() {}
-error: Functions names are duplicated
+error: functions names are duplicated
--> $DIR/rustc_must_implement_one_of_duplicates.rs:3:31
|
LL | #[rustc_must_implement_one_of(a, a)]
| ^ ^
|
- = note: All `#[rustc_must_implement_one_of]` arguments must be unique
+ = note: all `#[rustc_must_implement_one_of]` arguments must be unique
-error: Functions names are duplicated
+error: functions names are duplicated
--> $DIR/rustc_must_implement_one_of_duplicates.rs:9:34
|
LL | #[rustc_must_implement_one_of(b, a, a, c, b, c)]
| ^ ^
|
- = note: All `#[rustc_must_implement_one_of]` arguments must be unique
+ = note: all `#[rustc_must_implement_one_of]` arguments must be unique
-error: Functions names are duplicated
+error: functions names are duplicated
--> $DIR/rustc_must_implement_one_of_duplicates.rs:9:31
|
LL | #[rustc_must_implement_one_of(b, a, a, c, b, c)]
| ^ ^
|
- = note: All `#[rustc_must_implement_one_of]` arguments must be unique
+ = note: all `#[rustc_must_implement_one_of]` arguments must be unique
-error: Functions names are duplicated
+error: functions names are duplicated
--> $DIR/rustc_must_implement_one_of_duplicates.rs:9:40
|
LL | #[rustc_must_implement_one_of(b, a, a, c, b, c)]
| ^ ^
|
- = note: All `#[rustc_must_implement_one_of]` arguments must be unique
+ = note: all `#[rustc_must_implement_one_of]` arguments must be unique
error: aborting due to 4 previous errors
#![feature(rustc_attrs)]
#[rustc_must_implement_one_of(a, b)]
-//~^ Function not found in this trait
-//~| Function not found in this trait
+//~^ function not found in this trait
+//~| function not found in this trait
trait Tr0 {}
#[rustc_must_implement_one_of(a, b)]
-//~^ Function not found in this trait
+//~^ function not found in this trait
trait Tr1 {
fn a() {}
}
#[rustc_must_implement_one_of(A, B)]
trait Tr4 {
- const A: u8 = 1; //~ Not a function
+ const A: u8 = 1; //~ not a function
- type B; //~ Not a function
+ type B; //~ not a function
}
#[rustc_must_implement_one_of(a, b)]
trait Tr5 {
- fn a(); //~ This function doesn't have a default implementation
+ fn a(); //~ function doesn't have a default implementation
- fn b(); //~ This function doesn't have a default implementation
+ fn b(); //~ function doesn't have a default implementation
}
#[rustc_must_implement_one_of(abc, xyz)]
LL | struct Struct {}
| ---------------- not a trait
-error: Function not found in this trait
+error: function not found in this trait
--> $DIR/rustc_must_implement_one_of_misuse.rs:3:31
|
LL | #[rustc_must_implement_one_of(a, b)]
| ^
-error: Function not found in this trait
+error: function not found in this trait
--> $DIR/rustc_must_implement_one_of_misuse.rs:3:34
|
LL | #[rustc_must_implement_one_of(a, b)]
| ^
-error: Function not found in this trait
+error: function not found in this trait
--> $DIR/rustc_must_implement_one_of_misuse.rs:8:34
|
LL | #[rustc_must_implement_one_of(a, b)]
LL | #[rustc_must_implement_one_of(a)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: Not a function
+error: not a function
--> $DIR/rustc_must_implement_one_of_misuse.rs:26:5
|
LL | const A: u8 = 1;
|
LL | #[rustc_must_implement_one_of(A, B)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: All `#[rustc_must_implement_one_of]` arguments must be associated function names
+ = note: all `#[rustc_must_implement_one_of]` arguments must be associated function names
-error: Not a function
+error: not a function
--> $DIR/rustc_must_implement_one_of_misuse.rs:28:5
|
LL | type B;
|
LL | #[rustc_must_implement_one_of(A, B)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: All `#[rustc_must_implement_one_of]` arguments must be associated function names
+ = note: all `#[rustc_must_implement_one_of]` arguments must be associated function names
-error: This function doesn't have a default implementation
+error: function doesn't have a default implementation
--> $DIR/rustc_must_implement_one_of_misuse.rs:33:5
|
LL | fn a();
LL | #[rustc_must_implement_one_of(a, b)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: This function doesn't have a default implementation
+error: function doesn't have a default implementation
--> $DIR/rustc_must_implement_one_of_misuse.rs:35:5
|
LL | fn b();
LL | bar: &'a mut T
| ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `&mut T`
|
- = help: the following other types implement trait `Clone`:
- &T
- *const T
- *mut T
+ = help: the trait `Clone` is implemented for `&T`
= note: `Clone` is implemented for `&T`, but not for `&mut T`
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "512"]` attribute to your crate (`issue_91949_hangs_on_recursion`)
= note: required for `std::iter::Empty<()>` to implement `Iterator`
= note: 171 redundant requirements hidden
- = note: required for `IteratorOfWrapped<(), Map<IteratorOfWrapped<(), Map<..., ...>>, ...>>` to implement `Iterator`
+ = note: required for `IteratorOfWrapped<(), Map<IteratorOfWrapped<(), Map<IteratorOfWrapped<(), Map<..., ...>>, ...>>, ...>>` to implement `Iterator`
= note: the full type name has been written to '$TEST_BUILD_DIR/traits/issue-91949-hangs-on-recursion/issue-91949-hangs-on-recursion.long-type-hash.txt'
error: aborting due to previous error; 1 warning emitted
--- /dev/null
+// Taken from https://github.com/rust-lang/rust/issues/44454#issue-256435333
+
+trait Animal<X>: 'static {}
+
+fn foo<Y, X>()
+where
+ Y: Animal<X> + ?Sized,
+{
+ // `Y` implements `Animal<X>` so `Y` is 'static.
+ baz::<Y>()
+}
+
+fn bar<'a>(_arg: &'a i32) {
+ foo::<dyn Animal<&'a i32>, &'a i32>() //~ ERROR: lifetime may not live long enough
+}
+
+fn baz<T: 'static + ?Sized>() {}
+
+fn main() {
+ let a = 5;
+ bar(&a);
+}
--- /dev/null
+error: lifetime may not live long enough
+ --> $DIR/issue-44454-1.rs:14:5
+ |
+LL | fn bar<'a>(_arg: &'a i32) {
+ | -- lifetime `'a` defined here
+LL | foo::<dyn Animal<&'a i32>, &'a i32>()
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: aborting due to previous error
+
--- /dev/null
+// Taken from https://github.com/rust-lang/rust/issues/44454#issuecomment-1175925928
+
+trait Trait<ARG: 'static>: 'static {
+ type Assoc: AsRef<str>;
+}
+
+fn hr<T: ?Sized, ARG>(x: T::Assoc) -> Box<dyn AsRef<str> + 'static>
+where
+ T: Trait<ARG>
+{
+ Box::new(x)
+}
+
+fn extend_lt<'a>(x: &'a str) -> Box<dyn AsRef<str> + 'static> {
+ type DynTrait = dyn for<'a> Trait<&'a str, Assoc = &'a str>;
+ hr::<DynTrait, _>(x) //~ ERROR: borrowed data escapes outside of function
+}
+
+fn main() {
+ let extended = extend_lt(&String::from("hello"));
+ println!("{}", extended.as_ref().as_ref());
+}
--- /dev/null
+error[E0521]: borrowed data escapes outside of function
+ --> $DIR/issue-44454-2.rs:16:5
+ |
+LL | fn extend_lt<'a>(x: &'a str) -> Box<dyn AsRef<str> + 'static> {
+ | -- - `x` is a reference that is only valid in the function body
+ | |
+ | lifetime `'a` defined here
+LL | type DynTrait = dyn for<'a> Trait<&'a str, Assoc = &'a str>;
+LL | hr::<DynTrait, _>(x)
+ | ^^^^^^^^^^^^^^^^^^^^
+ | |
+ | `x` escapes the function body here
+ | argument requires that `'a` must outlive `'static`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0521`.
--- /dev/null
+// Taken from https://github.com/rust-lang/rust/issues/44454#issuecomment-1332781290
+
+use std::any::Any;
+
+trait Animal<X>: 'static {}
+
+trait Projector {
+ type Foo;
+}
+
+impl<X> Projector for dyn Animal<X> {
+ type Foo = X;
+}
+
+fn make_static<'a, T>(t: &'a T) -> &'static T {
+ let x: <dyn Animal<&'a T> as Projector>::Foo = t;
+ let any = generic::<dyn Animal<&'a T>, &'a T>(x);
+ //~^ ERROR: lifetime may not live long enough
+ any.downcast_ref::<&'static T>().unwrap()
+}
+
+fn generic<T: Projector + Animal<U> + ?Sized, U>(x: <T as Projector>::Foo) -> Box<dyn Any> {
+ make_static_any(x)
+}
+
+fn make_static_any<U: 'static>(u: U) -> Box<dyn Any> {
+ Box::new(u)
+}
+
+fn main() {
+ let a = make_static(&"salut".to_string());
+ println!("{}", *a);
+}
--- /dev/null
+error: lifetime may not live long enough
+ --> $DIR/issue-44454-3.rs:17:15
+ |
+LL | fn make_static<'a, T>(t: &'a T) -> &'static T {
+ | -- lifetime `'a` defined here
+LL | let x: <dyn Animal<&'a T> as Projector>::Foo = t;
+LL | let any = generic::<dyn Animal<&'a T>, &'a T>(x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: aborting due to previous error
+
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
- | ^ required by this bound in `std::mem::size_of`
+ | ^ required by this bound in `size_of`
help: consider removing the `?Sized` bound to make the type parameter `Sized`
|
LL - fn check<T: Iterator, U: ?Sized>() {
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
- | ^ required by this bound in `std::mem::size_of`
+ | ^ required by this bound in `size_of`
help: consider removing the `?Sized` bound to make the type parameter `Sized`
|
LL - fn check<T: Iterator, U: ?Sized>() {
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
- | ^ required by this bound in `std::mem::size_of`
+ | ^ required by this bound in `size_of`
error[E0277]: the size for values of type `[&U]` cannot be known at compilation time
--> $DIR/suggest-where-clause.rs:31:20
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
LL | pub const fn size_of<T>() -> usize {
- | ^ required by this bound in `std::mem::size_of`
+ | ^ required by this bound in `size_of`
error: aborting due to 7 previous errors
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following other types implement trait `From<T>`:
- <f32 as From<i16>>
- <f32 as From<i8>>
- <f32 as From<u16>>
- <f32 as From<u8>>
- <f64 as From<f32>>
- <f64 as From<i16>>
- <f64 as From<i32>>
- <f64 as From<i8>>
- and 68 others
+ <u8 as From<NonZeroU8>>
+ <u8 as From<bool>>
= note: required for `Result<u64, u8>` to implement `FromResidual<Result<Infallible, i32>>`
error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result`
| ------ return type was inferred to be `i32` here
|
= help: the trait `PartialEq<Foo>` is not implemented for `i32`
- = help: the following other types implement trait `PartialEq<Rhs>`:
- f32
- f64
- i128
- i16
- i32
- i64
- i8
- isize
- and 6 others
+ = help: the trait `PartialEq` is implemented for `i32`
error: aborting due to previous error
| - return type was inferred to be `&i32` here
|
= help: the trait `PartialEq<Bar<'b, 'static>>` is not implemented for `&i32`
- = help: the following other types implement trait `PartialEq<Rhs>`:
- f32
- f64
- i128
- i16
- i32
- i64
- i8
- isize
- and 6 others
+ = help: the trait `PartialEq` is implemented for `i32`
error[E0277]: can't compare `&i32` with `Foo<'static, 'b>`
--> $DIR/self-referential-4.rs:11:31
| - return type was inferred to be `&i32` here
|
= help: the trait `PartialEq<Foo<'static, 'b>>` is not implemented for `&i32`
- = help: the following other types implement trait `PartialEq<Rhs>`:
- f32
- f64
- i128
- i16
- i32
- i64
- i8
- isize
- and 6 others
+ = help: the trait `PartialEq` is implemented for `i32`
error[E0277]: can't compare `&i32` with `Moo<'static, 'a>`
--> $DIR/self-referential-4.rs:17:31
| - return type was inferred to be `&i32` here
|
= help: the trait `PartialEq<Moo<'static, 'a>>` is not implemented for `&i32`
- = help: the following other types implement trait `PartialEq<Rhs>`:
- f32
- f64
- i128
- i16
- i32
- i64
- i8
- isize
- and 6 others
+ = help: the trait `PartialEq` is implemented for `i32`
error: aborting due to 3 previous errors
| - return type was inferred to be `&i32` here
|
= help: the trait `PartialEq<Bar<'b, 'a>>` is not implemented for `&i32`
- = help: the following other types implement trait `PartialEq<Rhs>`:
- f32
- f64
- i128
- i16
- i32
- i64
- i8
- isize
- and 6 others
+ = help: the trait `PartialEq` is implemented for `i32`
error[E0277]: can't compare `&i32` with `(i32, &i32)`
--> $DIR/self-referential.rs:12:31
| ------- return type was inferred to be `(i32, &i32)` here
|
= help: the trait `PartialEq<(i32, &i32)>` is not implemented for `&i32`
- = help: the following other types implement trait `PartialEq<Rhs>`:
- f32
- f64
- i128
- i16
- i32
- i64
- i8
- isize
- and 6 others
+ = help: the trait `PartialEq` is implemented for `i32`
error[E0277]: can't compare `&i32` with `(i32, Moo<'b, 'a>::{opaque#0})`
--> $DIR/self-referential.rs:19:31
| ------- return type was inferred to be `(i32, &i32)` here
|
= help: the trait `PartialEq<(i32, Moo<'b, 'a>::{opaque#0})>` is not implemented for `&i32`
- = help: the following other types implement trait `PartialEq<Rhs>`:
- f32
- f64
- i128
- i16
- i32
- i64
- i8
- isize
- and 6 others
+ = help: the trait `PartialEq` is implemented for `i32`
error: aborting due to 3 previous errors
--> $DIR/type-ascription-precedence.rs:53:5
|
LL | (S .. S): S;
- | ^^^^^^^^ expected struct `S`, found struct `std::ops::Range`
+ | ^^^^^^^^ expected struct `S`, found struct `Range`
|
= note: expected struct `S`
found struct `std::ops::Range<S>`
fn main() {
let arr = &[1u8, 2, 3];
- let ref x = arr: &[u8]; //~ ERROR mismatched types
- let ref mut x = arr: &[u8]; //~ ERROR mismatched types
- match arr: &[u8] { //~ ERROR mismatched types
+ let ref x = type_ascribe!(arr, &[u8]); //~ ERROR mismatched types
+ let ref mut x = type_ascribe!(arr, &[u8]); //~ ERROR mismatched types
+ match type_ascribe!(arr, &[u8]) { //~ ERROR mismatched types
ref x => {}
}
- let _len = (arr: &[u8]).len(); //~ ERROR mismatched types
+ let _len = type_ascribe!(arr, &[u8]).len(); //~ ERROR mismatched types
}
error[E0308]: mismatched types
- --> $DIR/type-ascription-soundness.rs:7:17
+ --> $DIR/type-ascription-soundness.rs:7:31
|
-LL | let ref x = arr: &[u8];
- | ^^^ expected slice `[u8]`, found array `[u8; 3]`
+LL | let ref x = type_ascribe!(arr, &[u8]);
+ | ^^^ expected slice `[u8]`, found array `[u8; 3]`
|
= note: expected reference `&[u8]`
found reference `&[u8; 3]`
error[E0308]: mismatched types
- --> $DIR/type-ascription-soundness.rs:8:21
+ --> $DIR/type-ascription-soundness.rs:8:35
|
-LL | let ref mut x = arr: &[u8];
- | ^^^ expected slice `[u8]`, found array `[u8; 3]`
+LL | let ref mut x = type_ascribe!(arr, &[u8]);
+ | ^^^ expected slice `[u8]`, found array `[u8; 3]`
|
= note: expected reference `&[u8]`
found reference `&[u8; 3]`
error[E0308]: mismatched types
- --> $DIR/type-ascription-soundness.rs:9:11
+ --> $DIR/type-ascription-soundness.rs:9:25
|
-LL | match arr: &[u8] {
- | ^^^ expected slice `[u8]`, found array `[u8; 3]`
+LL | match type_ascribe!(arr, &[u8]) {
+ | ^^^ expected slice `[u8]`, found array `[u8; 3]`
|
= note: expected reference `&[u8]`
found reference `&[u8; 3]`
error[E0308]: mismatched types
- --> $DIR/type-ascription-soundness.rs:12:17
+ --> $DIR/type-ascription-soundness.rs:12:30
|
-LL | let _len = (arr: &[u8]).len();
- | ^^^ expected slice `[u8]`, found array `[u8; 3]`
+LL | let _len = type_ascribe!(arr, &[u8]).len();
+ | ^^^ expected slice `[u8]`, found array `[u8; 3]`
|
= note: expected reference `&[u8]`
found reference `&[u8; 3]`
use std::mem;
-const C1: u8 = 10: u8;
-const C2: [u8; 1: usize] = [1];
+const C1: u8 = type_ascribe!(10, u8);
+const C2: [u8; type_ascribe!(1, usize)] = [1];
struct S {
a: u8
}
fn main() {
- assert_eq!(C1.into(): i32, 10);
+ assert_eq!(type_ascribe!(C1.into(), i32), 10);
assert_eq!(C2[0], 1);
- let s = S { a: 10: u8 };
+ let s = S { a: type_ascribe!(10, u8) };
let arr = &[1u8, 2, 3];
- let mut v = arr.iter().cloned().collect(): Vec<_>;
+ let mut v = type_ascribe!(arr.iter().cloned().collect(), Vec<_>);
v.push(4);
assert_eq!(v, [1, 2, 3, 4]);
- let a = 1: u8;
- let b = a.into(): u16;
- assert_eq!(v[a.into(): usize], 2);
+ let a = type_ascribe!(1, u8);
+ let b = type_ascribe!(a.into(), u16);
+ assert_eq!(v[type_ascribe!(a.into(), usize)], 2);
assert_eq!(mem::size_of_val(&a), 1);
assert_eq!(mem::size_of_val(&b), 2);
- assert_eq!(b, 1: u16);
+ assert_eq!(b, type_ascribe!(1, u16));
let mut v = Vec::new();
- v: Vec<u8> = vec![1, 2, 3]; // Place expression type ascription
+ type_ascribe!(v, Vec<u8>) = vec![1, 2, 3]; // Place expression type ascription
assert_eq!(v, [1u8, 2, 3]);
}
|
= help: the trait `Add<u8>` is not implemented for `i32`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
<&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&i32 as Add<&i32>>
+ <i32 as Add<&i32>>
+ <i32 as Add>
error: aborting due to 7 previous errors
--> $DIR/assignment-in-if.rs:44:18
|
LL | if x == x && x = x && x == x {
- | ^ expected `bool`, found `usize`
+ | ------ ^ expected `bool`, found `usize`
+ | |
+ | expected because this is `bool`
error[E0308]: mismatched types
--> $DIR/assignment-in-if.rs:44:22
--> $DIR/assignment-in-if.rs:51:28
|
LL | if x == x && x == x && x = x {
- | ^ expected `bool`, found `usize`
+ | ---------------- ^ expected `bool`, found `usize`
+ | |
+ | expected because this is `bool`
error[E0308]: mismatched types
--> $DIR/assignment-in-if.rs:51:8
LL | fn broken_add<T>(&self, rhs: T) -> Self {
| - found type parameter
LL | *self + rhs
- | ^^^ expected type parameter `Self`, found type parameter `T`
+ | ----- ^^^ expected type parameter `Self`, found type parameter `T`
+ | |
+ | expected because this is `Self`
|
= note: expected type parameter `Self`
found type parameter `T`
--- /dev/null
+fn main(){
+ let my_var: String(String?);
+ //~^ ERROR: invalid `?` in type
+ //~| ERROR: parenthesized type parameters may only be used with a `Fn` trait
+}
--- /dev/null
+error: invalid `?` in type
+ --> $DIR/issue-104582.rs:2:30
+ |
+LL | let my_var: String(String?);
+ | ^ `?` is only allowed on expressions, not types
+ |
+help: if you meant to express that the type might not contain a value, use the `Option` wrapper type
+ |
+LL | let my_var: String(Option<String>);
+ | +++++++ ~
+
+error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
+ --> $DIR/issue-104582.rs:2:17
+ |
+LL | let my_var: String(String?);
+ | ^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses
+ |
+help: use angle brackets instead
+ |
+LL | let my_var: String<String?>;
+ | ~ ~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0214`.
| - this type parameter - expected `I` because of return type
...
LL | self.iter()
- | ^^^^^^^^^^^ expected type parameter `I`, found struct `std::slice::Iter`
+ | ^^^^^^^^^^^ expected type parameter `I`, found struct `Iter`
|
= note: expected type parameter `I`
found struct `std::slice::Iter<'_, N>`
|
= help: the trait `Add<u16>` is not implemented for `usize`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
- <&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&'a usize as Add<usize>>
+ <&usize as Add<&usize>>
+ <usize as Add<&usize>>
+ <usize as Add>
error: aborting due to 3 previous errors
+#![feature(type_ascription)]
+
fn main() {
- 0: u8<e<5>=e>
+ type_ascribe!(0, u8<e<5>=e>)
//~^ ERROR: cannot find type `e` in this scope [E0412]
//~| ERROR: associated type bindings are not allowed here [E0229]
//~| ERROR: mismatched types [E0308]
error[E0412]: cannot find type `e` in this scope
- --> $DIR/issue-91267.rs:2:16
+ --> $DIR/issue-91267.rs:4:30
|
-LL | 0: u8<e<5>=e>
- | ^
- | |
- | not found in this scope
- | help: maybe you meant to write an assignment here: `let e`
+LL | type_ascribe!(0, u8<e<5>=e>)
+ | ^ not found in this scope
error[E0229]: associated type bindings are not allowed here
- --> $DIR/issue-91267.rs:2:11
+ --> $DIR/issue-91267.rs:4:25
|
-LL | 0: u8<e<5>=e>
- | ^^^^^^ associated type not allowed here
+LL | type_ascribe!(0, u8<e<5>=e>)
+ | ^^^^^^ associated type not allowed here
error[E0308]: mismatched types
- --> $DIR/issue-91267.rs:2:5
+ --> $DIR/issue-91267.rs:4:5
|
LL | fn main() {
| - expected `()` because of default return type
-LL | 0: u8<e<5>=e>
- | ^^^^^^^^^^^^^ expected `()`, found `u8`
+LL | type_ascribe!(0, u8<e<5>=e>)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `u8`
error: aborting due to 3 previous errors
--- /dev/null
+fn main() {
+ let page_size = page_size::get();
+ //~^ ERROR failed to resolve: use of undeclared crate or module `page_size`
+}
--- /dev/null
+error[E0433]: failed to resolve: use of undeclared crate or module `page_size`
+ --> $DIR/path-to-method-sugg-unresolved-expr.rs:2:21
+ |
+LL | let page_size = page_size::get();
+ | ^^^^^^^^^ use of undeclared crate or module `page_size`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0433`.
|
= help: the trait `Add<u32>` is not implemented for `i32`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
<&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&i32 as Add<&i32>>
+ <i32 as Add<&i32>>
+ <i32 as Add>
error[E0308]: mismatched types
--> $DIR/ufcs-qpath-self-mismatch.rs:7:28
|
= help: the trait `Add<u32>` is not implemented for `i32`
= help: the following other types implement trait `Add<Rhs>`:
- <&'a f32 as Add<f32>>
- <&'a f64 as Add<f64>>
- <&'a i128 as Add<i128>>
- <&'a i16 as Add<i16>>
<&'a i32 as Add<i32>>
- <&'a i64 as Add<i64>>
- <&'a i8 as Add<i8>>
- <&'a isize as Add<isize>>
- and 48 others
+ <&i32 as Add<&i32>>
+ <i32 as Add<&i32>>
+ <i32 as Add>
error: aborting due to 4 previous errors
--- /dev/null
+// incremental
+
+trait Foo {
+ type V;
+}
+
+trait Callback<T: Foo>: Fn(&Bar<'_, T>, &T::V) {}
+
+struct Bar<'a, T> {
+ callback: Box<dyn Callback<dyn Callback<Bar<'a, T>>>>,
+ //~^ ERROR the trait bound `Bar<'a, T>: Foo` is not satisfied
+ //~| ERROR the trait bound `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static): Foo` is not satisfied
+ //~| ERROR the size for values of type `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static)` cannot be known at compilation time
+}
+
+impl<T: Foo> Bar<'_, Bar<'_, T>> {}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `Bar<'a, T>: Foo` is not satisfied
+ --> $DIR/hir-wf-canonicalized.rs:10:15
+ |
+LL | callback: Box<dyn Callback<dyn Callback<Bar<'a, T>>>>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bar<'a, T>`
+
+error[E0277]: the trait bound `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static): Foo` is not satisfied
+ --> $DIR/hir-wf-canonicalized.rs:10:15
+ |
+LL | callback: Box<dyn Callback<dyn Callback<Bar<'a, T>>>>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static)`
+
+error[E0277]: the size for values of type `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static)` cannot be known at compilation time
+ --> $DIR/hir-wf-canonicalized.rs:10:15
+ |
+LL | callback: Box<dyn Callback<dyn Callback<Bar<'a, T>>>>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+ |
+ = help: the trait `Sized` is not implemented for `(dyn Callback<Bar<'a, T>, for<'b, 'c, 'd> Output = ()> + 'static)`
+note: required by a bound in `Bar`
+ --> $DIR/hir-wf-canonicalized.rs:9:16
+ |
+LL | struct Bar<'a, T> {
+ | ^ required by this bound in `Bar`
+help: consider relaxing the implicit `Sized` restriction
+ |
+LL | struct Bar<'a, T: ?Sized> {
+ | ++++++++
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--> $DIR/wrong-mul-method-signature.rs:63:45
|
LL | let x: Vec2 = Vec2 { x: 1.0, y: 2.0 } * 2.0; // trait had reversed order
- | ^^^ expected struct `Vec2`, found floating-point number
+ | ----------------------- ^^^ expected struct `Vec2`, found floating-point number
+ | |
+ | expected because this is `Vec2`
error[E0308]: mismatched types
--> $DIR/wrong-mul-method-signature.rs:63:19
-Subproject commit e027c4b5d25af2119b1956fac42863b9b3242744
+Subproject commit 70898e522116f6c23971e2a554b2dc85fd4c84cd
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
+[`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
+[`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment
[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
cognitive-complexity-threshold = 30
```
-See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
-lints can be configured and the meaning of the variables.
+See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
+the lint descriptions contain the names and meanings of these configuration variables.
> **Note**
>
rust-version = "1.30"
```
-The MSRV can also be specified as an inner attribute, like below.
+The MSRV can also be specified as an attribute, like below.
```rust
#![feature(custom_inner_attributes)]
- [The Clippy Book](development/infrastructure/book.md)
- [Proposals](development/proposals/README.md)
- [Roadmap 2021](development/proposals/roadmap-2021.md)
+ - [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md)
cognitive-complexity-threshold = 30
```
-See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
-lints can be configured and the meaning of the variables.
+See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
+the lint descriptions contain the names and meanings of these configuration variables.
To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS`
environment variable.
msrv = "1.30.0"
```
-The MSRV can also be specified as an inner attribute, like below.
+The MSRV can also be specified as an attribute, like below.
```rust
#![feature(custom_inner_attributes)]
```rust
pub struct ManualStrip {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl ManualStrip {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
```
The project's MSRV can then be matched against the feature MSRV in the LintPass
-using the `meets_msrv` utility function.
+using the `Msrv::meets` method.
``` rust
-if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
+if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
return;
}
```
-The project's MSRV can also be specified as an inner attribute, which overrides
+The project's MSRV can also be specified as an attribute, which overrides
the value from `clippy.toml`. This can be accounted for using the
`extract_msrv_attr!(LintContext)` macro and passing
`LateContext`/`EarlyContext`.
for the MSRV version itself.
```rust
-#![feature(custom_inner_attributes)]
-
...
+#[clippy::msrv = "1.44"]
fn msrv_1_44() {
- #![clippy::msrv = "1.44"]
-
/* something that would trigger the lint */
}
+#[clippy::msrv = "1.45"]
fn msrv_1_45() {
- #![clippy::msrv = "1.45"]
-
/* something that would trigger the lint */
}
```
--- /dev/null
+- Feature Name: syntax-tree-patterns
+- Start Date: 2019-03-12
+- RFC PR: [#3875](https://github.com/rust-lang/rust-clippy/pull/3875)
+
+# Summary
+
+Introduce a domain-specific language (similar to regular expressions) that
+allows to describe lints using *syntax tree patterns*.
+
+
+# Motivation
+
+Finding parts of a syntax tree (AST, HIR, ...) that have certain properties
+(e.g. "*an if that has a block as its condition*") is a major task when writing
+lints. For non-trivial lints, it often requires nested pattern matching of AST /
+HIR nodes. For example, testing that an expression is a boolean literal requires
+the following checks:
+
+```rust
+if let ast::ExprKind::Lit(lit) = &expr.node {
+ if let ast::LitKind::Bool(_) = &lit.node {
+ ...
+ }
+}
+```
+
+Writing this kind of matching code quickly becomes a complex task and the
+resulting code is often hard to comprehend. The code below shows a simplified
+version of the pattern matching required by the `collapsible_if` lint:
+
+```rust
+// simplified version of the collapsible_if lint
+if let ast::ExprKind::If(check, then, None) = &expr.node {
+ if then.stmts.len() == 1 {
+ if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node {
+ if let ast::ExprKind::If(check_inner, content, None) = &inner.node {
+ ...
+ }
+ }
+ }
+}
+```
+
+The `if_chain` macro can improve readability by flattening the nested if
+statements, but the resulting code is still quite hard to read:
+
+```rust
+// simplified version of the collapsible_if lint
+if_chain! {
+ if let ast::ExprKind::If(check, then, None) = &expr.node;
+ if then.stmts.len() == 1;
+ if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node;
+ if let ast::ExprKind::If(check_inner, content, None) = &inner.node;
+ then {
+ ...
+ }
+}
+```
+
+The code above matches if expressions that contain only another if expression
+(where both ifs don't have an else branch). While it's easy to explain what the
+lint does, it's hard to see that from looking at the code samples above.
+
+Following the motivation above, the first goal this RFC is to **simplify writing
+and reading lints**.
+
+The second part of the motivation is clippy's dependence on unstable
+compiler-internal data structures. Clippy lints are currently written against
+the compiler's AST / HIR which means that even small changes in these data
+structures might break a lot of lints. The second goal of this RFC is to **make
+lints independant of the compiler's AST / HIR data structures**.
+
+# Approach
+
+A lot of complexity in writing lints currently seems to come from having to
+manually implement the matching logic (see code samples above). It's an
+imparative style that describes *how* to match a syntax tree node instead of
+specifying *what* should be matched against declaratively. In other areas, it's
+common to use declarative patterns to describe desired information and let the
+implementation do the actual matching. A well-known example of this approach are
+[regular expressions](https://en.wikipedia.org/wiki/Regular_expression). Instead
+of writing code that detects certain character sequences, one can describe a
+search pattern using a domain-specific language and search for matches using
+that pattern. The advantage of using a declarative domain-specific language is
+that its limited domain (e.g. matching character sequences in the case of
+regular expressions) allows to express entities in that domain in a very natural
+and expressive way.
+
+While regular expressions are very useful when searching for patterns in flat
+character sequences, they cannot easily be applied to hierarchical data
+structures like syntax trees. This RFC therefore proposes a pattern matching
+system that is inspired by regular expressions and designed for hierarchical
+syntax trees.
+
+# Guide-level explanation
+
+This proposal adds a `pattern!` macro that can be used to specify a syntax tree
+pattern to search for. A simple pattern is shown below:
+
+```rust
+pattern!{
+ my_pattern: Expr =
+ Lit(Bool(false))
+}
+```
+
+This macro call defines a pattern named `my_pattern` that can be matched against
+an `Expr` syntax tree node. The actual pattern (`Lit(Bool(false))` in this case)
+defines which syntax trees should match the pattern. This pattern matches
+expressions that are boolean literals with value `false`.
+
+The pattern can then be used to implement lints in the following way:
+
+```rust
+...
+
+impl EarlyLintPass for MyAwesomeLint {
+ fn check_expr(&mut self, cx: &EarlyContext, expr: &syntax::ast::Expr) {
+
+ if my_pattern(expr).is_some() {
+ cx.span_lint(
+ MY_AWESOME_LINT,
+ expr.span,
+ "This is a match for a simple pattern. Well done!",
+ );
+ }
+
+ }
+}
+```
+
+The `pattern!` macro call expands to a function `my_pattern` that expects a
+syntax tree expression as its argument and returns an `Option` that indicates
+whether the pattern matched.
+
+> Note: The result type is explained in more detail in [a later
+> section](#the-result-type). For now, it's enough to know that the result is
+> `Some` if the pattern matched and `None` otherwise.
+
+## Pattern syntax
+
+The following examples demonstate the pattern syntax:
+
+
+#### Any (`_`)
+
+The simplest pattern is the any pattern. It matches anything and is therefore
+similar to regex's `*`.
+
+```rust
+pattern!{
+ // matches any expression
+ my_pattern: Expr =
+ _
+}
+```
+
+#### Node (`<node-name>(<args>)`)
+
+Nodes are used to match a specific variant of an AST node. A node has a name and
+a number of arguments that depends on the node type. For example, the `Lit` node
+has a single argument that describes the type of the literal. As another
+example, the `If` node has three arguments describing the if's condition, then
+block and else block.
+
+```rust
+pattern!{
+ // matches any expression that is a literal
+ my_pattern: Expr =
+ Lit(_)
+}
+
+pattern!{
+ // matches any expression that is a boolean literal
+ my_pattern: Expr =
+ Lit(Bool(_))
+}
+
+pattern!{
+ // matches if expressions that have a boolean literal in their condition
+ // Note: The `_?` syntax here means that the else branch is optional and can be anything.
+ // This is discussed in more detail in the section `Repetition`.
+ my_pattern: Expr =
+ If( Lit(Bool(_)) , _, _?)
+}
+```
+
+
+#### Literal (`<lit>`)
+
+A pattern can also contain Rust literals. These literals match themselves.
+
+```rust
+pattern!{
+ // matches the boolean literal false
+ my_pattern: Expr =
+ Lit(Bool(false))
+}
+
+pattern!{
+ // matches the character literal 'x'
+ my_pattern: Expr =
+ Lit(Char('x'))
+}
+```
+
+#### Alternations (`a | b`)
+
+```rust
+pattern!{
+ // matches if the literal is a boolean or integer literal
+ my_pattern: Lit =
+ Bool(_) | Int(_)
+}
+
+pattern!{
+ // matches if the expression is a char literal with value 'x' or 'y'
+ my_pattern: Expr =
+ Lit( Char('x' | 'y') )
+}
+```
+
+#### Empty (`()`)
+
+The empty pattern represents an empty sequence or the `None` variant of an
+optional.
+
+```rust
+pattern!{
+ // matches if the expression is an empty array
+ my_pattern: Expr =
+ Array( () )
+}
+
+pattern!{
+ // matches if expressions that don't have an else clause
+ my_pattern: Expr =
+ If(_, _, ())
+}
+```
+
+#### Sequence (`<a> <b>`)
+
+```rust
+pattern!{
+ // matches the array [true, false]
+ my_pattern: Expr =
+ Array( Lit(Bool(true)) Lit(Bool(false)) )
+}
+```
+
+#### Repetition (`<a>*`, `<a>+`, `<a>?`, `<a>{n}`, `<a>{n,m}`, `<a>{n,}`)
+
+Elements may be repeated. The syntax for specifying repetitions is identical to
+[regex's syntax](https://docs.rs/regex/1.1.2/regex/#repetitions).
+
+```rust
+pattern!{
+ // matches arrays that contain 2 'x's as their last or second-last elements
+ // Examples:
+ // ['x', 'x'] match
+ // ['x', 'x', 'y'] match
+ // ['a', 'b', 'c', 'x', 'x', 'y'] match
+ // ['x', 'x', 'y', 'z'] no match
+ my_pattern: Expr =
+ Array( _* Lit(Char('x')){2} _? )
+}
+
+pattern!{
+ // matches if expressions that **may or may not** have an else block
+ // Attn: `If(_, _, _)` matches only ifs that **have** an else block
+ //
+ // | if with else block | if witout else block
+ // If(_, _, _) | match | no match
+ // If(_, _, _?) | match | match
+ // If(_, _, ()) | no match | match
+ my_pattern: Expr =
+ If(_, _, _?)
+}
+```
+
+#### Named submatch (`<a>#<name>`)
+
+```rust
+pattern!{
+ // matches character literals and gives the literal the name foo
+ my_pattern: Expr =
+ Lit(Char(_)#foo)
+}
+
+pattern!{
+ // matches character literals and gives the char the name bar
+ my_pattern: Expr =
+ Lit(Char(_#bar))
+}
+
+pattern!{
+ // matches character literals and gives the expression the name baz
+ my_pattern: Expr =
+ Lit(Char(_))#baz
+}
+```
+
+The reason for using named submatches is described in the section [The result
+type](#the-result-type).
+
+### Summary
+
+The following table gives an summary of the pattern syntax:
+
+| Syntax | Concept | Examples |
+|-------------------------|------------------|--------------------------------------------|
+|`_` | Any | `_` |
+|`<node-name>(<args>)` | Node | `Lit(Bool(true))`, `If(_, _, _)` |
+|`<lit>` | Literal | `'x'`, `false`, `101` |
+|`<a> \| <b>` | Alternation | `Char(_) \| Bool(_)` |
+|`()` | Empty | `Array( () )` |
+|`<a> <b>` | Sequence | `Tuple( Lit(Bool(_)) Lit(Int(_)) Lit(_) )` |
+|`<a>*` <br> `<a>+` <br> `<a>?` <br> `<a>{n}` <br> `<a>{n,m}` <br> `<a>{n,}` | Repetition <br> <br> <br> <br> <br><br> | `Array( _* )`, <br> `Block( Semi(_)+ )`, <br> `If(_, _, Block(_)?)`, <br> `Array( Lit(_){10} )`, <br> `Lit(_){5,10}`, <br> `Lit(Bool(_)){10,}` |
+|`<a>#<name>` | Named submatch | `Lit(Int(_))#foo` `Lit(Int(_#bar))` |
+
+
+## The result type
+
+A lot of lints require checks that go beyond what the pattern syntax described
+above can express. For example, a lint might want to check whether a node was
+created as part of a macro expansion or whether there's no comment above a node.
+Another example would be a lint that wants to match two nodes that have the same
+value (as needed by lints like `almost_swapped`). Instead of allowing users to
+write these checks into the pattern directly (which might make patterns hard to
+read), the proposed solution allows users to assign names to parts of a pattern
+expression. When matching a pattern against a syntax tree node, the return value
+will contain references to all nodes that were matched by these named
+subpatterns. This is similar to capture groups in regular expressions.
+
+For example, given the following pattern
+
+```rust
+pattern!{
+ // matches character literals
+ my_pattern: Expr =
+ Lit(Char(_#val_inner)#val)#val_outer
+}
+```
+
+one could get references to the nodes that matched the subpatterns in the
+following way:
+
+```rust
+...
+fn check_expr(expr: &syntax::ast::Expr) {
+ if let Some(result) = my_pattern(expr) {
+ result.val_inner // type: &char
+ result.val // type: &syntax::ast::Lit
+ result.val_outer // type: &syntax::ast::Expr
+ }
+}
+```
+
+The types in the `result` struct depend on the pattern. For example, the
+following pattern
+
+```rust
+pattern!{
+ // matches arrays of character literals
+ my_pattern_seq: Expr =
+ Array( Lit(_)*#foo )
+}
+```
+
+matches arrays that consist of any number of literal expressions. Because those
+expressions are named `foo`, the result struct contains a `foo` attribute which
+is a vector of expressions:
+
+```rust
+...
+if let Some(result) = my_pattern_seq(expr) {
+ result.foo // type: Vec<&syntax::ast::Expr>
+}
+```
+
+Another result type occurs when a name is only defined in one branch of an
+alternation:
+
+```rust
+pattern!{
+ // matches if expression is a boolean or integer literal
+ my_pattern_alt: Expr =
+ Lit( Bool(_#bar) | Int(_) )
+}
+```
+
+In the pattern above, the `bar` name is only defined if the pattern matches a
+boolean literal. If it matches an integer literal, the name isn't set. To
+account for this, the result struct's `bar` attribute is an option type:
+
+```rust
+...
+if let Some(result) = my_pattern_alt(expr) {
+ result.bar // type: Option<&bool>
+}
+```
+
+It's also possible to use a name in multiple alternation branches if they have
+compatible types:
+
+```rust
+pattern!{
+ // matches if expression is a boolean or integer literal
+ my_pattern_mult: Expr =
+ Lit(_#baz) | Array( Lit(_#baz) )
+}
+...
+if let Some(result) = my_pattern_mult(expr) {
+ result.baz // type: &syntax::ast::Lit
+}
+```
+
+Named submatches are a **flat** namespace and this is intended. In the example
+above, two different sub-structures are assigned to a flat name. I expect that
+for most lints, a flat namespace is sufficient and easier to work with than a
+hierarchical one.
+
+#### Two stages
+
+Using named subpatterns, users can write lints in two stages. First, a coarse
+selection of possible matches is produced by the pattern syntax. In the second
+stage, the named subpattern references can be used to do additional tests like
+asserting that a node hasn't been created as part of a macro expansion.
+
+## Implementing clippy lints using patterns
+
+As a "real-world" example, I re-implemented the `collapsible_if` lint using
+patterns. The code can be found
+[here](https://github.com/fkohlgrueber/rust-clippy-pattern/blob/039b07ecccaf96d6aa7504f5126720d2c9cceddd/clippy_lints/src/collapsible_if.rs#L88-L163).
+The pattern-based version passes all test cases that were written for
+`collapsible_if`.
+
+
+# Reference-level explanation
+
+## Overview
+
+The following diagram shows the dependencies between the main parts of the
+proposed solution:
+
+```
+ Pattern syntax
+ |
+ | parsing / lowering
+ v
+ PatternTree
+ ^
+ |
+ |
+ IsMatch trait
+ |
+ |
+ +---------------+-----------+---------+
+ | | | |
+ v v v v
+ syntax::ast rustc::hir syn ...
+```
+
+The pattern syntax described in the previous section is parsed / lowered into
+the so-called *PatternTree* data structure that represents a valid syntax tree
+pattern. Matching a *PatternTree* against an actual syntax tree (e.g. rust ast /
+hir or the syn ast, ...) is done using the *IsMatch* trait.
+
+The *PatternTree* and the *IsMatch* trait are introduced in more detail in the
+following sections.
+
+## PatternTree
+
+The core data structure of this RFC is the **PatternTree**.
+
+It's a data structure similar to rust's AST / HIR, but with the following
+differences:
+
+- The PatternTree doesn't contain parsing information like `Span`s
+- The PatternTree can represent alternatives, sequences and optionals
+
+The code below shows a simplified version of the current PatternTree:
+
+> Note: The current implementation can be found
+> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/pattern_tree.rs#L50-L96).
+
+
+```rust
+pub enum Expr {
+ Lit(Alt<Lit>),
+ Array(Seq<Expr>),
+ Block_(Alt<BlockType>),
+ If(Alt<Expr>, Alt<BlockType>, Opt<Expr>),
+ IfLet(
+ Alt<BlockType>,
+ Opt<Expr>,
+ ),
+}
+
+pub enum Lit {
+ Char(Alt<char>),
+ Bool(Alt<bool>),
+ Int(Alt<u128>),
+}
+
+pub enum Stmt {
+ Expr(Alt<Expr>),
+ Semi(Alt<Expr>),
+}
+
+pub enum BlockType {
+ Block(Seq<Stmt>),
+}
+```
+
+The `Alt`, `Seq` and `Opt` structs look like these:
+
+> Note: The current implementation can be found
+> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60).
+
+```rust
+pub enum Alt<T> {
+ Any,
+ Elmt(Box<T>),
+ Alt(Box<Self>, Box<Self>),
+ Named(Box<Self>, ...)
+}
+
+pub enum Opt<T> {
+ Any, // anything, but not None
+ Elmt(Box<T>),
+ None,
+ Alt(Box<Self>, Box<Self>),
+ Named(Box<Self>, ...)
+}
+
+pub enum Seq<T> {
+ Any,
+ Empty,
+ Elmt(Box<T>),
+ Repeat(Box<Self>, RepeatRange),
+ Seq(Box<Self>, Box<Self>),
+ Alt(Box<Self>, Box<Self>),
+ Named(Box<Self>, ...)
+}
+
+pub struct RepeatRange {
+ pub start: usize,
+ pub end: Option<usize> // exclusive
+}
+```
+
+## Parsing / Lowering
+
+The input of a `pattern!` macro call is parsed into a `ParseTree` first and then
+lowered to a `PatternTree`.
+
+Valid patterns depend on the *PatternTree* definitions. For example, the pattern
+`Lit(Bool(_)*)` isn't valid because the parameter type of the `Lit` variant of
+the `Expr` enum is `Any<Lit>` and therefore doesn't support repetition (`*`). As
+another example, `Array( Lit(_)* )` is a valid pattern because the parameter of
+`Array` is of type `Seq<Expr>` which allows sequences and repetitions.
+
+> Note: names in the pattern syntax correspond to *PatternTree* enum
+> **variants**. For example, the `Lit` in the pattern above refers to the `Lit`
+> variant of the `Expr` enum (`Expr::Lit`), not the `Lit` enum.
+
+## The IsMatch Trait
+
+The pattern syntax and the *PatternTree* are independant of specific syntax tree
+implementations (rust ast / hir, syn, ...). When looking at the different
+pattern examples in the previous sections, it can be seen that the patterns
+don't contain any information specific to a certain syntax tree implementation.
+In contrast, clippy lints currently match against ast / hir syntax tree nodes
+and therefore directly depend on their implementation.
+
+The connection between the *PatternTree* and specific syntax tree
+implementations is the `IsMatch` trait. It defines how to match *PatternTree*
+nodes against specific syntax tree nodes. A simplified implementation of the
+`IsMatch` trait is shown below:
+
+```rust
+pub trait IsMatch<O> {
+ fn is_match(&self, other: &'o O) -> bool;
+}
+```
+
+This trait needs to be implemented on each enum of the *PatternTree* (for the
+corresponding syntax tree types). For example, the `IsMatch` implementation for
+matching `ast::LitKind` against the *PatternTree's* `Lit` enum might look like
+this:
+
+```rust
+impl IsMatch<ast::LitKind> for Lit {
+ fn is_match(&self, other: &ast::LitKind) -> bool {
+ match (self, other) {
+ (Lit::Char(i), ast::LitKind::Char(j)) => i.is_match(j),
+ (Lit::Bool(i), ast::LitKind::Bool(j)) => i.is_match(j),
+ (Lit::Int(i), ast::LitKind::Int(j, _)) => i.is_match(j),
+ _ => false,
+ }
+ }
+}
+```
+
+All `IsMatch` implementations for matching the current *PatternTree* against
+`syntax::ast` can be found
+[here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/ast_match.rs).
+
+
+# Drawbacks
+
+#### Performance
+
+The pattern matching code is currently not optimized for performance, so it
+might be slower than hand-written matching code. Additionally, the two-stage
+approach (matching against the coarse pattern first and checking for additional
+properties later) might be slower than the current practice of checking for
+structure and additional properties in one pass. For example, the following lint
+
+```rust
+pattern!{
+ pat_if_without_else: Expr =
+ If(
+ _,
+ Block(
+ Expr( If(_, _, ())#inner )
+ | Semi( If(_, _, ())#inner )
+ )#then,
+ ()
+ )
+}
+...
+fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
+ if let Some(result) = pat_if_without_else(expr) {
+ if !block_starts_with_comment(cx, result.then) {
+ ...
+ }
+}
+```
+
+first matches against the pattern and then checks that the `then` block doesn't
+start with a comment. Using clippy's current approach, it's possible to check
+for these conditions earlier:
+
+```rust
+fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
+ if_chain! {
+ if let ast::ExprKind::If(ref check, ref then, None) = expr.node;
+ if !block_starts_with_comment(cx, then);
+ if let Some(inner) = expr_block(then);
+ if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node;
+ then {
+ ...
+ }
+ }
+}
+```
+
+Whether or not this causes performance regressions depends on actual patterns.
+If it turns out to be a problem, the pattern matching algorithms could be
+extended to allow "early filtering" (see the [Early Filtering](#early-filtering)
+section in Future Possibilities).
+
+That being said, I don't see any conceptual limitations regarding pattern
+matching performance.
+
+#### Applicability
+
+Even though I'd expect that a lot of lints can be written using the proposed
+pattern syntax, it's unlikely that all lints can be expressed using patterns. I
+suspect that there will still be lints that need to be implemented by writing
+custom pattern matching code. This would lead to mix within clippy's codebase
+where some lints are implemented using patterns and others aren't. This
+inconsistency might be considered a drawback.
+
+
+# Rationale and alternatives
+
+Specifying lints using syntax tree patterns has a couple of advantages compared
+to the current approach of manually writing matching code. First, syntax tree
+patterns allow users to describe patterns in a simple and expressive way. This
+makes it easier to write new lints for both novices and experts and also makes
+reading / modifying existing lints simpler.
+
+Another advantage is that lints are independent of specific syntax tree
+implementations (e.g. AST / HIR, ...). When these syntax tree implementations
+change, only the `IsMatch` trait implementations need to be adapted and existing
+lints can remain unchanged. This also means that if the `IsMatch` trait
+implementations were integrated into the compiler, updating the `IsMatch`
+implementations would be required for the compiler to compile successfully. This
+could reduce the number of times clippy breaks because of changes in the
+compiler. Another advantage of the pattern's independence is that converting an
+`EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole
+pattern matching code. In fact, the pattern might work just fine without any
+adaptions.
+
+
+## Alternatives
+
+### Rust-like pattern syntax
+
+The proposed pattern syntax requires users to know the structure of the
+`PatternTree` (which is very similar to the AST's / HIR's structure) and also
+the pattern syntax. An alternative would be to introduce a pattern syntax that
+is similar to actual Rust syntax (probably like the `quote!` macro). For
+example, a pattern that matches `if` expressions that have `false` in their
+condition could look like this:
+
+```rust
+if false {
+ #[*]
+}
+```
+
+#### Problems
+
+Extending Rust syntax (which is quite complex by itself) with additional syntax
+needed for specifying patterns (alternations, sequences, repetisions, named
+submatches, ...) might become difficult to read and really hard to parse
+properly.
+
+For example, a pattern that matches a binary operation that has `0` on both
+sides might look like this:
+
+```
+0 #[*:BinOpKind] 0
+```
+
+Now consider this slightly more complex example:
+
+```
+1 + 0 #[*:BinOpKind] 0
+```
+
+The parser would need to know the precedence of `#[*:BinOpKind]` because it
+affects the structure of the resulting AST. `1 + 0 + 0` is parsed as `(1 + 0) +
+0` while `1 + 0 * 0` is parsed as `1 + (0 * 0)`. Since the pattern could be any
+`BinOpKind`, the precedence cannot be known in advance.
+
+Another example of a problem would be named submatches. Take a look at this
+pattern:
+
+```rust
+fn test() {
+ 1 #foo
+}
+```
+
+Which node is `#foo` referring to? `int`, `ast::Lit`, `ast::Expr`, `ast::Stmt`?
+Naming subpatterns in a rust-like syntax is difficult because a lot of AST nodes
+don't have a syntactic element that can be used to put the name tag on. In these
+situations, the only sensible option would be to assign the name tag to the
+outermost node (`ast::Stmt` in the example above), because the information of
+all child nodes can be retrieved through the outermost node. The problem with
+this then would be that accessing inner nodes (like `ast::Lit`) would again
+require manual pattern matching.
+
+In general, Rust syntax contains a lot of code structure implicitly. This
+structure is reconstructed during parsing (e.g. binary operations are
+reconstructed using operator precedence and left-to-right) and is one of the
+reasons why parsing is a complex task. The advantage of this approach is that
+writing code is simpler for users.
+
+When writing *syntax tree patterns*, each element of the hierarchy might have
+alternatives, repetitions, etc.. Respecting that while still allowing
+human-friendly syntax that contains structure implicitly seems to be really
+complex, if not impossible.
+
+Developing such a syntax would also require to maintain a custom parser that is
+at least as complex as the Rust parser itself. Additionally, future changes in
+the Rust syntax might be incompatible with such a syntax.
+
+In summary, I think that developing such a syntax would introduce a lot of
+complexity to solve a relatively minor problem.
+
+The issue of users not knowing about the *PatternTree* structure could be solved
+by a tool that, given a rust program, generates a pattern that matches only this
+program (similar to the clippy author lint).
+
+For some simple cases (like the first example above), it might be possible to
+successfully mix Rust and pattern syntax. This space could be further explored
+in a future extension.
+
+# Prior art
+
+The pattern syntax is heavily inspired by regular expressions (repetitions,
+alternatives, sequences, ...).
+
+From what I've seen until now, other linters also implement lints that directly
+work on syntax tree data structures, just like clippy does currently. I would
+therefore consider the pattern syntax to be *new*, but please correct me if I'm
+wrong.
+
+# Unresolved questions
+
+#### How to handle multiple matches?
+
+When matching a syntax tree node against a pattern, there are possibly multiple
+ways in which the pattern can be matched. A simple example of this would be the
+following pattern:
+
+```rust
+pattern!{
+ my_pattern: Expr =
+ Array( _* Lit(_)+#literals)
+}
+```
+
+This pattern matches arrays that end with at least one literal. Now given the
+array `[x, 1, 2]`, should `1` be matched as part of the `_*` or the `Lit(_)+`
+part of the pattern? The difference is important because the named submatch
+`#literals` would contain 1 or 2 elements depending how the pattern is matched.
+In regular expressions, this problem is solved by matching "greedy" by default
+and "non-greedy" optionally.
+
+I haven't looked much into this yet because I don't know how relevant it is for
+most lints. The current implementation simply returns the first match it finds.
+
+# Future possibilities
+
+#### Implement rest of Rust Syntax
+
+The current project only implements a small part of the Rust syntax. In the
+future, this should incrementally be extended to more syntax to allow
+implementing more lints. Implementing more of the Rust syntax requires extending
+the `PatternTree` and `IsMatch` implementations, but should be relatively
+straight-forward.
+
+#### Early filtering
+
+As described in the *Drawbacks/Performance* section, allowing additional checks
+during the pattern matching might be beneficial.
+
+The pattern below shows how this could look like:
+
+```rust
+pattern!{
+ pat_if_without_else: Expr =
+ If(
+ _,
+ Block(
+ Expr( If(_, _, ())#inner )
+ | Semi( If(_, _, ())#inner )
+ )#then,
+ ()
+ )
+ where
+ !in_macro(#then.span);
+}
+```
+
+The difference compared to the currently proposed two-stage filtering is that
+using early filtering, the condition (`!in_macro(#then.span)` in this case)
+would be evaluated as soon as the `Block(_)#then` was matched.
+
+Another idea in this area would be to introduce a syntax for backreferences.
+They could be used to require that multiple parts of a pattern should match the
+same value. For example, the `assign_op_pattern` lint that searches for `a = a
+op b` and recommends changing it to `a op= b` requires that both occurrances of
+`a` are the same. Using `=#...` as syntax for backreferences, the lint could be
+implemented like this:
+
+```rust
+pattern!{
+ assign_op_pattern: Expr =
+ Assign(_#target, Binary(_, =#target, _)
+}
+```
+
+#### Match descendant
+
+A lot of lints currently implement custom visitors that check whether any
+subtree (which might not be a direct descendant) of the current node matches
+some properties. This cannot be expressed with the proposed pattern syntax.
+Extending the pattern syntax to allow patterns like "a function that contains at
+least two return statements" could be a practical addition.
+
+#### Negation operator for alternatives
+
+For patterns like "a literal that is not a boolean literal" one currently needs
+to list all alternatives except the boolean case. Introducing a negation
+operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern
+would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
+literal types are implemented).
+
+#### Functional composition
+
+Patterns currently don't have any concept of composition. This leads to
+repetitions within patterns. For example, one of the collapsible-if patterns
+currently has to be written like this:
+
+```rust
+pattern!{
+ pat_if_else: Expr =
+ If(
+ _,
+ _,
+ Block_(
+ Block(
+ Expr((If(_, _, _?) | IfLet(_, _?))#else_) |
+ Semi((If(_, _, _?) | IfLet(_, _?))#else_)
+ )#block_inner
+ )#block
+ ) |
+ IfLet(
+ _,
+ Block_(
+ Block(
+ Expr((If(_, _, _?) | IfLet(_, _?))#else_) |
+ Semi((If(_, _, _?) | IfLet(_, _?))#else_)
+ )#block_inner
+ )#block
+ )
+}
+```
+
+If patterns supported defining functions of subpatterns, the code could be
+simplified as follows:
+
+```rust
+pattern!{
+ fn expr_or_semi(expr: Expr) -> Stmt {
+ Expr(expr) | Semi(expr)
+ }
+ fn if_or_if_let(then: Block, else: Opt<Expr>) -> Expr {
+ If(_, then, else) | IfLet(then, else)
+ }
+ pat_if_else: Expr =
+ if_or_if_let(
+ _,
+ Block_(
+ Block(
+ expr_or_semi( if_or_if_let(_, _?)#else_ )
+ )#block_inner
+ )#block
+ )
+}
+```
+
+Additionally, common patterns like `expr_or_semi` could be shared between
+different lints.
+
+#### Clippy Pattern Author
+
+Another improvement could be to create a tool that, given some valid Rust
+syntax, generates a pattern that matches this syntax exactly. This would make
+starting to write a pattern easier. A user could take a look at the patterns
+generated for a couple of Rust code examples and use that information to write a
+pattern that matches all of them.
+
+This is similar to clippy's author lint.
+
+#### Supporting other syntaxes
+
+Most of the proposed system is language-agnostic. For example, the pattern
+syntax could also be used to describe patterns for other programming languages.
+
+In order to support other languages' syntaxes, one would need to implement
+another `PatternTree` that sufficiently describes the languages' AST and
+implement `IsMatch` for this `PatternTree` and the languages' AST.
+
+One aspect of this is that it would even be possible to write lints that work on
+the pattern syntax itself. For example, when writing the following pattern
+
+
+```rust
+pattern!{
+ my_pattern: Expr =
+ Array( Lit(Bool(false)) Lit(Bool(false)) )
+}
+```
+
+a lint that works on the pattern syntax's AST could suggest using this pattern
+instead:
+
+```rust
+pattern!{
+ my_pattern: Expr =
+ Array( Lit(Bool(false)){2} )
+}
+```
+
+In the future, clippy could use this system to also provide lints for custom
+syntaxes like those found in macros.
let new_lint = if enable_msrv {
format!(
- "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv)));\n ",
+ "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv())));\n ",
lint_pass = lint.pass,
ctor_arg = if lint.pass == "late" { "_" } else { "" },
module_name = lint.name,
result.push_str(&if enable_msrv {
formatdoc!(
r#"
- use clippy_utils::msrvs;
+ use clippy_utils::msrvs::{{self, Msrv}};
{pass_import}
use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
- use rustc_semver::RustcVersion;
use rustc_session::{{declare_tool_lint, impl_lint_pass}};
"#
formatdoc!(
r#"
pub struct {name_camel} {{
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}}
impl {name_camel} {{
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {{
+ pub fn new(msrv: Msrv) -> Self {{
Self {{ msrv }}
}}
}}
let _ = writedoc!(
lint_file_contents,
r#"
- use clippy_utils::{{meets_msrv, msrvs}};
+ use clippy_utils::msrvs::{{self, Msrv}};
use rustc_lint::{{{context_import}, LintContext}};
- use rustc_semver::RustcVersion;
use super::{name_upper};
// TODO: Adjust the parameters as necessary
- pub(super) fn check(cx: &{context_import}, msrv: Option<RustcVersion>) {{
- if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
+ pub(super) fn check(cx: &{context_import}, msrv: &Msrv) {{
+ if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
return;
}}
todo!();
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{trim_span, walk_span_to_context};
-use clippy_utils::{meets_msrv, msrvs};
use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
pub struct AlmostCompleteLetterRange {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl AlmostCompleteLetterRange {
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
let ctxt = e.span.ctxt();
let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt)
&& let Some(end) = walk_span_to_context(end.span, ctxt)
- && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE)
+ && self.msrv.meets(msrvs::RANGE_INCLUSIVE)
{
Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
} else {
if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
&& matches!(kind.node, RangeEnd::Excluded)
{
- let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) {
+ let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) {
"..="
} else {
"..."
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::msrvs::{self, Msrv};
use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
];
pub struct ApproxConstant {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl ApproxConstant {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
let s = s.as_str();
if s.parse::<f64>().is_ok() {
for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
- if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) {
+ if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| self.msrv.meets(msrv)) {
span_lint_and_help(
cx,
APPROX_CONSTANT,
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::macros::{is_panic, macro_backtrace};
-use clippy_utils::msrvs;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
-use clippy_utils::{extract_msrv_attr, meets_msrv};
use if_chain::if_chain;
use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
}
pub struct EarlyAttributes {
- pub msrv: Option<RustcVersion>,
+ pub msrv: Msrv,
}
impl_lint_pass!(EarlyAttributes => [
}
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
- check_deprecated_cfg_attr(cx, attr, self.msrv);
+ check_deprecated_cfg_attr(cx, attr, &self.msrv);
check_mismatched_target_os(cx, attr);
}
}
}
-fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
+fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
if_chain! {
- if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES);
+ if msrv.meets(msrvs::TOOL_ATTRIBUTES);
// check cfg_attr
if attr.has_name(sym::cfg_attr);
if let Some(items) = attr.meta_item_list();
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::sugg::Sugg;
-use clippy_utils::{meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
-use rustc_semver::RustcVersion;
use super::CAST_ABS_TO_UNSIGNED;
cast_expr: &Expr<'_>,
cast_from: Ty<'_>,
cast_to: Ty<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) {
- if meets_msrv(msrv, msrvs::UNSIGNED_ABS)
+ if msrv.meets(msrvs::UNSIGNED_ABS)
&& let ty::Int(from) = cast_from.kind()
&& let ty::Uint(to) = cast_to.kind()
&& let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_constant;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_isize_or_usize;
-use clippy_utils::{in_constant, meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, FloatTy, Ty};
-use rustc_semver::RustcVersion;
use super::{utils, CAST_LOSSLESS};
cast_op: &Expr<'_>,
cast_from: Ty<'_>,
cast_to: Ty<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) {
if !should_lint(cx, expr, cast_from, cast_to, msrv) {
return;
);
}
-fn should_lint(
- cx: &LateContext<'_>,
- expr: &Expr<'_>,
- cast_from: Ty<'_>,
- cast_to: Ty<'_>,
- msrv: Option<RustcVersion>,
-) -> bool {
+fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
// Do not suggest using From in consts/statics until it is valid to do so (see #2267).
if in_constant(cx, expr.hir_id) {
return false;
};
!is_isize_or_usize(cast_from) && from_nbits < to_nbits
},
- (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true,
+ (false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(msrvs::FROM_BOOL) => true,
(_, _) => {
matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
},
};
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
- let cast_from_ptr_size = def.repr().int.map_or(true, |ty| {
- matches!(
- ty,
- IntegerType::Pointer(_),
- )
- });
+ let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),));
let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
(false, false) if from_nbits > to_nbits => "",
(true, false) if from_nbits > to_nbits => "",
-use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source};
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::{diagnostics::span_lint_and_then, source};
use if_chain::if_chain;
use rustc_ast::Mutability;
use rustc_hir::{Expr, ExprKind, Node};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
-use rustc_semver::RustcVersion;
use super::CAST_SLICE_DIFFERENT_SIZES;
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option<RustcVersion>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv) {
// suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
- if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) {
+ if !msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) {
return;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
+use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def_id::DefId, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
-use rustc_semver::RustcVersion;
use super::CAST_SLICE_FROM_RAW_PARTS;
}
}
-pub(super) fn check(
- cx: &LateContext<'_>,
- expr: &Expr<'_>,
- cast_expr: &Expr<'_>,
- cast_to: Ty<'_>,
- msrv: Option<RustcVersion>,
-) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) {
if_chain! {
- if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
+ if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS);
if let ty::RawPtr(ptrty) = cast_to.kind();
if let ty::Slice(_) = ptrty.ty.kind();
if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
mod unnecessary_cast;
mod utils;
-use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
+use clippy_utils::is_hir_ty_cfg_dependant;
+use clippy_utils::msrvs::{self, Msrv};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
}
pub struct Casts {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl Casts {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for Casts {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !in_external_macro(cx.sess(), expr.span) {
- ptr_as_ptr::check(cx, expr, self.msrv);
+ ptr_as_ptr::check(cx, expr, &self.msrv);
}
if expr.span.from_expansion() {
if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
return;
}
- cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
+ cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv);
as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to);
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
cast_possible_wrap::check(cx, expr, cast_from, cast_to);
cast_precision_loss::check(cx, expr, cast_from, cast_to);
cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
- cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
+ cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to);
}
- cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
+ cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
}
as_underscore::check(cx, expr, cast_to_hir);
- if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
+ if self.msrv.meets(msrvs::BORROW_AS_PTR) {
borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
}
}
cast_ref_to_mut::check(cx, expr);
cast_ptr_alignment::check(cx, expr);
char_lit_as_u8::check(cx, expr);
- ptr_as_ptr::check(cx, expr, self.msrv);
- cast_slice_different_sizes::check(cx, expr, self.msrv);
+ ptr_as_ptr::check(cx, expr, &self.msrv);
+ cast_slice_different_sizes::check(cx, expr, &self.msrv);
}
extract_msrv_attr!(LateContext);
use std::borrow::Cow;
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::sugg::Sugg;
-use clippy_utils::{meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, TypeAndMut};
-use rustc_semver::RustcVersion;
use super::PTR_AS_PTR;
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVersion>) {
- if !meets_msrv(msrv, msrvs::POINTER_CAST) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
+ if !msrv.meets(msrvs::POINTER_CAST) {
return;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::get_parent_expr;
use clippy_utils::numeric_literal::NumericLiteral;
use clippy_utils::source::snippet_opt;
+use clippy_utils::{get_parent_expr, path_to_local};
use if_chain::if_chain;
use rustc_ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
}
if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
+ if let Some(id) = path_to_local(cast_expr)
+ && let Some(span) = cx.tcx.hir().opt_span(id)
+ && span.ctxt() != cast_expr.span.ctxt()
+ {
+ // Binding context is different than the identifiers context.
+ // Weird macro wizardry could be involved here.
+ return false;
+ }
+
span_lint_and_sugg(
cx,
UNNECESSARY_CAST,
expr.span,
&format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
"try",
- cast_str,
+ if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(..))) {
+ format!("{{ {cast_str} }}")
+ } else {
+ cast_str
+ },
Applicability::MachineApplicable,
);
return true;
//! lint on manually implemented checked conversions that could be transformed into `try_from`
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq};
+use clippy_utils::{in_constant, is_integer_literal, SpanlessEq};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
}
pub struct CheckedConversions {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl CheckedConversions {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
- if !meets_msrv(self.msrv, msrvs::TRY_FROM) {
+ if !self.msrv.meets(msrvs::TRY_FROM) {
return;
}
if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
// Prevent triggering on `if c { if let a = b { .. } }`.
if !matches!(check_inner.kind, ast::ExprKind::Let(..));
- if expr.span.ctxt() == inner.span.ctxt();
+ let ctxt = expr.span.ctxt();
+ if inner.span.ctxt() == ctxt;
then {
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
- let lhs = Sugg::ast(cx, check, "..");
- let rhs = Sugg::ast(cx, check_inner, "..");
+ let mut app = Applicability::MachineApplicable;
+ let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app);
+ let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app);
diag.span_suggestion(
expr.span,
"collapse nested if block",
lhs.and(&rhs),
snippet_block(cx, content.span, "..", Some(expr.span)),
),
- Applicability::MachineApplicable, // snippet
+ app, // snippet
);
});
}
crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
crate::functions::DOUBLE_MUST_USE_INFO,
+ crate::functions::MISNAMED_GETTERS_INFO,
crate::functions::MUST_USE_CANDIDATE_INFO,
crate::functions::MUST_USE_UNIT_INFO,
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
crate::types::TYPE_COMPLEXITY_INFO,
crate::types::VEC_BOX_INFO,
crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO,
+ crate::undocumented_unsafe_blocks::UNNECESSARY_SAFETY_COMMENT_INFO,
crate::unicode::INVISIBLE_CHARACTERS_INFO,
crate::unicode::NON_ASCII_LITERAL_INFO,
crate::unicode::UNICODE_NOT_NFC_INFO,
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
use clippy_utils::{
- fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
- walk_to_expr_usage,
+ fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage,
};
+
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch};
self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
};
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt as _;
possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
// `IntoIterator` for arrays requires Rust 1.53.
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl<'tcx> Dereferencing<'tcx> {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self {
msrv,
..Dereferencing::default()
match (self.state.take(), kind) {
(None, kind) => {
let expr_ty = typeck.expr_ty(expr);
- let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv);
+ let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv);
match kind {
RefOp::Deref => {
+ let sub_ty = typeck.expr_ty(sub_expr);
if let Position::FieldAccess {
name,
of_union: false,
} = position
- && !ty_contains_field(typeck.expr_ty(sub_expr), name)
+ && !ty_contains_field(sub_ty, name)
{
self.state = Some((
State::ExplicitDerefField { name },
StateData { span: expr.span, hir_id: expr.hir_id, position },
));
- } else if position.is_deref_stable() {
+ } else if position.is_deref_stable() && sub_ty.is_ref() {
self.state = Some((
State::ExplicitDeref { mutability: None },
StateData { span: expr.span, hir_id: expr.hir_id, position },
));
}
- }
+ },
RefOp::Method(target_mut)
if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
&& position.lint_explicit_deref() =>
StateData {
span: expr.span,
hir_id: expr.hir_id,
- position
+ position,
},
));
},
msg,
snip_expr,
}),
- StateData { span: expr.span, hir_id: expr.hir_id, position },
+ StateData {
+ span: expr.span,
+ hir_id: expr.hir_id,
+ position,
+ },
));
} else if position.is_deref_stable()
// Auto-deref doesn't combine with other adjustments
StateData {
span: expr.span,
hir_id: expr.hir_id,
- position
+ position,
},
));
}
cx: &LateContext<'tcx>,
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
e: &'tcx Expr<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) -> (Position, &'tcx [Adjustment<'tcx>]) {
let mut adjustments = [].as_slice();
let mut precedence = 0i8;
} && impl_ty.is_ref()
&& let infcx = cx.tcx.infer_ctxt().build()
&& infcx
- .type_implements_trait(trait_id, [impl_ty.into()].into_iter().chain(subs.iter().copied()), cx.param_env)
+ .type_implements_trait(
+ trait_id,
+ [impl_ty.into()].into_iter().chain(subs.iter().copied()),
+ cx.param_env,
+ )
.must_apply_modulo_regions()
{
return Some(Position::MethodReceiverRefImpl)
param_ty: ParamTy,
mut expr: &Expr<'tcx>,
precedence: i8,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) -> Position {
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
&& let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
&& ty.is_array()
- && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
+ && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
{
return false;
}
use rustc_middle::hir::nested_filter;
use rustc_middle::traits::Reveal;
use rustc_middle::ty::{
- self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
- Ty, TyCtxt,
+ self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate,
+ TraitRef, Ty, TyCtxt,
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Use(path, UseKind::Single) = &item.kind {
- self.check_res_emit(cx, &path.res, item.span);
+ for res in &path.res {
+ self.check_res_emit(cx, res, item.span);
+ }
}
}
/// ```
#[clippy::version = "1.66.0"]
pub UNNECESSARY_SAFETY_DOC,
- style,
+ restriction,
"`pub fn` or `pub trait` with `# Safety` docs"
}
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::binding::BindingMode;
-use rustc_middle::ty::{self, Ty, TypeVisitable};
+use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TypeVisitable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait()
&& let args = cx.tcx.erase_late_bound_regions(substs.as_closure().sig()).inputs()
- && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &args.iter().copied().map(Into::into).collect::<Vec<_>>())
+ && implements_trait(
+ cx,
+ callee_ty.peel_refs(),
+ fn_mut_id,
+ &args.iter().copied().map(Into::into).collect::<Vec<_>>(),
+ )
&& path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr))
{
// Mutable closure is used after current expr; we cannot consume it.
if check_sig(cx, closure_ty, call_ty);
then {
span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
- let name = get_ufcs_type_name(cx, method_def_id);
+ let name = get_ufcs_type_name(cx, method_def_id, substs);
diag.span_suggestion(
expr.span,
"replace the closure with the method itself",
cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
}
-fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
+fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs: SubstsRef<'tcx>) -> String {
let assoc_item = cx.tcx.associated_item(method_def_id);
let def_id = assoc_item.container_id(cx.tcx);
match assoc_item.container {
let ty = cx.tcx.type_of(def_id);
match ty.kind() {
ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()),
+ ty::Array(..)
+ | ty::Dynamic(..)
+ | ty::Never
+ | ty::RawPtr(_)
+ | ty::Ref(..)
+ | ty::Slice(_)
+ | ty::Tuple(_) => {
+ format!("<{}>", EarlyBinder(ty).subst(cx.tcx, substs))
+ },
_ => ty.to_string(),
}
},
declare_clippy_lint! {
/// ### What it does
- /// `exit()` terminates the program and doesn't provide a
- /// stack trace.
+ /// Detects calls to the `exit()` function which terminates the program.
///
/// ### Why is this bad?
- /// Ideally a program is terminated by finishing
+ /// Exit terminates the program at the location it is called. For unrecoverable
+ /// errors `panics` should be used to provide a stacktrace and potentualy other
+ /// information. A normal termination or one with an error code should happen in
/// the main function.
///
/// ### Example
- /// ```ignore
+ /// ```
/// std::process::exit(0)
/// ```
+ ///
+ /// Use instead:
+ ///
+ /// ```ignore
+ /// // To provide a stacktrace and additional information
+ /// panic!("message");
+ ///
+ /// // or a main method with a return
+ /// fn main() -> Result<(), i32> {
+ /// Ok(())
+ /// }
+ /// ```
#[clippy::version = "1.41.0"]
pub EXIT,
restriction,
- "`std::process::exit` is called, terminating the program"
+ "detects `std::process::exit` calls"
}
declare_lint_pass!(Exit => [EXIT]);
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred};
+use clippy_utils::is_diag_trait_item;
+use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred};
use clippy_utils::macros::{
is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage,
};
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs};
use if_chain::if_chain;
use itertools::Itertools;
-use rustc_errors::Applicability;
+use rustc_errors::{
+ Applicability,
+ SuggestionStyle::{CompletelyHidden, ShowCode},
+};
use rustc_hir::{Expr, ExprKind, HirId, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::DefId;
use rustc_span::edition::Edition::Edition2021;
/// format!("{var:.prec$}");
/// ```
///
- /// ### Known Problems
- ///
- /// There may be a false positive if the format string is expanded from certain proc macros:
- ///
- /// ```ignore
- /// println!(indoc!("{}"), var);
+ /// If allow-mixed-uninlined-format-args is set to false in clippy.toml,
+ /// the following code will also trigger the lint:
+ /// ```rust
+ /// # let var = 42;
+ /// format!("{} {}", var, 1+2);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let var = 42;
+ /// format!("{var} {}", 1+2);
/// ```
///
+ /// ### Known Problems
+ ///
/// If a format string contains a numbered argument that cannot be inlined
/// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
#[clippy::version = "1.65.0"]
pub UNINLINED_FORMAT_ARGS,
- pedantic,
+ style,
"using non-inlined variables in `format!` calls"
}
]);
pub struct FormatArgs {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
+ ignore_mixed: bool,
}
impl FormatArgs {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
- Self { msrv }
+ pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self {
+ Self {
+ msrv,
+ ignore_mixed: allow_mixed_uninlined_format_args,
+ }
}
}
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
check_to_string_in_format_args(cx, name, arg.param.value);
}
- if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) {
- check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id);
+ if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
+ check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed);
}
}
}
}
}
-fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) {
+fn check_uninlined_args(
+ cx: &LateContext<'_>,
+ args: &FormatArgsExpn<'_>,
+ call_site: Span,
+ def_id: DefId,
+ ignore_mixed: bool,
+) {
if args.format_string.span.from_expansion() {
return;
}
// we cannot remove any other arguments in the format string,
// because the index numbers might be wrong after inlining.
// Example of an un-inlinable format: print!("{}{1}", foo, 2)
- if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() {
+ if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() {
return;
}
- // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
- if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) {
- return;
- }
+ // multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
+ // in those cases, make the code suggestion hidden
+ let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span));
span_lint_and_then(
cx,
call_site,
"variables can be used directly in the `format!` string",
|diag| {
- diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable);
+ diag.multipart_suggestion_with_style(
+ "change this to",
+ fixes,
+ Applicability::MachineApplicable,
+ if multiline_fix { CompletelyHidden } else { ShowCode },
+ );
},
);
}
-fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool {
+fn check_one_arg(
+ args: &FormatArgsExpn<'_>,
+ param: &FormatParam<'_>,
+ fixes: &mut Vec<(Span, String)>,
+ ignore_mixed: bool,
+) -> bool {
if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
&& let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
&& let [segment] = path.segments
fixes.push((arg_span, String::new()));
true // successful inlining, continue checking
} else {
- // if we can't inline a numbered argument, we can't continue
- param.kind != Numbered
+ // Do not continue inlining (return false) in case
+ // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
+ // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
+ param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_)))
}
}
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::span_is_local;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::path_def_id;
use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs, path_def_id};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_path, Visitor};
use rustc_hir::{
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter::OnlyBodies;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, sym};
use rustc_span::{Span, Symbol};
}
pub struct FromOverInto {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl FromOverInto {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
FromOverInto { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for FromOverInto {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
- if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
+ if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
return;
}
self.cx.tcx.hir()
}
- fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
+ fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
for segment in path.segments {
match segment.ident.name {
kw::SelfLower => self.lower.push(segment.ident.span),
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use rustc_errors::Applicability;
+use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind, Unsafety};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::Span;
+
+use std::iter;
+
+use super::MISNAMED_GETTERS;
+
+pub fn check_fn(
+ cx: &LateContext<'_>,
+ kind: FnKind<'_>,
+ decl: &FnDecl<'_>,
+ body: &Body<'_>,
+ span: Span,
+ _hir_id: HirId,
+) {
+ let FnKind::Method(ref ident, sig) = kind else {
+ return;
+ };
+
+ // Takes only &(mut) self
+ if decl.inputs.len() != 1 {
+ return;
+ }
+
+ let name = ident.name.as_str();
+
+ let name = match decl.implicit_self {
+ ImplicitSelfKind::MutRef => {
+ let Some(name) = name.strip_suffix("_mut") else {
+ return;
+ };
+ name
+ },
+ ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name,
+ ImplicitSelfKind::None => return,
+ };
+
+ let name = if sig.header.unsafety == Unsafety::Unsafe {
+ name.strip_suffix("_unchecked").unwrap_or(name)
+ } else {
+ name
+ };
+
+ // Body must be &(mut) <self_data>.name
+ // self_data is not neccessarilly self, to also lint sub-getters, etc…
+
+ let block_expr = if_chain! {
+ if let ExprKind::Block(block,_) = body.value.kind;
+ if block.stmts.is_empty();
+ if let Some(block_expr) = block.expr;
+ then {
+ block_expr
+ } else {
+ return;
+ }
+ };
+ let expr_span = block_expr.span;
+
+ // Accept &<expr>, &mut <expr> and <expr>
+ let expr = if let ExprKind::AddrOf(_, _, tmp) = block_expr.kind {
+ tmp
+ } else {
+ block_expr
+ };
+ let (self_data, used_ident) = if_chain! {
+ if let ExprKind::Field(self_data, ident) = expr.kind;
+ if ident.name.as_str() != name;
+ then {
+ (self_data, ident)
+ } else {
+ return;
+ }
+ };
+
+ let mut used_field = None;
+ let mut correct_field = None;
+ let typeck_results = cx.typeck_results();
+ for adjusted_type in iter::once(typeck_results.expr_ty(self_data))
+ .chain(typeck_results.expr_adjustments(self_data).iter().map(|adj| adj.target))
+ {
+ let ty::Adt(def,_) = adjusted_type.kind() else {
+ continue;
+ };
+
+ for f in def.all_fields() {
+ if f.name.as_str() == name {
+ correct_field = Some(f);
+ }
+ if f.name == used_ident.name {
+ used_field = Some(f);
+ }
+ }
+ }
+
+ let Some(used_field) = used_field else {
+ // Can happen if the field access is a tuple. We don't lint those because the getter name could not start with a number.
+ return;
+ };
+
+ let Some(correct_field) = correct_field else {
+ // There is no field corresponding to the getter name.
+ // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is
+ return;
+ };
+
+ if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) {
+ let left_span = block_expr.span.until(used_ident.span);
+ let snippet = snippet(cx, left_span, "..");
+ let sugg = format!("{snippet}{name}");
+ span_lint_and_then(
+ cx,
+ MISNAMED_GETTERS,
+ span,
+ "getter function appears to return the wrong field",
+ |diag| {
+ diag.span_suggestion(expr_span, "consider using", sugg, Applicability::MaybeIncorrect);
+ },
+ );
+ }
+}
+mod misnamed_getters;
mod must_use;
mod not_unsafe_ptr_arg_deref;
mod result;
"function returning `Result` with large `Err` type"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for getter methods that return a field that doesn't correspond
+ /// to the name of the method, when there is a field's whose name matches that of the method.
+ ///
+ /// ### Why is this bad?
+ /// It is most likely that such a method is a bug caused by a typo or by copy-pasting.
+ ///
+ /// ### Example
+
+ /// ```rust
+ /// struct A {
+ /// a: String,
+ /// b: String,
+ /// }
+ ///
+ /// impl A {
+ /// fn a(&self) -> &str{
+ /// &self.b
+ /// }
+ /// }
+
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// struct A {
+ /// a: String,
+ /// b: String,
+ /// }
+ ///
+ /// impl A {
+ /// fn a(&self) -> &str{
+ /// &self.a
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "1.67.0"]
+ pub MISNAMED_GETTERS,
+ suspicious,
+ "getter method returning the wrong field"
+}
+
#[derive(Copy, Clone)]
pub struct Functions {
too_many_arguments_threshold: u64,
MUST_USE_CANDIDATE,
RESULT_UNIT_ERR,
RESULT_LARGE_ERR,
+ MISNAMED_GETTERS,
]);
impl<'tcx> LateLintPass<'tcx> for Functions {
too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold);
too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold);
not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id);
+ misnamed_getters::check_fn(cx, kind, decl, body, span, hir_id);
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
if let hir::ItemKind::Enum(ref def, _) = item.kind;
then {
let variants_size = AdtVariantInfo::new(cx, *adt, subst);
- if variants_size[0].size >= large_err_threshold {
+ if let Some((first_variant, variants)) = variants_size.split_first()
+ && first_variant.size >= large_err_threshold
+ {
span_lint_and_then(
cx,
RESULT_LARGE_ERR,
"the `Err`-variant returned from this function is very large",
|diag| {
diag.span_label(
- def.variants[variants_size[0].ind].span,
+ def.variants[first_variant.ind].span,
format!("the largest variant contains at least {} bytes", variants_size[0].size),
);
- for variant in &variants_size[1..] {
+ for variant in variants {
if variant.size >= large_err_threshold {
let variant_def = &def.variants[variant.ind];
diag.span_label(
infcx
.err_ctxt()
.maybe_note_obligation_cause_for_async_await(db, &obligation);
- if let PredicateKind::Clause(Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() {
+ if let PredicateKind::Clause(Clause::Trait(trait_pred)) =
+ obligation.predicate.kind().skip_binder()
+ {
db.note(&format!(
"`{}` doesn't implement `{}`",
trait_pred.self_ty(),
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{
- contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks,
-};
+use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
}
pub struct IfThenSomeElseNone {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl IfThenSomeElseNone {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
+ if !self.msrv.meets(msrvs::BOOL_THEN) {
return;
}
} else {
format!("{{ /* snippet */ {arg_snip} }}")
};
- let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+ let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) {
"then_some"
} else {
method_body.insert_str(0, "|| ");
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLet;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::is_copy;
-use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local};
+use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
use if_chain::if_chain;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::Applicability;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::Ident, Span};
/// ```
#[clippy::version = "1.59.0"]
pub INDEX_REFUTABLE_SLICE,
- nursery,
+ pedantic,
"avoid indexing on slices which could be destructed"
}
-#[derive(Copy, Clone)]
pub struct IndexRefutableSlice {
max_suggested_slice: u64,
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl IndexRefutableSlice {
- pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> Self {
+ pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self {
Self {
max_suggested_slice: max_suggested_slice_pattern_length,
msrv,
if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
- if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS);
+ if self.msrv.meets(msrvs::SLICE_PATTERNS);
let found_slices = find_slice_values(cx, let_pat);
if !found_slices.is_empty();
-use clippy_utils::{
- diagnostics::{self, span_lint_and_sugg},
- meets_msrv, msrvs, source,
- sugg::Sugg,
- ty,
-};
+use clippy_utils::diagnostics::{self, span_lint_and_sugg};
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{source_map::Spanned, sym};
}
pub struct InstantSubtraction {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl InstantSubtraction {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
} else {
if_chain! {
if !expr.span.from_expansion();
- if meets_msrv(self.msrv, msrvs::TRY_FROM);
+ if self.msrv.meets(msrvs::TRY_FROM);
if is_an_instant(cx, lhs);
if is_a_duration(cx, rhs);
use std::io;
use std::path::PathBuf;
-use clippy_utils::parse_msrv;
+use clippy_utils::msrvs::Msrv;
use rustc_data_structures::fx::FxHashSet;
use rustc_lint::{Lint, LintId};
-use rustc_semver::RustcVersion;
use rustc_session::Session;
#[cfg(feature = "internal")]
/// Used in `./src/driver.rs`.
pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
// NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
+ let msrv = Msrv::read(&conf.msrv, sess);
+ let msrv = move || msrv.clone();
- let msrv = conf.msrv.as_ref().and_then(|s| {
- parse_msrv(s, None, None).or_else(|| {
- sess.err(format!(
- "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
- ));
- None
- })
- });
-
- store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
-}
-
-fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
- let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
- .ok()
- .and_then(|v| parse_msrv(&v, None, None));
- let clippy_msrv = conf.msrv.as_ref().and_then(|s| {
- parse_msrv(s, None, None).or_else(|| {
- sess.err(format!(
- "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
- ));
- None
- })
- });
-
- if let Some(cargo_msrv) = cargo_msrv {
- if let Some(clippy_msrv) = clippy_msrv {
- // if both files have an msrv, let's compare them and emit a warning if they differ
- if clippy_msrv != cargo_msrv {
- sess.warn(format!(
- "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
- ));
- }
-
- Some(clippy_msrv)
- } else {
- Some(cargo_msrv)
- }
- } else {
- clippy_msrv
- }
+ store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() }));
}
#[doc(hidden)]
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
- let msrv = read_msrv(conf, sess);
+ let msrv = Msrv::read(&conf.msrv, sess);
+ let msrv = move || msrv.clone();
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
let allow_expect_in_tests = conf.allow_expect_in_tests;
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
- store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv)));
+ store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
store.register_late_pass(move |_| {
Box::new(methods::Methods::new(
avoid_breaking_exported_api,
- msrv,
+ msrv(),
allow_expect_in_tests,
allow_unwrap_in_tests,
))
});
- store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv)));
+ store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
let matches_for_let_else = conf.matches_for_let_else;
- store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else)));
- store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
- store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
- store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv)));
- store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
- store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
- store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv)));
- store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv)));
- store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv)));
- store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv)));
- store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv)));
- store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
+ store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv(), matches_for_let_else)));
+ store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv())));
+ store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv())));
+ store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv())));
+ store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv())));
+ store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv())));
+ store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv())));
+ store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv())));
+ store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv())));
+ store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv())));
+ store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv())));
+ store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv())));
store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark));
- store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv)));
- store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
+ store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv())));
+ store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv())));
store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount));
store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod));
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
store.register_late_pass(move |_| {
Box::new(index_refutable_slice::IndexRefutableSlice::new(
max_suggested_slice_pattern_length,
- msrv,
+ msrv(),
))
});
store.register_late_pass(|_| Box::<shadow::Shadow>::default());
store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef));
store.register_late_pass(|_| Box::new(no_effect::NoEffect));
store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
- store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv)));
+ store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv())));
let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
store.register_late_pass(move |_| {
Box::new(cognitive_complexity::CognitiveComplexity::new(
store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
- store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv)));
+ store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default());
store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing));
store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10));
- store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
+ store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv())));
store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
store.register_early_pass(move || Box::new(module_style::ModStyle));
store.register_late_pass(|_| Box::new(unused_async::UnusedAsync));
))
});
store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
- store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv)));
+ let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args;
+ store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined)));
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields));
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
- store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv)));
+ store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv())));
store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
- store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
+ store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv())));
store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef));
store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch));
store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec));
store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
- store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
- store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv)));
+ store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv())));
+ store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv())));
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
- store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv)));
+ store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv())));
store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
- store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv)));
+ store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv())));
store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods));
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
- store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv)));
+ store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
// add lints here, do not remove this comment, it's used in `new_lint`
}
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
use rustc_hir::intravisit::{
- walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
+ walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
};
use rustc_hir::lang_items;
sub_visitor.visit_fn_decl(decl);
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
},
- TyKind::TraitObject(bounds, ref lt, _) => {
+ TyKind::TraitObject(bounds, lt, _) => {
if !lt.is_elided() {
self.unelided_trait_object_lifetime = true;
}
if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res {
self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span);
}
- // Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands.
- // walk_generic_arg(self, generic_arg);
- 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.value),
- GenericArg::Infer(inf) => self.visit_infer(inf),
- }
+ walk_generic_arg(self, generic_arg);
}
}
let hir_id = item.hir_id();
let attrs = cx.tcx.hir().attrs(hir_id);
if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
- if let Res::Def(DefKind::Mod, id) = path.res;
+ if let Some(id) = path.res.iter().find_map(|res| match res {
+ Res::Def(DefKind::Mod, id) => Some(id),
+ _ => None,
+ });
if !id.is_local();
then {
for kid in cx.tcx.module_children(id).iter() {
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::get_parent_expr;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_parent_expr, meets_msrv, msrvs};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
#[derive(Clone)]
pub struct ManualBits {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl ManualBits {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for ManualBits {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) {
+ if !self.msrv.meets(msrvs::MANUAL_BITS) {
return;
}
+use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::higher::If;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::visitors::is_const_evaluatable;
+use clippy_utils::MaybePath;
+use clippy_utils::{
+ eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
+};
use itertools::Itertools;
+use rustc_errors::Applicability;
use rustc_errors::Diagnostic;
use rustc_hir::{
def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span};
use std::ops::Deref;
-use clippy_utils::{
- diagnostics::{span_lint_and_then, span_lint_hir_and_then},
- eq_expr_value,
- higher::If,
- is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks,
- peel_blocks_with_stmt,
- sugg::Sugg,
- ty::implements_trait,
- visitors::is_const_evaluatable,
- MaybePath,
-};
-use rustc_errors::Applicability;
-
declare_clippy_lint! {
/// ### What it does
/// Identifies good opportunities for a clamp function from std or core, and suggests using it.
impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]);
pub struct ManualClamp {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl ManualClamp {
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for ManualClamp {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if !meets_msrv(self.msrv, msrvs::CLAMP) {
+ if !self.msrv.meets(msrvs::CLAMP) {
return;
}
if !expr.span.from_expansion() {
}
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
- if !meets_msrv(self.msrv, msrvs::CLAMP) {
+ if !self.msrv.meets(msrvs::CLAMP) {
return;
}
for suggestion in is_two_if_pattern(cx, block) {
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::{diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, source::snippet};
use rustc_ast::LitKind::{Byte, Char};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{def_id::DefId, sym};
-use clippy_utils::{
- diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet,
-};
-
declare_clippy_lint! {
/// ### What it does
/// Suggests to use dedicated built-in methods,
impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]);
pub struct ManualIsAsciiCheck {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl ManualIsAsciiCheck {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) {
+ if !self.msrv.meets(msrvs::IS_ASCII_DIGIT) {
return;
}
- if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) {
+ if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) {
return;
}
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLetOrMatch;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::peel_blocks;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::{for_each_expr, Descend};
-use clippy_utils::{meets_msrv, msrvs, peel_blocks};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::sym;
use rustc_span::Span;
}
pub struct ManualLetElse {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
matches_behaviour: MatchLintBehaviour,
}
impl ManualLetElse {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>, matches_behaviour: MatchLintBehaviour) -> Self {
+ pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self {
Self {
msrv,
matches_behaviour,
impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) {
let if_let_or_match = if_chain! {
- if meets_msrv(self.msrv, msrvs::LET_ELSE);
+ if self.msrv.meets(msrvs::LET_ELSE);
if !in_external_macro(cx.sess(), stmt.span);
if let StmtKind::Local(local) = stmt.kind;
if let Some(init) = local.init;
// * unused binding collision detection with existing ones
// * putting patterns with at the top level | inside ()
// for this to be machine applicable.
- let app = Applicability::HasPlaceholders;
+ let mut app = Applicability::HasPlaceholders;
+ let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app);
+ let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app);
+ let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app);
- if let Some(sn_pat) = snippet_opt(cx, pat.span) &&
- let Some(sn_expr) = snippet_opt(cx, expr.span) &&
- let Some(sn_else) = snippet_opt(cx, else_body.span)
- {
- let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
- sn_else
- } else {
- format!("{{ {sn_else} }}")
- };
- let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
- diag.span_suggestion(span, "consider writing", sugg, app);
- }
+ let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
+ sn_else.into_owned()
+ } else {
+ format!("{{ {sn_else} }}")
+ };
+ let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
+ diag.span_suggestion(span, "consider writing", sugg, app);
},
);
}
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::is_doc_hidden;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
-use clippy_utils::{is_doc_hidden, meets_msrv, msrvs};
use rustc_ast::ast::{self, VisibilityKind};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{self as hir, Expr, ExprKind, QPath};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_middle::ty::DefIdTree;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::{sym, Span};
#[expect(clippy::module_name_repetitions)]
pub struct ManualNonExhaustiveStruct {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl ManualNonExhaustiveStruct {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
#[expect(clippy::module_name_repetitions)]
pub struct ManualNonExhaustiveEnum {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
constructed_enum_variants: FxHashSet<(DefId, DefId)>,
potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
}
impl ManualNonExhaustiveEnum {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self {
msrv,
constructed_enum_variants: FxHashSet::default(),
impl EarlyLintPass for ManualNonExhaustiveStruct {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
- if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
+ if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
return;
}
impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
- if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
+ if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
return;
}
use clippy_utils::consts::{constant_full_int, FullInt};
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local};
+use clippy_utils::{in_constant, path_to_local};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
}
pub struct ManualRemEuclid {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl ManualRemEuclid {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if !meets_msrv(self.msrv, msrvs::REM_EUCLID) {
+ if !self.msrv.meets(msrvs::REM_EUCLID) {
return;
}
- if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) {
+ if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) {
return;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq};
-use clippy_utils::{meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
}
pub struct ManualRetain {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl ManualRetain {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
&& let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind
&& let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
&& match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) {
- check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
- check_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
- check_to_owned(cx, parent_expr, left_expr, target_expr, self.msrv);
+ check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
+ check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
+ check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv);
}
}
parent_expr: &hir::Expr<'_>,
left_expr: &hir::Expr<'_>,
target_expr: &hir::Expr<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) {
if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind
&& let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
parent_expr: &hir::Expr<'_>,
left_expr: &hir::Expr<'_>,
target_expr: &hir::Expr<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) {
if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
&& let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
parent_expr: &hir::Expr<'_>,
left_expr: &hir::Expr<'_>,
target_expr: &hir::Expr<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) {
- if meets_msrv(msrv, msrvs::STRING_RETAIN)
+ if msrv.meets(msrvs::STRING_RETAIN)
&& let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
&& let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
&& match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD)
.any(|&method| match_def_path(cx, collect_def_id, method))
}
-fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option<RustcVersion>) -> bool {
+fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
is_type_diagnostic_item(cx, expr_ty, *ty)
- && acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv))
+ && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv))
})
}
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use clippy_utils::usage::mutated_variables;
-use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths};
+use clippy_utils::{eq_expr_value, higher, match_def_path, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::def::Res;
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Spanned;
use rustc_span::Span;
}
pub struct ManualStrip {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl ManualStrip {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
+ if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
return;
}
mod try_err;
mod wild_in_or_pats;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet_opt, walk_span_to_context};
-use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs};
+use clippy_utils::{higher, in_constant, is_span_match};
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{Span, SpanData, SyntaxContext};
#[derive(Default)]
pub struct Matches {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
infallible_destructuring_match_linted: bool,
}
impl Matches {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self {
msrv,
..Matches::default()
if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
if source == MatchSource::Normal {
- if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO)
- && match_like_matches::check_match(cx, expr, ex, arms))
- {
+ if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) {
match_same_arms::check(cx, arms);
}
collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
if !from_expansion {
if let Some(else_expr) = if_let.if_else {
- if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
+ if self.msrv.meets(msrvs::MATCHES_MACRO) {
match_like_matches::check_if_let(
cx,
expr,
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths};
+use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::ResultErr;
fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if_chain! {
if let ty::Adt(def, subst) = ty.kind();
- if match_def_path(cx, def.did(), &paths::POLL);
+ if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did());
let ready_ty = subst.type_at(0);
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if_chain! {
if let ty::Adt(def, subst) = ty.kind();
- if match_def_path(cx, def.did(), &paths::POLL);
+ if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did());
let ready_ty = subst.type_at(0);
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::ty::is_non_aggregate_primitive_type;
-use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res};
+use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::sym;
}
pub struct MemReplace {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl MemReplace {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
then {
check_replace_option_with_none(cx, src, dest, expr.span);
check_replace_with_uninit(cx, src, dest, expr.span);
- if meets_msrv(self.msrv, msrvs::MEM_TAKE) {
+ if self.msrv.meets(msrvs::MEM_TAKE) {
check_replace_with_default(cx, src, dest, expr.span);
}
}
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::{get_iterator_item_ty, is_copy};
-use clippy_utils::{is_trait_method, meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_semver::RustcVersion;
use rustc_span::{sym, Span};
use super::CLONED_INSTEAD_OF_COPIED;
-pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<RustcVersion>) {
+pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: &Msrv) {
let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
let inner_ty = match recv_ty.kind() {
// `Option<T>` -> `T`
ty::Adt(adt, subst)
- if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) =>
+ if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && msrv.meets(msrvs::OPTION_COPIED) =>
{
subst.type_at(0)
},
- _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => {
+ _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(msrvs::ITERATOR_COPIED) => {
match get_iterator_item_ty(cx, recv_ty) {
// <T as Iterator>::Item
Some(ty) => ty,
use super::ERR_EXPECT;
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::has_debug_impl;
-use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item};
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_middle::ty::Ty;
-use rustc_semver::RustcVersion;
use rustc_span::{sym, Span};
pub(super) fn check(
cx: &LateContext<'_>,
_expr: &rustc_hir::Expr<'_>,
recv: &rustc_hir::Expr<'_>,
- msrv: Option<RustcVersion>,
expect_span: Span,
err_span: Span,
+ msrv: &Msrv,
) {
if_chain! {
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
// Test the version to make sure the lint can be showed (expect_err has been
// introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982)
- if meets_msrv(msrv, msrvs::EXPECT_ERR);
+ if msrv.meets(msrvs::EXPECT_ERR);
// Grabs the `Result<T, E>` type
let result_type = cx.typeck_results().expr_ty(recv);
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::is_trait_method;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
-use clippy_utils::{is_trait_method, meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_semver::RustcVersion;
use rustc_span::sym;
use super::FILTER_MAP_NEXT;
expr: &'tcx hir::Expr<'_>,
recv: &'tcx hir::Expr<'_>,
arg: &'tcx hir::Expr<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) {
if is_trait_method(cx, expr, sym::Iterator) {
- if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) {
+ if !msrv.meets(msrvs::ITERATOR_FIND_MAP) {
return;
}
//! Lint for `c.is_digit(10)`
use super::IS_DIGIT_ASCII_RADIX;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::{
- consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
- source::snippet_with_applicability,
+ consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability,
};
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
-use rustc_semver::RustcVersion;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
self_arg: &'tcx Expr<'_>,
radix: &'tcx Expr<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) {
- if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) {
+ if !msrv.meets(msrvs::IS_ASCII_DIGIT) {
return;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
-use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
+use clippy_utils::{is_diag_trait_item, peel_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_middle::mir::Mutability;
use rustc_middle::ty;
use rustc_middle::ty::adjustment::Adjust;
-use rustc_semver::RustcVersion;
use rustc_span::symbol::Ident;
use rustc_span::{sym, Span};
use super::MAP_CLONE;
-pub(super) fn check(
- cx: &LateContext<'_>,
- e: &hir::Expr<'_>,
- recv: &hir::Expr<'_>,
- arg: &hir::Expr<'_>,
- msrv: Option<RustcVersion>,
-) {
+pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) {
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
if cx.tcx.impl_of_method(method_id)
);
}
-fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
+fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) {
let mut applicability = Applicability::MachineApplicable;
- let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
+ let (message, sugg_method) = if is_copy && msrv.meets(msrvs::ITERATOR_COPIED) {
("you are using an explicit closure for copying elements", "copied")
} else {
("you are using an explicit closure for cloning elements", "cloned")
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::mutated_variables;
-use clippy_utils::{meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_semver::RustcVersion;
use rustc_span::symbol::sym;
use super::MAP_UNWRAP_OR;
recv: &'tcx hir::Expr<'_>,
map_arg: &'tcx hir::Expr<'_>,
unwrap_arg: &'tcx hir::Expr<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) -> bool {
// lint if the caller of `map()` is an `Option`
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
- if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) {
+ if is_result && !msrv.meets(msrvs::RESULT_MAP_OR_ELSE) {
return false;
}
use bind_instead_of_map::BindInsteadOfMap;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
-use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
+use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, TraitRef, Ty};
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, Span};
pub struct Methods {
avoid_breaking_exported_api: bool,
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
allow_expect_in_tests: bool,
allow_unwrap_in_tests: bool,
}
#[must_use]
pub fn new(
avoid_breaking_exported_api: bool,
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
allow_expect_in_tests: bool,
allow_unwrap_in_tests: bool,
) -> Self {
single_char_add_str::check(cx, expr, receiver, args);
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
- unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv);
+ unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv);
},
hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
let mut info = BinaryExprInfo {
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
- ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
+ ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv),
("collect", []) if is_trait_method(cx, expr, sym::Iterator) => {
needless_collect::check(cx, span, expr, recv, call_span);
match method_call(recv) {
map_collect_result_unit::check(cx, expr, m_recv, m_arg);
},
Some(("take", take_self_arg, [take_arg], _, _)) => {
- if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
+ if self.msrv.meets(msrvs::STR_REPEAT) {
manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
}
},
},
("expect", [_]) => match method_call(recv) {
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
- Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
+ Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
_ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
},
("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
unit_hash::check(cx, expr, recv, arg);
},
("is_file", []) => filetype_is_file::check(cx, expr, recv),
- ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
+ ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv),
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
("iter" | "iter_mut" | "into_iter", []) => {
},
(name @ ("map" | "map_err"), [m_arg]) => {
if name == "map" {
- map_clone::check(cx, expr, recv, m_arg, self.msrv);
+ map_clone::check(cx, expr, recv, m_arg, &self.msrv);
if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) {
iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
}
}
if let Some((name, recv2, args, span2,_)) = method_call(recv) {
match (name, args) {
- ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
- ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
+ ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
+ ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
("filter", [f_arg]) => {
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
},
match (name2, args2) {
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
- ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
+ ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv),
("iter", []) => iter_next_slice::check(cx, expr, recv2),
("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
("skip_while", [_]) => skip_while_next::check(cx, expr),
vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
},
("seek", [arg]) => {
- if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) {
+ if self.msrv.meets(msrvs::SEEK_FROM_CURRENT) {
seek_from_current::check(cx, expr, recv, arg);
}
- if meets_msrv(self.msrv, msrvs::SEEK_REWIND) {
+ if self.msrv.meets(msrvs::SEEK_REWIND) {
seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
}
},
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
suspicious_splitn::check(cx, name, expr, recv, count);
- str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv);
+ str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv);
}
},
("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
},
("take", []) => needless_option_take::check(cx, expr, recv),
("then", [arg]) => {
- if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+ if !self.msrv.meets(msrvs::BOOL_THEN_SOME) {
return;
}
unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
},
("unwrap_or_else", [u_arg]) => match method_call(recv) {
Some(("map", recv, [map_arg], _, _))
- if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
+ if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
_ => {
unwrap_or_else_default::check(cx, expr, recv, u_arg);
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
+use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_semver::RustcVersion;
use rustc_span::sym;
use super::OPTION_AS_REF_DEREF;
as_ref_recv: &hir::Expr<'_>,
map_arg: &hir::Expr<'_>,
is_mut: bool,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) {
- if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) {
+ if !msrv.meets(msrvs::OPTION_AS_DEREF) {
return;
}
impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
type NestedFilter = nested_filter::All;
- fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
+ fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
self.identifiers.insert(ident(path));
walk_path(self, path);
}
impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
type NestedFilter = nested_filter::All;
- fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
+ fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
if self.identifiers.contains(&ident(path)) {
self.found_identifier = true;
return;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_context;
use clippy_utils::usage::local_used_after_expr;
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
-use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
+use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
use core::ops::ControlFlow;
use if_chain::if_chain;
use rustc_errors::Applicability;
};
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_semver::RustcVersion;
use rustc_span::{sym, Span, Symbol, SyntaxContext};
use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
self_arg: &Expr<'_>,
pat_arg: &Expr<'_>,
count: u128,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) {
if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
return;
IterUsageKind::Nth(n) => count > n + 1,
IterUsageKind::NextTuple => count > 2,
};
- let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE);
+ let manual = count == 2 && msrv.meets(msrvs::STR_SPLIT_ONCE);
match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) {
Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
use super::implicit_clone::is_clone_like;
use super::unnecessary_iter_cloned::{self, is_into_iter};
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
use clippy_utils::visitors::find_all_ret_expressions;
-use clippy_utils::{
- fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty,
-};
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
use rustc_errors::Applicability;
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
use rustc_hir_typeck::{FnCtxt, Inherited};
use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
-use rustc_middle::ty::EarlyBinder;
-use rustc_middle::ty::{self, Clause, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
-use rustc_semver::RustcVersion;
+use rustc_middle::ty::{self, Clause, EarlyBinder, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
use rustc_span::{sym, Symbol};
-use rustc_trait_selection::traits::{
- query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause,
-};
-use std::cmp::max;
+use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
use super::UNNECESSARY_TO_OWNED;
method_name: Symbol,
receiver: &'tcx Expr<'_>,
args: &'tcx [Expr<'_>],
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) {
if_chain! {
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
expr: &Expr<'_>,
method_name: Symbol,
receiver: &Expr<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) -> bool {
if_chain! {
if let Some(parent) = get_parent_expr(cx, expr);
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
return true;
}
- let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
+ let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) {
"copied"
} else {
"cloned"
if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id;
let receiver_ty = cx.typeck_results().expr_ty(receiver);
- if can_change_type(cx, maybe_arg, receiver_ty);
// We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
// `Target = T`.
- if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
- let n_refs = max(n_refs, usize::from(!is_copy(cx, receiver_ty)));
+ if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) {
+ Some((n_refs, receiver_ty))
+ } else if trait_predicate.def_id() != deref_trait_id {
+ Some((1, cx.tcx.mk_ref(
+ cx.tcx.lifetimes.re_erased,
+ ty::TypeAndMut {
+ ty: receiver_ty,
+ mutbl: Mutability::Not,
+ },
+ )))
+ } else {
+ None
+ };
+ if can_change_type(cx, maybe_arg, receiver_ty);
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then {
span_lint_and_sugg(
if trait_predicate.trait_ref.self_ty() == input {
trait_predicates.push(trait_predicate);
}
- }
+ },
PredicateKind::Clause(Clause::Projection(projection_predicate)) => {
if projection_predicate.projection_ty.self_ty() == input {
projection_predicates.push(projection_predicate);
}
- }
- _ => {}
+ },
+ _ => {},
}
}
(trait_predicates, projection_predicates)
Node::Expr(parent_expr) => {
if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr)
{
- if Some(callee_def_id) == cx.tcx.lang_items().into_future_fn() {
- return false;
- }
-
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
&& let Some(param_ty) = fn_sig.inputs().get(arg_index)
&& let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
+ // https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021
+ && (*param_index as usize) < call_substs.len()
{
if fn_sig
.inputs()
let mut trait_predicates = cx.tcx.param_env(callee_def_id)
.caller_bounds().iter().filter(|predicate| {
- if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder()
- && trait_predicate.trait_ref.self_ty() == *param_ty {
- true
- } else {
+ if let PredicateKind::Clause(Clause::Trait(trait_predicate))
+ = predicate.kind().skip_binder()
+ && trait_predicate.trait_ref.self_ty() == *param_ty
+ {
+ true
+ } else {
false
}
});
/// Returns true if the named method can be used to convert the receiver to its "owned"
/// representation.
-fn is_to_owned_like<'a>(
- cx: &LateContext<'a>,
- call_expr: &Expr<'a>,
- method_name: Symbol,
- method_def_id: DefId,
-) -> bool {
+fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool {
is_clone_like(cx, method_name.as_str(), method_def_id)
|| is_cow_into_owned(cx, method_name, method_def_id)
|| is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
use clippy_utils::diagnostics::span_lint;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::ty::has_drop;
-use clippy_utils::{
- fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method,
-};
+use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
use rustc_hir as hir;
use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_hir::intravisit::FnKind;
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
pub struct MissingConstForFn {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl MissingConstForFn {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
span: Span,
hir_id: HirId,
) {
- if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) {
+ if !self.msrv.meets(msrvs::CONST_IF_MATCH) {
return;
}
let mir = cx.tcx.optimized_mir(def_id);
- if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) {
+ if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
cx.tcx.sess.span_err(span, err.as_ref());
}
}
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
- if_chain! {
- if let ItemKind::Use(path, UseKind::Single) = &item.kind;
- if let Res::Def(_, id) = path.res;
- if let Some(name) = self.renames.get(&id);
- // Remove semicolon since it is not present for nested imports
- let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';');
- if let Some(snip) = snippet_opt(cx, span_without_semi);
- if let Some(import) = match snip.split_once(" as ") {
- None => Some(snip.as_str()),
- Some((import, rename)) => {
- if rename.trim() == name.as_str() {
- None
- } else {
- Some(import.trim())
+ if let ItemKind::Use(path, UseKind::Single) = &item.kind {
+ for &res in &path.res {
+ if_chain! {
+ if let Res::Def(_, id) = res;
+ if let Some(name) = self.renames.get(&id);
+ // Remove semicolon since it is not present for nested imports
+ let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';');
+ if let Some(snip) = snippet_opt(cx, span_without_semi);
+ if let Some(import) = match snip.split_once(" as ") {
+ None => Some(snip.as_str()),
+ Some((import, rename)) => {
+ if rename.trim() == name.as_str() {
+ None
+ } else {
+ Some(import.trim())
+ }
+ },
+ };
+ then {
+ span_lint_and_sugg(
+ cx,
+ MISSING_ENFORCED_IMPORT_RENAMES,
+ span_without_semi,
+ "this import should be renamed",
+ "try",
+ format!(
+ "{import} as {name}",
+ ),
+ Applicability::MachineApplicable,
+ );
}
- },
- };
- then {
- span_lint_and_sugg(
- cx,
- MISSING_ENFORCED_IMPORT_RENAMES,
- span_without_semi,
- "this import should be renamed",
- "try",
- format!(
- "{import} as {name}",
- ),
- Applicability::MachineApplicable,
- );
+ }
}
}
}
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::ptr::get_spans;
use clippy_utils::source::{snippet, snippet_opt};
-use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::ty::{
+ implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item,
+};
use clippy_utils::{get_trait_def_id, is_self, paths};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
.filter_map(|obligation| {
// Note that we do not want to deal with qualified predicates here.
match obligation.predicate.kind().no_bound_vars() {
- Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => Some(pred),
+ Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => {
+ Some(pred)
+ },
_ => None,
}
})
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use std::ops::Deref;
fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
if_chain! {
if let StmtKind::Semi(expr) = stmt.kind;
+ let ctxt = stmt.span.ctxt();
+ if expr.span.ctxt() == ctxt;
if let Some(reduced) = reduce_expression(cx, expr);
- if !&reduced.iter().any(|e| e.span.from_expansion());
+ if !in_external_macro(cx.sess(), stmt.span);
+ if reduced.iter().all(|e| e.span.ctxt() == ctxt);
then {
if let ExprKind::Index(..) = &expr.kind {
let snippet = if let (Some(arr), Some(func)) =
ty_name: name.ident.name,
method_renames,
ref_prefix: RefPrefix {
- lt: lt.clone(),
+ lt: *lt,
mutability,
},
deref_ty,
cx.tcx,
ObligationCause::dummy(),
cx.param_env,
- cx.tcx.mk_predicate(Binder::dummy(
- PredicateKind::Clause(Clause::Projection(p.with_self_ty(cx.tcx, ty))),
- )),
+ cx.tcx
+ .mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Projection(
+ p.with_self_ty(cx.tcx, ty),
+ )))),
)),
ExistentialPredicate::AutoTrait(p) => infcx
.type_implements_trait(p, [ty], cx.param_env)
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, path_to_local};
use if_chain::if_chain;
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned};
use std::cmp::Ordering;
}
pub struct Ranges {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl Ranges {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for Ranges {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Binary(ref op, l, r) = expr.kind {
- if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
+ if self.msrv.meets(msrvs::RANGE_CONTAINS) {
check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
}
}
"try not to call a closure in the expression where it is declared",
|diag| {
if fn_decl.inputs.is_empty() {
- let app = Applicability::MachineApplicable;
- let mut hint = Sugg::ast(cx, body, "..");
+ let mut app = Applicability::MachineApplicable;
+ let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app);
if asyncness.is_async() {
// `async x` is a syntax error, so it becomes `async { x }`
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::msrvs::{self, Msrv};
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
}
pub struct RedundantFieldNames {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl RedundantFieldNames {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl EarlyLintPass for RedundantFieldNames {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
- if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) {
+ if !self.msrv.meets(msrvs::FIELD_INIT_SHORTHAND) {
return;
}
fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool {
if let ItemKind::Use(path, _) = item.kind {
- if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res {
+ if path.res.iter().all(|res| matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _))) {
return false;
}
} else if let ItemKind::Macro(..) = item.kind {
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
-use clippy_utils::{meets_msrv, msrvs};
use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
}
pub struct RedundantStaticLifetimes {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl RedundantStaticLifetimes {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl EarlyLintPass for RedundantStaticLifetimes {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
- if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) {
+ if !self.msrv.meets(msrvs::STATIC_IN_CONST) {
return;
}
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
+use rustc_span::{BytePos, Pos};
declare_clippy_lint! {
/// ### What it does
if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
if !borrows {
- emit_return_lint(
- cx,
- peeled_drop_expr.span,
- semi_spans,
- inner.as_ref().map(|i| i.span),
- replacement,
- );
+ // check if expr return nothing
+ let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
+ extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
+ } else {
+ peeled_drop_expr.span
+ };
+
+ emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement);
}
}
},
})
.is_some()
}
+
+// Go backwards while encountering whitespace and extend the given Span to that point.
+fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span {
+ if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) {
+ let ws = [' ', '\t', '\n'];
+ if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) {
+ let len = prev_source.len() - non_ws_pos - 1;
+ return sp.with_lo(sp.lo() - BytePos::from_usize(len));
+ }
+ }
+
+ sp
+}
// keep track of `use some_module;` usages
if segments.len() == 1 {
- if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
+ if let UseTreeKind::Simple(None) = use_tree.kind {
let name = segments[0].ident.name;
if !macros.contains(&name) {
single_use_usages.push(SingleUse {
for tree in trees {
let segments = &tree.0.prefix.segments;
if segments.len() == 1 {
- if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
+ if let UseTreeKind::Simple(None) = tree.0.kind {
let name = segments[0].ident.name;
if !macros.contains(&name) {
single_use_usages.push(SingleUse {
mod wrong_transmute;
use clippy_utils::in_constant;
+use clippy_utils::msrvs::Msrv;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::sym;
}
pub struct Transmute {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl_lint_pass!(Transmute => [
CROSSPOINTER_TRANSMUTE,
]);
impl Transmute {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
| transmuting_null::check(cx, e, arg, to_ty)
- | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
+ | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv)
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
use super::TRANSMUTE_PTR_TO_REF;
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{meets_msrv, msrvs, sugg};
+use clippy_utils::sugg;
use rustc_errors::Applicability;
use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty, TypeVisitable};
-use rustc_semver::RustcVersion;
/// Checks for `transmute_ptr_to_ref` lint.
/// Returns `true` if it's triggered, otherwise returns `false`.
to_ty: Ty<'tcx>,
arg: &'tcx Expr<'_>,
path: &'tcx Path<'_>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
(ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => {
let sugg = if let Some(ty) = get_explicit_type(path) {
let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app);
- if meets_msrv(msrv, msrvs::POINTER_CAST) {
+ if msrv.meets(msrvs::POINTER_CAST) {
format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par())
} else if from_ptr_ty.has_erased_regions() {
sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string()
}
} else if from_ptr_ty.ty == *to_ref_ty {
if from_ptr_ty.has_erased_regions() {
- if meets_msrv(msrv, msrvs::POINTER_CAST) {
+ if msrv.meets(msrvs::POINTER_CAST) {
format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par())
} else {
sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}")))
+use std::ops::ControlFlow;
+
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::walk_span_to_context;
+use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
use clippy_utils::{get_parent_node, is_lint_allowed};
+use hir::HirId;
use rustc_data_structures::sync::Lrc;
use rustc_hir as hir;
use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
restriction,
"creating an unsafe block without explaining why it is safe"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `// SAFETY: ` comments on safe code.
+ ///
+ /// ### Why is this bad?
+ /// Safe code has no safety requirements, so there is no need to
+ /// describe safety invariants.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::ptr::NonNull;
+ /// let a = &mut 42;
+ ///
+ /// // SAFETY: references are guaranteed to be non-null.
+ /// let ptr = NonNull::new(a).unwrap();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::ptr::NonNull;
+ /// let a = &mut 42;
+ ///
+ /// let ptr = NonNull::new(a).unwrap();
+ /// ```
+ #[clippy::version = "1.67.0"]
+ pub UNNECESSARY_SAFETY_COMMENT,
+ restriction,
+ "annotating safe code with a safety comment"
+}
-declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
+declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]);
-impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
- fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
+impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
+ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
&& !in_external_macro(cx.tcx.sess, block.span)
&& !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
"consider adding a safety comment on the preceding line",
);
}
+
+ if let Some(tail) = block.expr
+ && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id)
+ && !in_external_macro(cx.tcx.sess, tail.span)
+ && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id)
+ && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos)
+ {
+ span_lint_and_help(
+ cx,
+ UNNECESSARY_SAFETY_COMMENT,
+ tail.span,
+ "expression has unnecessary safety comment",
+ Some(help_span),
+ "consider removing the safety comment",
+ );
+ }
}
- fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if let hir::ItemKind::Impl(imple) = item.kind
- && imple.unsafety == hir::Unsafety::Unsafe
- && !in_external_macro(cx.tcx.sess, item.span)
- && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
- && !is_unsafe_from_proc_macro(cx, item.span)
- && !item_has_safety_comment(cx, item)
+ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) {
+ let (
+ hir::StmtKind::Local(&hir::Local { init: Some(expr), .. })
+ | hir::StmtKind::Expr(expr)
+ | hir::StmtKind::Semi(expr)
+ ) = stmt.kind else { return };
+ if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id)
+ && !in_external_macro(cx.tcx.sess, stmt.span)
+ && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id)
+ && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos)
{
+ span_lint_and_help(
+ cx,
+ UNNECESSARY_SAFETY_COMMENT,
+ stmt.span,
+ "statement has unnecessary safety comment",
+ Some(help_span),
+ "consider removing the safety comment",
+ );
+ }
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
+ if in_external_macro(cx.tcx.sess, item.span) {
+ return;
+ }
+
+ let mk_spans = |pos: BytePos| {
let source_map = cx.tcx.sess.source_map();
+ let span = Span::new(pos, pos, SyntaxContext::root(), None);
+ let help_span = source_map.span_extend_to_next_char(span, '\n', true);
let span = if source_map.is_multiline(item.span) {
source_map.span_until_char(item.span, '\n')
} else {
item.span
};
+ (span, help_span)
+ };
- span_lint_and_help(
- cx,
- UNDOCUMENTED_UNSAFE_BLOCKS,
- span,
- "unsafe impl missing a safety comment",
- None,
- "consider adding a safety comment on the preceding line",
- );
+ let item_has_safety_comment = item_has_safety_comment(cx, item);
+ match (&item.kind, item_has_safety_comment) {
+ // lint unsafe impl without safety comment
+ (hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => {
+ if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
+ && !is_unsafe_from_proc_macro(cx, item.span)
+ {
+ let source_map = cx.tcx.sess.source_map();
+ let span = if source_map.is_multiline(item.span) {
+ source_map.span_until_char(item.span, '\n')
+ } else {
+ item.span
+ };
+
+ span_lint_and_help(
+ cx,
+ UNDOCUMENTED_UNSAFE_BLOCKS,
+ span,
+ "unsafe impl missing a safety comment",
+ None,
+ "consider adding a safety comment on the preceding line",
+ );
+ }
+ },
+ // lint safe impl with unnecessary safety comment
+ (hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => {
+ if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
+ let (span, help_span) = mk_spans(pos);
+
+ span_lint_and_help(
+ cx,
+ UNNECESSARY_SAFETY_COMMENT,
+ span,
+ "impl has unnecessary safety comment",
+ Some(help_span),
+ "consider removing the safety comment",
+ );
+ }
+ },
+ (hir::ItemKind::Impl(_), _) => {},
+ // const and static items only need a safety comment if their body is an unsafe block, lint otherwise
+ (&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => {
+ if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) {
+ let body = cx.tcx.hir().body(body);
+ if !matches!(
+ body.value.kind, hir::ExprKind::Block(block, _)
+ if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
+ ) {
+ let (span, help_span) = mk_spans(pos);
+
+ span_lint_and_help(
+ cx,
+ UNNECESSARY_SAFETY_COMMENT,
+ span,
+ &format!("{} has unnecessary safety comment", item.kind.descr()),
+ Some(help_span),
+ "consider removing the safety comment",
+ );
+ }
+ }
+ },
+ // Aside from unsafe impls and consts/statics with an unsafe block, items in general
+ // do not have safety invariants that need to be documented, so lint those.
+ (_, HasSafetyComment::Yes(pos)) => {
+ if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
+ let (span, help_span) = mk_spans(pos);
+
+ span_lint_and_help(
+ cx,
+ UNNECESSARY_SAFETY_COMMENT,
+ span,
+ &format!("{} has unnecessary safety comment", item.kind.descr()),
+ Some(help_span),
+ "consider removing the safety comment",
+ );
+ }
+ },
+ _ => (),
}
}
}
+fn expr_has_unnecessary_safety_comment<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ comment_pos: BytePos,
+) -> Option<Span> {
+ // this should roughly be the reverse of `block_parents_have_safety_comment`
+ if for_each_expr_with_closures(cx, expr, |expr| match expr.kind {
+ hir::ExprKind::Block(
+ Block {
+ rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
+ ..
+ },
+ _,
+ ) => ControlFlow::Break(()),
+ // statements will be handled by check_stmt itself again
+ hir::ExprKind::Block(..) => ControlFlow::Continue(Descend::No),
+ _ => ControlFlow::Continue(Descend::Yes),
+ })
+ .is_some()
+ {
+ return None;
+ }
+
+ let source_map = cx.tcx.sess.source_map();
+ let span = Span::new(comment_pos, comment_pos, SyntaxContext::root(), None);
+ let help_span = source_map.span_extend_to_next_char(span, '\n', true);
+
+ Some(help_span)
+}
+
fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool {
let source_map = cx.sess().source_map();
let file_pos = source_map.lookup_byte_offset(span.lo());
// won't work. This is to avoid dealing with where such a comment should be place relative to
// attributes and doc comments.
- span_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span)
+ matches!(
+ span_from_macro_expansion_has_safety_comment(cx, span),
+ HasSafetyComment::Yes(_)
+ ) || span_in_body_has_safety_comment(cx, span)
+}
+
+enum HasSafetyComment {
+ Yes(BytePos),
+ No,
+ Maybe,
}
/// Checks if the lines immediately preceding the item contain a safety comment.
#[allow(clippy::collapsible_match)]
-fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
- if span_from_macro_expansion_has_safety_comment(cx, item.span) {
- return true;
+fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment {
+ match span_from_macro_expansion_has_safety_comment(cx, item.span) {
+ HasSafetyComment::Maybe => (),
+ has_safety_comment => return has_safety_comment,
}
- if item.span.ctxt() == SyntaxContext::root() {
- if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
- let comment_start = match parent_node {
- Node::Crate(parent_mod) => {
- comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item)
- },
- Node::Item(parent_item) => {
- if let ItemKind::Mod(parent_mod) = &parent_item.kind {
- comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item)
- } else {
- // Doesn't support impls in this position. Pretend a comment was found.
- return true;
- }
- },
- Node::Stmt(stmt) => {
- if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) {
- match stmt_parent {
- Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo),
- _ => {
- // Doesn't support impls in this position. Pretend a comment was found.
- return true;
- },
- }
- } else {
- // Problem getting the parent node. Pretend a comment was found.
- return true;
- }
- },
- _ => {
+ if item.span.ctxt() != SyntaxContext::root() {
+ return HasSafetyComment::No;
+ }
+ if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
+ let comment_start = match parent_node {
+ Node::Crate(parent_mod) => {
+ comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item)
+ },
+ Node::Item(parent_item) => {
+ if let ItemKind::Mod(parent_mod) = &parent_item.kind {
+ comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item)
+ } else {
// Doesn't support impls in this position. Pretend a comment was found.
- return true;
- },
- };
+ return HasSafetyComment::Maybe;
+ }
+ },
+ Node::Stmt(stmt) => {
+ if let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) {
+ walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo)
+ } else {
+ // Problem getting the parent node. Pretend a comment was found.
+ return HasSafetyComment::Maybe;
+ }
+ },
+ _ => {
+ // Doesn't support impls in this position. Pretend a comment was found.
+ return HasSafetyComment::Maybe;
+ },
+ };
- let source_map = cx.sess().source_map();
- if let Some(comment_start) = comment_start
- && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
- && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
- && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
- && let Some(src) = unsafe_line.sf.src.as_deref()
- {
- unsafe_line.sf.lines(|lines| {
- comment_start_line.line < unsafe_line.line && text_has_safety_comment(
+ let source_map = cx.sess().source_map();
+ if let Some(comment_start) = comment_start
+ && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
+ && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
+ && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
+ && let Some(src) = unsafe_line.sf.src.as_deref()
+ {
+ return unsafe_line.sf.lines(|lines| {
+ if comment_start_line.line >= unsafe_line.line {
+ HasSafetyComment::No
+ } else {
+ match text_has_safety_comment(
src,
&lines[comment_start_line.line + 1..=unsafe_line.line],
unsafe_line.sf.start_pos.to_usize(),
- )
- })
- } else {
- // Problem getting source text. Pretend a comment was found.
- true
- }
- } else {
- // No parent node. Pretend a comment was found.
- true
+ ) {
+ Some(b) => HasSafetyComment::Yes(b),
+ None => HasSafetyComment::No,
+ }
+ }
+ });
+ }
+ }
+ HasSafetyComment::Maybe
+}
+
+/// Checks if the lines immediately preceding the item contain a safety comment.
+#[allow(clippy::collapsible_match)]
+fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment {
+ match span_from_macro_expansion_has_safety_comment(cx, span) {
+ HasSafetyComment::Maybe => (),
+ has_safety_comment => return has_safety_comment,
+ }
+
+ if span.ctxt() != SyntaxContext::root() {
+ return HasSafetyComment::No;
+ }
+
+ if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) {
+ let comment_start = match parent_node {
+ Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo),
+ _ => return HasSafetyComment::Maybe,
+ };
+
+ let source_map = cx.sess().source_map();
+ if let Some(comment_start) = comment_start
+ && let Ok(unsafe_line) = source_map.lookup_line(span.lo())
+ && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
+ && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
+ && let Some(src) = unsafe_line.sf.src.as_deref()
+ {
+ return unsafe_line.sf.lines(|lines| {
+ if comment_start_line.line >= unsafe_line.line {
+ HasSafetyComment::No
+ } else {
+ match text_has_safety_comment(
+ src,
+ &lines[comment_start_line.line + 1..=unsafe_line.line],
+ unsafe_line.sf.start_pos.to_usize(),
+ ) {
+ Some(b) => HasSafetyComment::Yes(b),
+ None => HasSafetyComment::No,
+ }
+ }
+ });
}
- } else {
- false
}
+ HasSafetyComment::Maybe
}
-fn comment_start_before_impl_in_mod(
+fn comment_start_before_item_in_mod(
cx: &LateContext<'_>,
parent_mod: &hir::Mod<'_>,
parent_mod_span: Span,
- imple: &hir::Item<'_>,
+ item: &hir::Item<'_>,
) -> Option<BytePos> {
parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| {
- if *item_id == imple.item_id() {
+ if *item_id == item.item_id() {
if idx == 0 {
// mod A { /* comment */ unsafe impl T {} ... }
// ^------------------------------------------^ returns the start of this span
})
}
-fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
+fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment {
let source_map = cx.sess().source_map();
let ctxt = span.ctxt();
if ctxt == SyntaxContext::root() {
- false
+ HasSafetyComment::Maybe
} else {
// From a macro expansion. Get the text from the start of the macro declaration to start of the
// unsafe block.
&& let Some(src) = unsafe_line.sf.src.as_deref()
{
unsafe_line.sf.lines(|lines| {
- macro_line.line < unsafe_line.line && text_has_safety_comment(
- src,
- &lines[macro_line.line + 1..=unsafe_line.line],
- unsafe_line.sf.start_pos.to_usize(),
- )
+ if macro_line.line < unsafe_line.line {
+ match text_has_safety_comment(
+ src,
+ &lines[macro_line.line + 1..=unsafe_line.line],
+ unsafe_line.sf.start_pos.to_usize(),
+ ) {
+ Some(b) => HasSafetyComment::Yes(b),
+ None => HasSafetyComment::No,
+ }
+ } else {
+ HasSafetyComment::No
+ }
})
} else {
// Problem getting source text. Pretend a comment was found.
- true
+ HasSafetyComment::Maybe
}
}
}
src,
&lines[body_line.line + 1..=unsafe_line.line],
unsafe_line.sf.start_pos.to_usize(),
- )
+ ).is_some()
})
} else {
// Problem getting source text. Pretend a comment was found.
}
/// Checks if the given text has a safety comment for the immediately proceeding line.
-fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool {
+fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> Option<BytePos> {
let mut lines = line_starts
.array_windows::<2>()
.rev()
.map_while(|[start, end]| {
let start = start.to_usize() - offset;
let end = end.to_usize() - offset;
- src.get(start..end).map(|text| (start, text.trim_start()))
+ let text = src.get(start..end)?;
+ let trimmed = text.trim_start();
+ Some((start + (text.len() - trimmed.len()), trimmed))
})
.filter(|(_, text)| !text.is_empty());
let Some((line_start, line)) = lines.next() else {
- return false;
+ return None;
};
// Check for a sequence of line comments.
if line.starts_with("//") {
- let mut line = line;
+ let (mut line, mut line_start) = (line, line_start);
loop {
if line.to_ascii_uppercase().contains("SAFETY:") {
- return true;
+ return Some(BytePos(
+ u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(),
+ ));
}
match lines.next() {
- Some((_, x)) if x.starts_with("//") => line = x,
- _ => return false,
+ Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s),
+ _ => return None,
}
}
}
let (mut line_start, mut line) = (line_start, line);
loop {
if line.starts_with("/*") {
- let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
+ let src = &src[line_start..line_starts.last().unwrap().to_usize() - offset];
let mut tokens = tokenize(src);
- return src[..tokens.next().unwrap().len as usize]
+ return (src[..tokens.next().unwrap().len as usize]
.to_ascii_uppercase()
.contains("SAFETY:")
- && tokens.all(|t| t.kind == TokenKind::Whitespace);
+ && tokens.all(|t| t.kind == TokenKind::Whitespace))
+ .then_some(BytePos(
+ u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(),
+ ));
}
match lines.next() {
Some(x) => (line_start, line) = x,
- None => return false,
+ None => return None,
}
}
}
format!(
"{}{};",
last_segment.ident,
- if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {alias}") } else { String::new() },
+ if let UseTreeKind::Simple(Some(alias)) = self_tree.kind { format!(" as {alias}") } else { String::new() },
),
Applicability::MaybeIncorrect,
);
use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{meets_msrv, msrvs, over};
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::over;
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::DUMMY_SP;
"unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
}
-#[derive(Clone, Copy)]
pub struct UnnestedOrPatterns {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl UnnestedOrPatterns {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl EarlyLintPass for UnnestedOrPatterns {
fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
- if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+ if self.msrv.meets(msrvs::OR_PATTERNS) {
lint_unnested_or_patterns(cx, &a.pat);
}
}
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
- if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+ if self.msrv.meets(msrvs::OR_PATTERNS) {
if let ast::ExprKind::Let(pat, _, _) = &e.kind {
lint_unnested_or_patterns(cx, pat);
}
}
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
- if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+ if self.msrv.meets(msrvs::OR_PATTERNS) {
lint_unnested_or_patterns(cx, &p.pat);
}
}
fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
- if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
+ if self.msrv.meets(msrvs::OR_PATTERNS) {
lint_unnested_or_patterns(cx, &l.pat);
}
}
fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) {
match use_tree.kind {
- UseTreeKind::Simple(Some(new_name), ..) => {
+ UseTreeKind::Simple(Some(new_name)) => {
let old_name = use_tree
.prefix
.segments
.ident;
unsafe_to_safe_check(old_name, new_name, cx, span);
},
- UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {},
+ UseTreeKind::Simple(None) | UseTreeKind::Glob => {},
UseTreeKind::Nested(ref nested_use_tree) => {
for (use_tree, _) in nested_use_tree {
check_use_tree(use_tree, cx, span);
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
use rustc_ast::ast::{Expr, ExprKind, MethodCall};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
}
declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]);
-fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
+fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> {
if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind
&& let method_name = name_ident.ident.name.as_str()
&& (method_name == "ceil" || method_name == "round" || method_name == "floor")
&& let ExprKind::Lit(token_lit) = &receiver.kind
- && token_lit.is_semantic_float() {
- let mut f_str = token_lit.symbol.to_string();
- let f = f_str.trim_end_matches('_').parse::<f64>().unwrap();
- if let Some(suffix) = token_lit.suffix {
- f_str.push_str(suffix.as_str());
- }
- if f.fract() == 0.0 {
- Some((method_name, f_str))
- } else {
- None
- }
+ && token_lit.is_semantic_float()
+ && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::<f64>() {
+ (f.fract() == 0.0).then(||
+ (method_name, snippet(cx, receiver.span, "..").to_string())
+ )
} else {
None
}
impl EarlyLintPass for UnusedRounding {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
- if let Some((method_name, float)) = is_useless_rounding(expr) {
+ if let Some((method_name, float)) = is_useless_rounding(cx, expr) {
span_lint_and_sugg(
cx,
UNUSED_ROUNDING,
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_from_proc_macro;
+use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::same_type_and_consts;
-use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
#[derive(Default)]
pub struct UseSelf {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
stack: Vec<StackItem>,
}
impl UseSelf {
#[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
+ pub fn new(msrv: Msrv) -> Self {
Self {
msrv,
..Self::default()
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
if_chain! {
if !hir_ty.span.from_expansion();
- if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+ if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
if let Some(&StackItem::Check {
impl_id,
in_body,
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if !expr.span.from_expansion();
- if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+ if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
then {} else { return; }
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
if_chain! {
if !pat.span.from_expansion();
- if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+ if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
// get the path from the pattern
if let PatKind::Path(QPath::Resolved(_, path))
/// A list of paths to types that should be treated like `Arc`, i.e. ignored but
/// for the generic parameters for determining interior mutability
(ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])),
+ /// Lint: UNINLINED_FORMAT_ARGS.
+ ///
+ /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
+ (allow_mixed_uninlined_format_args: bool = true),
}
/// Search for the configuration file.
impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
type NestedFilter = nested_filter::All;
- fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
+ fn visit_path(&mut self, path: &Path<'_>, _: HirId) {
if path.segments.len() == 1 {
self.output.insert(path.segments[0].ident.name);
}
self.cx.tcx.hir()
}
- fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
+ fn visit_path(&mut self, path: &hir::Path<'hir>, _id: hir::HirId) {
for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
if match_path(path, enum_value) {
self.add_new_index(index);
.type_of(f.did)
.walk()
.filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
- .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
+ .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV))
});
if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
then {
format!("{import_source_snippet}::{imports_string}")
};
- let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res {
+ // Glob imports always have a single resolution.
+ let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] {
(ENUM_GLOB_USE, "usage of wildcard import for enum variants")
} else {
(WILDCARD_IMPORTS, "usage of wildcard import")
use UseTreeKind::*;
match (l, r) {
(Glob, Glob) => true,
- (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
+ (Simple(l), Simple(r)) => both(l, r, |l, r| eq_id(*l, *r)),
(Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
_ => false,
}
}
}
-pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
- let mut unique_attr = None;
+pub fn get_unique_attr<'a>(
+ sess: &'a Session,
+ attrs: &'a [ast::Attribute],
+ name: &'static str,
+) -> Option<&'a ast::Attribute> {
+ let mut unique_attr: Option<&ast::Attribute> = None;
for attr in get_attr(sess, attrs, name) {
- match attr.style {
- ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
- ast::AttrStyle::Inner => {
- sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
- .span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
- .emit();
- },
- ast::AttrStyle::Outer => {
- sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute"));
- },
+ if let Some(duplicate) = unique_attr {
+ sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
+ .span_note(duplicate.span, "first definition found here")
+ .emit();
+ } else {
+ unique_attr = Some(attr);
}
}
unique_attr
}
}
+fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res {
+ cx.typeck_results()
+ .expr_ty(e)
+ .has_significant_drop(cx.tcx, cx.param_env)
+ } else {
+ false
+ }
+}
+
#[expect(clippy::too_many_lines)]
fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
struct V<'cx, 'tcx> {
},
args,
) => match self.cx.qpath_res(path, hir_id) {
- Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => {
- if self
- .cx
- .typeck_results()
- .expr_ty(e)
- .has_significant_drop(self.cx.tcx, self.cx.param_env)
- {
+ res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => {
+ if res_has_significant_drop(res, self.cx, e) {
self.eagerness = ForceNoChange;
return;
}
self.eagerness |= NoChange;
return;
},
+ ExprKind::Path(ref path) => {
+ if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
+ self.eagerness = ForceNoChange;
+ return;
+ }
+ },
ExprKind::MethodCall(name, ..) => {
self.eagerness |= self
.cx
| ExprKind::Match(..)
| ExprKind::Closure { .. }
| ExprKind::Field(..)
- | ExprKind::Path(_)
| ExprKind::AddrOf(..)
| ExprKind::Struct(..)
| ExprKind::Repeat(..)
layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
};
use rustc_middle::ty::{FloatTy, IntTy, UintTy};
-use rustc_semver::RustcVersion;
-use rustc_session::Session;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::SourceMap;
use rustc_span::sym;
use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
use crate::visitors::for_each_expr;
-pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
- if let Ok(version) = RustcVersion::parse(msrv) {
- return Some(version);
- } else if let Some(sess) = sess {
- if let Some(span) = span {
- sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
- }
- }
- None
-}
-
-pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
- msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
-}
-
#[macro_export]
macro_rules! extract_msrv_attr {
($context:ident) => {
fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
let sess = rustc_lint::LintContext::sess(cx);
- match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
- Some(msrv_attr) => {
- if let Some(msrv) = msrv_attr.value_str() {
- self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
- } else {
- sess.span_err(msrv_attr.span, "bad clippy attribute");
- }
- },
- _ => (),
- }
+ self.msrv.enter_lint_attrs(sess, attrs);
+ }
+
+ fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
+ let sess = rustc_lint::LintContext::sess(cx);
+ self.msrv.exit_lint_attrs(sess, attrs);
}
};
}
+use std::sync::OnceLock;
+
+use rustc_ast::Attribute;
use rustc_semver::RustcVersion;
+use rustc_session::Session;
+use rustc_span::Span;
+
+use crate::attrs::get_unique_attr;
macro_rules! msrv_aliases {
($($major:literal,$minor:literal,$patch:literal {
1,16,0 { STR_REPEAT }
1,55,0 { SEEK_REWIND }
}
+
+fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
+ if let Ok(version) = RustcVersion::parse(msrv) {
+ return Some(version);
+ } else if let Some(sess) = sess {
+ if let Some(span) = span {
+ sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
+ }
+ }
+ None
+}
+
+/// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]`
+#[derive(Debug, Clone, Default)]
+pub struct Msrv {
+ stack: Vec<RustcVersion>,
+}
+
+impl Msrv {
+ fn new(initial: Option<RustcVersion>) -> Self {
+ Self {
+ stack: Vec::from_iter(initial),
+ }
+ }
+
+ fn read_inner(conf_msrv: &Option<String>, sess: &Session) -> Self {
+ let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
+ .ok()
+ .and_then(|v| parse_msrv(&v, None, None));
+ let clippy_msrv = conf_msrv.as_ref().and_then(|s| {
+ parse_msrv(s, None, None).or_else(|| {
+ sess.err(format!(
+ "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
+ ));
+ None
+ })
+ });
+
+ // if both files have an msrv, let's compare them and emit a warning if they differ
+ if let Some(cargo_msrv) = cargo_msrv
+ && let Some(clippy_msrv) = clippy_msrv
+ && clippy_msrv != cargo_msrv
+ {
+ sess.warn(format!(
+ "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
+ ));
+ }
+
+ Self::new(clippy_msrv.or(cargo_msrv))
+ }
+
+ /// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version`
+ /// field in `Cargo.toml`
+ ///
+ /// Returns a `&'static Msrv` as `Copy` types are more easily passed to the
+ /// `register_{late,early}_pass` callbacks
+ pub fn read(conf_msrv: &Option<String>, sess: &Session) -> &'static Self {
+ static PARSED: OnceLock<Msrv> = OnceLock::new();
+
+ PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess))
+ }
+
+ pub fn current(&self) -> Option<RustcVersion> {
+ self.stack.last().copied()
+ }
+
+ pub fn meets(&self, required: RustcVersion) -> bool {
+ self.current().map_or(true, |version| version.meets(required))
+ }
+
+ fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
+ if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") {
+ if let Some(msrv) = msrv_attr.value_str() {
+ return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
+ }
+
+ sess.span_err(msrv_attr.span, "bad clippy attribute");
+ }
+
+ None
+ }
+
+ pub fn enter_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
+ if let Some(version) = Self::parse_attr(sess, attrs) {
+ self.stack.push(version);
+ }
+ }
+
+ pub fn exit_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
+ if Self::parse_attr(sess, attrs).is_some() {
+ self.stack.pop();
+ }
+ }
+}
#[cfg(feature = "internal")]
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
+#[cfg(feature = "internal")]
+pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
-pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
-#[cfg(feature = "internal")]
-pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
// differ from the time of `rustc` even if the name stays the same.
+use crate::msrvs::Msrv;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
type McfResult = Result<(), (Span, Cow<'static, str>)>;
-pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult {
+pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) -> McfResult {
let def_id = body.source.def_id();
let mut current = def_id;
loop {
let predicates = tcx.predicates_of(current);
for (predicate, _) in predicates.predicates {
match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
+ ty::PredicateKind::Clause(
+ ty::Clause::RegionOutlives(_)
+ | ty::Clause::TypeOutlives(_)
+ | ty::Clause::Projection(_)
+ | ty::Clause::Trait(..),
+ )
| ty::PredicateKind::WellFormed(_)
- | ty::PredicateKind::Clause(ty::Clause::Projection(_))
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Clause(ty::Clause::Trait(..))
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"),
ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
terminator: &Terminator<'tcx>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) -> McfResult {
let span = terminator.source_info.span;
match &terminator.kind {
TerminatorKind::SwitchInt {
discr,
- switch_ty: _,
targets: _,
} => check_operand(tcx, discr, span, body),
}
}
-fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
+fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
tcx.is_const_fn(def_id)
&& tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level {
let since = rustc_span::Symbol::intern(short_version);
- crate::meets_msrv(
- msrv,
- RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
- panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
- }),
- )
+ msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
+ panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
+ }))
} else {
// Unstable const fn with the feature enabled.
- msrv.is_none()
+ msrv.current().is_none()
}
})
}
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LintContext};
+use rustc_session::Session;
use rustc_span::hygiene;
use rustc_span::source_map::{original_sp, SourceMap};
use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
span: Span,
default: &'a str,
applicability: &mut Applicability,
+) -> Cow<'a, str> {
+ snippet_with_applicability_sess(cx.sess(), span, default, applicability)
+}
+
+fn snippet_with_applicability_sess<'a>(
+ sess: &Session,
+ span: Span,
+ default: &'a str,
+ applicability: &mut Applicability,
) -> Cow<'a, str> {
if *applicability != Applicability::Unspecified && span.from_expansion() {
*applicability = Applicability::MaybeIncorrect;
}
- snippet_opt(cx, span).map_or_else(
+ snippet_opt_sess(sess, span).map_or_else(
|| {
if *applicability == Applicability::MachineApplicable {
*applicability = Applicability::HasPlaceholders;
}
/// Converts a span to a code snippet. Returns `None` if not available.
-pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
- cx.sess().source_map().span_to_snippet(span).ok()
+pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
+ snippet_opt_sess(cx.sess(), span)
+}
+
+fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
+ sess.source_map().span_to_snippet(span).ok()
}
/// Converts a span (from a block) to a code snippet if available, otherwise use default.
/// Same as `snippet_block`, but adapts the applicability level by the rules of
/// `snippet_with_applicability`.
-pub fn snippet_block_with_applicability<'a, T: LintContext>(
- cx: &T,
+pub fn snippet_block_with_applicability<'a>(
+ cx: &impl LintContext,
span: Span,
default: &'a str,
indent_relative_to: Option<Span>,
///
/// This will also return whether or not the snippet is a macro call.
pub fn snippet_with_context<'a>(
- cx: &LateContext<'_>,
+ cx: &impl LintContext,
+ span: Span,
+ outer: SyntaxContext,
+ default: &'a str,
+ applicability: &mut Applicability,
+) -> (Cow<'a, str>, bool) {
+ snippet_with_context_sess(cx.sess(), span, outer, default, applicability)
+}
+
+fn snippet_with_context_sess<'a>(
+ sess: &Session,
span: Span,
outer: SyntaxContext,
default: &'a str,
);
(
- snippet_with_applicability(cx, span, default, applicability),
+ snippet_with_applicability_sess(sess, span, default, applicability),
is_macro_call,
)
}
}
/// Prepare a suggestion from an expression.
- pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
+ pub fn ast(
+ cx: &EarlyContext<'_>,
+ expr: &ast::Expr,
+ default: &'a str,
+ ctxt: SyntaxContext,
+ app: &mut Applicability,
+ ) -> Self {
use rustc_ast::ast::RangeLimits;
- let snippet_without_expansion = |cx, span: Span, default| {
- if span.from_expansion() {
- snippet_with_macro_callsite(cx, span, default)
- } else {
- snippet(cx, span, default)
- }
- };
-
+ #[expect(clippy::match_wildcard_for_single_variants)]
match expr.kind {
+ _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
ast::ExprKind::AddrOf(..)
| ast::ExprKind::Box(..)
| ast::ExprKind::Closure { .. }
| ast::ExprKind::If(..)
| ast::ExprKind::Let(..)
| ast::ExprKind::Unary(..)
- | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)),
+ | ast::ExprKind::Match(..) => match snippet_with_context(cx, expr.span, ctxt, default, app) {
+ (snip, false) => Sugg::MaybeParen(snip),
+ (snip, true) => Sugg::NonParen(snip),
+ },
ast::ExprKind::Async(..)
| ast::ExprKind::Block(..)
| ast::ExprKind::Break(..)
| ast::ExprKind::Array(..)
| ast::ExprKind::While(..)
| ast::ExprKind::Await(..)
- | ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)),
+ | ast::ExprKind::Err => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
AssocOp::DotDot,
- lhs.as_ref()
- .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
- rhs.as_ref()
- .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
+ lhs.as_ref().map_or("".into(), |lhs| {
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0
+ }),
+ rhs.as_ref().map_or("".into(), |rhs| {
+ snippet_with_context(cx, rhs.span, ctxt, default, app).0
+ }),
),
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
AssocOp::DotDotEq,
- lhs.as_ref()
- .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
- rhs.as_ref()
- .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
+ lhs.as_ref().map_or("".into(), |lhs| {
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0
+ }),
+ rhs.as_ref().map_or("".into(), |rhs| {
+ snippet_with_context(cx, rhs.span, ctxt, default, app).0
+ }),
),
ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
AssocOp::Assign,
- snippet_without_expansion(cx, lhs.span, default),
- snippet_without_expansion(cx, rhs.span, default),
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+ snippet_with_context(cx, rhs.span, ctxt, default, app).0,
),
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
astbinop2assignop(op),
- snippet_without_expansion(cx, lhs.span, default),
- snippet_without_expansion(cx, rhs.span, default),
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+ snippet_with_context(cx, rhs.span, ctxt, default, app).0,
),
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
AssocOp::from_ast_binop(op.node),
- snippet_without_expansion(cx, lhs.span, default),
- snippet_without_expansion(cx, rhs.span, default),
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+ snippet_with_context(cx, rhs.span, ctxt, default, app).0,
),
ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::As,
- snippet_without_expansion(cx, lhs.span, default),
- snippet_without_expansion(cx, ty.span, default),
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+ snippet_with_context(cx, ty.span, ctxt, default, app).0,
),
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::Colon,
- snippet_without_expansion(cx, lhs.span, default),
- snippet_without_expansion(cx, ty.span, default),
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+ snippet_with_context(cx, ty.span, ctxt, default, app).0,
),
}
}
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
-use rustc_infer::infer::{TyCtxtInferExt, type_variable::{TypeVariableOrigin, TypeVariableOriginKind}};
+use rustc_infer::infer::{
+ type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
+ TyCtxtInferExt,
+};
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_middle::ty::{
trait_id: DefId,
ty_params: &[GenericArg<'tcx>],
) -> bool {
- implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params.iter().map(|&arg| Some(arg)))
+ implements_trait_with_env(
+ cx.tcx,
+ cx.param_env,
+ ty,
+ trait_id,
+ ty_params.iter().map(|&arg| Some(arg)),
+ )
}
/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
kind: TypeVariableOriginKind::MiscVariable,
span: DUMMY_SP,
};
- let ty_params = tcx.mk_substs(ty_params.into_iter().map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())));
+ let ty_params = tcx.mk_substs(
+ ty_params
+ .into_iter()
+ .map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())),
+ );
infcx
.type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
.must_apply_modulo_regions()
}
inputs = Some(i);
},
- PredicateKind::Clause(ty::Clause::Projection(p)) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
+ PredicateKind::Clause(ty::Clause::Projection(p))
+ if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() =>
+ {
if output.is_some() {
// Multiple different fn trait impls. Is this even allowed?
return None;
debug_assert!(
generic_count == substs.len(),
- "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\
+ "wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\
note: the expected parameters are: {:#?}\n\
- the given arguments are: `{:#?}`",
+ the given arguments are: `{substs:#?}`",
assoc_item.def_id,
substs.len(),
- generic_count,
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
- substs,
);
if let Some((idx, (param, arg))) = params
{
debug_assert!(
false,
- "mismatched subst type at index {}: expected a {}, found `{:?}`\n\
+ "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\
note: the expected parameters are {:#?}\n\
- the given arguments are {:#?}",
- idx,
+ the given arguments are {substs:#?}",
param.descr(),
- arg,
- params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
- substs,
+ params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
);
}
}
}
}
- fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+ fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
if let hir::def::Res::Local(id) = path.res {
if self.binding_ids.contains(&id) {
self.usage_found = true;
cb: F,
}
- struct WithStmtGuarg<'a, F> {
+ struct WithStmtGuard<'a, F> {
val: &'a mut RetFinder<F>,
prev_in_stmt: bool,
}
impl<F> RetFinder<F> {
- fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
+ fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> {
let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
- WithStmtGuarg {
+ WithStmtGuard {
val: self,
prev_in_stmt,
}
}
}
- impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
+ impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
type Target = RetFinder<F>;
fn deref(&self) -> &Self::Target {
}
}
- impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
+ impl<F> std::ops::DerefMut for WithStmtGuard<'_, F> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.val
}
}
- impl<F> Drop for WithStmtGuarg<'_, F> {
+ impl<F> Drop for WithStmtGuard<'_, F> {
fn drop(&mut self) {
self.val.in_stmt = self.prev_in_stmt;
}
format!("$CARGO_HOME/{}", stripped.display())
} else {
format!(
- "target/lintcheck/sources/{}-{}/{}",
- crate_name, crate_version, span.file_name
+ "target/lintcheck/sources/{crate_name}-{crate_version}/{}",
+ span.file_name
)
};
if config.max_jobs == 1 {
println!(
- "{}/{} {}% Linting {} {}",
- index, total_crates_to_lint, perc, &self.name, &self.version
+ "{index}/{total_crates_to_lint} {perc}% Linting {} {}",
+ &self.name, &self.version
);
} else {
println!(
- "{}/{} {}% Linting {} {} in target dir {:?}",
- index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
+ "{index}/{total_crates_to_lint} {perc}% Linting {} {} in target dir {thread_index:?}",
+ &self.name, &self.version
);
}
.output()
.unwrap_or_else(|error| {
panic!(
- "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
- error,
+ "Encountered error:\n{error:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
&cargo_clippy_path.display(),
&self.path.display()
);
[toolchain]
-channel = "nightly-2022-11-21"
+channel = "nightly-2022-12-01"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
#![feature(rustc_private)]
#![feature(let_chains)]
#![feature(once_cell)]
+#![feature(lint_reasons)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
// During development track the `clippy-driver` executable so that cargo will re-run clippy whenever
// it is rebuilt
+ #[expect(
+ clippy::collapsible_if,
+ reason = "Due to a bug in let_chains this if statement can't be collapsed"
+ )]
if cfg!(debug_assertions) {
if let Ok(current_exe) = env::current_exe()
&& let Some(current_exe) = current_exe.to_str()
#[macro_use]
extern crate rustc_session;
use clippy_utils::extract_msrv_attr;
+use clippy_utils::msrvs::Msrv;
use rustc_hir::Expr;
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
declare_lint! {
pub TEST_LINT,
}
struct Pass {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl_lint_pass!(Pass => [TEST_LINT]);
#[macro_use]
extern crate rustc_session;
use clippy_utils::extract_msrv_attr;
+use clippy_utils::msrvs::Msrv;
use rustc_hir::Expr;
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
declare_lint! {
pub TEST_LINT,
}
struct Pass {
- msrv: Option<RustcVersion>,
+ msrv: Msrv,
}
impl_lint_pass!(Pass => [TEST_LINT]);
-error: hardcoded path to a language item
- --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
- |
-LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: convert all references to use `LangItem::DerefMut`
- = note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
-
error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::Deref`
+ = note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
|
= help: convert all references to use `sym::deref_method`
+error: hardcoded path to a language item
+ --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
+ |
+LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: convert all references to use `LangItem::DerefMut`
+
error: aborting due to 3 previous errors
--- /dev/null
+allow-mixed-uninlined-format-args = false
--- /dev/null
+// run-rustfix
+#![warn(clippy::uninlined_format_args)]
+
+fn main() {
+ let local_i32 = 1;
+ let local_f64 = 2.0;
+ let local_opt: Option<i32> = Some(3);
+
+ println!("val='{local_i32}'");
+ println!("Hello x is {local_f64:.local_i32$}");
+ println!("Hello {local_i32} is {local_f64:.*}", 5);
+ println!("Hello {local_i32} is {local_f64:.*}", 5);
+ println!("{local_i32}, {}", local_opt.unwrap());
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::uninlined_format_args)]
+
+fn main() {
+ let local_i32 = 1;
+ let local_f64 = 2.0;
+ let local_opt: Option<i32> = Some(3);
+
+ println!("val='{}'", local_i32);
+ println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+ println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+ println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
+ println!("{}, {}", local_i32, local_opt.unwrap());
+}
--- /dev/null
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args.rs:9:5
+ |
+LL | println!("val='{}'", local_i32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::uninlined-format-args` implied by `-D warnings`
+help: change this to
+ |
+LL - println!("val='{}'", local_i32);
+LL + println!("val='{local_i32}'");
+ |
+
+error: literal with an empty format string
+ --> $DIR/uninlined_format_args.rs:10:35
+ |
+LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+ | ^^^
+ |
+ = note: `-D clippy::print-literal` implied by `-D warnings`
+help: try this
+ |
+LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+LL + println!("Hello x is {:.*}", local_i32, local_f64);
+ |
+
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args.rs:10:5
+ |
+LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: change this to
+ |
+LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+LL + println!("Hello {} is {local_f64:.local_i32$}", "x");
+ |
+
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args.rs:11:5
+ |
+LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: change this to
+ |
+LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+LL + println!("Hello {local_i32} is {local_f64:.*}", 5);
+ |
+
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args.rs:12:5
+ |
+LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: change this to
+ |
+LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
+LL + println!("Hello {local_i32} is {local_f64:.*}", 5);
+ |
+
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args.rs:13:5
+ |
+LL | println!("{}, {}", local_i32, local_opt.unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: change this to
+ |
+LL - println!("{}, {}", local_i32, local_opt.unwrap());
+LL + println!("{local_i32}, {}", local_opt.unwrap());
+ |
+
+error: aborting due to 6 previous errors
+
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of
allow-dbg-in-tests
allow-expect-in-tests
+ allow-mixed-uninlined-format-args
allow-print-in-tests
allow-unwrap-in-tests
allowed-scripts
// edition:2018
// aux-build:macro_rules.rs
-#![feature(custom_inner_attributes)]
#![feature(exclusive_range_pattern)]
#![feature(stmt_expr_attributes)]
#![warn(clippy::almost_complete_letter_range)]
b!();
}
+#[clippy::msrv = "1.25"]
fn _under_msrv() {
- #![clippy::msrv = "1.25"]
let _ = match 'a' {
'a'...'z' => 1,
_ => 2,
};
}
+#[clippy::msrv = "1.26"]
fn _meets_msrv() {
- #![clippy::msrv = "1.26"]
let _ = 'a'..='z';
let _ = match 'a' {
'a'..='z' => 1,
// edition:2018
// aux-build:macro_rules.rs
-#![feature(custom_inner_attributes)]
#![feature(exclusive_range_pattern)]
#![feature(stmt_expr_attributes)]
#![warn(clippy::almost_complete_letter_range)]
b!();
}
+#[clippy::msrv = "1.25"]
fn _under_msrv() {
- #![clippy::msrv = "1.25"]
let _ = match 'a' {
'a'..'z' => 1,
_ => 2,
};
}
+#[clippy::msrv = "1.26"]
fn _meets_msrv() {
- #![clippy::msrv = "1.26"]
let _ = 'a'..'z';
let _ = match 'a' {
'a'..'z' => 1,
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:30:17
+ --> $DIR/almost_complete_letter_range.rs:29:17
|
LL | let _ = ('a') ..'z';
| ^^^^^^--^^^
= note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:31:17
+ --> $DIR/almost_complete_letter_range.rs:30:17
|
LL | let _ = 'A' .. ('Z');
| ^^^^--^^^^^^
| help: use an inclusive range: `..=`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:37:13
+ --> $DIR/almost_complete_letter_range.rs:36:13
|
LL | let _ = (b'a')..(b'z');
| ^^^^^^--^^^^^^
| help: use an inclusive range: `..=`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:38:13
+ --> $DIR/almost_complete_letter_range.rs:37:13
|
LL | let _ = b'A'..b'Z';
| ^^^^--^^^^
| help: use an inclusive range: `..=`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:43:13
+ --> $DIR/almost_complete_letter_range.rs:42:13
|
LL | let _ = a!()..'z';
| ^^^^--^^^
| help: use an inclusive range: `..=`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:46:9
+ --> $DIR/almost_complete_letter_range.rs:45:9
|
LL | b'a'..b'z' if true => 1,
| ^^^^--^^^^
| help: use an inclusive range: `..=`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:47:9
+ --> $DIR/almost_complete_letter_range.rs:46:9
|
LL | b'A'..b'Z' if true => 2,
| ^^^^--^^^^
| help: use an inclusive range: `..=`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:54:9
+ --> $DIR/almost_complete_letter_range.rs:53:9
|
LL | 'a'..'z' if true => 1,
| ^^^--^^^
| help: use an inclusive range: `..=`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:55:9
+ --> $DIR/almost_complete_letter_range.rs:54:9
|
LL | 'A'..'Z' if true => 2,
| ^^^--^^^
| help: use an inclusive range: `..=`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:23:17
+ --> $DIR/almost_complete_letter_range.rs:22:17
|
LL | let _ = 'a'..'z';
| ^^^--^^^
= note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:68:9
+ --> $DIR/almost_complete_letter_range.rs:67:9
|
LL | 'a'..'z' => 1,
| ^^^--^^^
| help: use an inclusive range: `...`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:75:13
+ --> $DIR/almost_complete_letter_range.rs:74:13
|
LL | let _ = 'a'..'z';
| ^^^--^^^
| help: use an inclusive range: `..=`
error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:77:9
+ --> $DIR/almost_complete_letter_range.rs:76:9
|
LL | 'a'..'z' => 1,
| ^^^--^^^
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::cast_abs_to_unsigned)]
#![allow(clippy::uninlined_format_args, unused)]
let _ = (x as i64 - y as i64).unsigned_abs() as u32;
}
+#[clippy::msrv = "1.50"]
fn msrv_1_50() {
- #![clippy::msrv = "1.50"]
-
let x: i32 = 10;
assert_eq!(10u32, x.abs() as u32);
}
+#[clippy::msrv = "1.51"]
fn msrv_1_51() {
- #![clippy::msrv = "1.51"]
-
let x: i32 = 10;
assert_eq!(10u32, x.unsigned_abs());
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::cast_abs_to_unsigned)]
#![allow(clippy::uninlined_format_args, unused)]
let _ = (x as i64 - y as i64).abs() as u32;
}
+#[clippy::msrv = "1.50"]
fn msrv_1_50() {
- #![clippy::msrv = "1.50"]
-
let x: i32 = 10;
assert_eq!(10u32, x.abs() as u32);
}
+#[clippy::msrv = "1.51"]
fn msrv_1_51() {
- #![clippy::msrv = "1.51"]
-
let x: i32 = 10;
assert_eq!(10u32, x.abs() as u32);
}
error: casting the result of `i32::abs()` to u32
- --> $DIR/cast_abs_to_unsigned.rs:9:18
+ --> $DIR/cast_abs_to_unsigned.rs:8:18
|
LL | let y: u32 = x.abs() as u32;
| ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
= note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings`
error: casting the result of `i32::abs()` to usize
- --> $DIR/cast_abs_to_unsigned.rs:13:20
+ --> $DIR/cast_abs_to_unsigned.rs:12:20
|
LL | let _: usize = a.abs() as usize;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `i32::abs()` to usize
- --> $DIR/cast_abs_to_unsigned.rs:14:20
+ --> $DIR/cast_abs_to_unsigned.rs:13:20
|
LL | let _: usize = a.abs() as _;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `i32::abs()` to usize
- --> $DIR/cast_abs_to_unsigned.rs:15:13
+ --> $DIR/cast_abs_to_unsigned.rs:14:13
|
LL | let _ = a.abs() as usize;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `i64::abs()` to usize
- --> $DIR/cast_abs_to_unsigned.rs:18:13
+ --> $DIR/cast_abs_to_unsigned.rs:17:13
|
LL | let _ = a.abs() as usize;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `i64::abs()` to u8
- --> $DIR/cast_abs_to_unsigned.rs:19:13
+ --> $DIR/cast_abs_to_unsigned.rs:18:13
|
LL | let _ = a.abs() as u8;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `i64::abs()` to u16
- --> $DIR/cast_abs_to_unsigned.rs:20:13
+ --> $DIR/cast_abs_to_unsigned.rs:19:13
|
LL | let _ = a.abs() as u16;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `i64::abs()` to u32
- --> $DIR/cast_abs_to_unsigned.rs:21:13
+ --> $DIR/cast_abs_to_unsigned.rs:20:13
|
LL | let _ = a.abs() as u32;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `i64::abs()` to u64
- --> $DIR/cast_abs_to_unsigned.rs:22:13
+ --> $DIR/cast_abs_to_unsigned.rs:21:13
|
LL | let _ = a.abs() as u64;
| ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `i64::abs()` to u128
- --> $DIR/cast_abs_to_unsigned.rs:23:13
+ --> $DIR/cast_abs_to_unsigned.rs:22:13
|
LL | let _ = a.abs() as u128;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `isize::abs()` to usize
- --> $DIR/cast_abs_to_unsigned.rs:26:13
+ --> $DIR/cast_abs_to_unsigned.rs:25:13
|
LL | let _ = a.abs() as usize;
| ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `isize::abs()` to u8
- --> $DIR/cast_abs_to_unsigned.rs:27:13
+ --> $DIR/cast_abs_to_unsigned.rs:26:13
|
LL | let _ = a.abs() as u8;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `isize::abs()` to u16
- --> $DIR/cast_abs_to_unsigned.rs:28:13
+ --> $DIR/cast_abs_to_unsigned.rs:27:13
|
LL | let _ = a.abs() as u16;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `isize::abs()` to u32
- --> $DIR/cast_abs_to_unsigned.rs:29:13
+ --> $DIR/cast_abs_to_unsigned.rs:28:13
|
LL | let _ = a.abs() as u32;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `isize::abs()` to u64
- --> $DIR/cast_abs_to_unsigned.rs:30:13
+ --> $DIR/cast_abs_to_unsigned.rs:29:13
|
LL | let _ = a.abs() as u64;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `isize::abs()` to u128
- --> $DIR/cast_abs_to_unsigned.rs:31:13
+ --> $DIR/cast_abs_to_unsigned.rs:30:13
|
LL | let _ = a.abs() as u128;
| ^^^^^^^ help: replace with: `a.unsigned_abs()`
error: casting the result of `i64::abs()` to u32
- --> $DIR/cast_abs_to_unsigned.rs:33:13
+ --> $DIR/cast_abs_to_unsigned.rs:32:13
|
LL | let _ = (x as i64 - y as i64).abs() as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()`
error: casting the result of `i32::abs()` to u32
- --> $DIR/cast_abs_to_unsigned.rs:47:23
+ --> $DIR/cast_abs_to_unsigned.rs:44:23
|
LL | assert_eq!(10u32, x.abs() as u32);
| ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(dead_code)]
#![warn(clippy::cast_lossless)]
}
}
+#[clippy::msrv = "1.27"]
fn msrv_1_27() {
- #![clippy::msrv = "1.27"]
-
let _ = true as u8;
}
+#[clippy::msrv = "1.28"]
fn msrv_1_28() {
- #![clippy::msrv = "1.28"]
-
let _ = u8::from(true);
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(dead_code)]
#![warn(clippy::cast_lossless)]
}
}
+#[clippy::msrv = "1.27"]
fn msrv_1_27() {
- #![clippy::msrv = "1.27"]
-
let _ = true as u8;
}
+#[clippy::msrv = "1.28"]
fn msrv_1_28() {
- #![clippy::msrv = "1.28"]
-
let _ = true as u8;
}
error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
- --> $DIR/cast_lossless_bool.rs:9:13
+ --> $DIR/cast_lossless_bool.rs:8:13
|
LL | let _ = true as u8;
| ^^^^^^^^^^ help: try: `u8::from(true)`
= note: `-D clippy::cast-lossless` implied by `-D warnings`
error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
- --> $DIR/cast_lossless_bool.rs:10:13
+ --> $DIR/cast_lossless_bool.rs:9:13
|
LL | let _ = true as u16;
| ^^^^^^^^^^^ help: try: `u16::from(true)`
error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)`
- --> $DIR/cast_lossless_bool.rs:11:13
+ --> $DIR/cast_lossless_bool.rs:10:13
|
LL | let _ = true as u32;
| ^^^^^^^^^^^ help: try: `u32::from(true)`
error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)`
- --> $DIR/cast_lossless_bool.rs:12:13
+ --> $DIR/cast_lossless_bool.rs:11:13
|
LL | let _ = true as u64;
| ^^^^^^^^^^^ help: try: `u64::from(true)`
error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)`
- --> $DIR/cast_lossless_bool.rs:13:13
+ --> $DIR/cast_lossless_bool.rs:12:13
|
LL | let _ = true as u128;
| ^^^^^^^^^^^^ help: try: `u128::from(true)`
error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)`
- --> $DIR/cast_lossless_bool.rs:14:13
+ --> $DIR/cast_lossless_bool.rs:13:13
|
LL | let _ = true as usize;
| ^^^^^^^^^^^^^ help: try: `usize::from(true)`
error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)`
- --> $DIR/cast_lossless_bool.rs:16:13
+ --> $DIR/cast_lossless_bool.rs:15:13
|
LL | let _ = true as i8;
| ^^^^^^^^^^ help: try: `i8::from(true)`
error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)`
- --> $DIR/cast_lossless_bool.rs:17:13
+ --> $DIR/cast_lossless_bool.rs:16:13
|
LL | let _ = true as i16;
| ^^^^^^^^^^^ help: try: `i16::from(true)`
error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)`
- --> $DIR/cast_lossless_bool.rs:18:13
+ --> $DIR/cast_lossless_bool.rs:17:13
|
LL | let _ = true as i32;
| ^^^^^^^^^^^ help: try: `i32::from(true)`
error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)`
- --> $DIR/cast_lossless_bool.rs:19:13
+ --> $DIR/cast_lossless_bool.rs:18:13
|
LL | let _ = true as i64;
| ^^^^^^^^^^^ help: try: `i64::from(true)`
error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)`
- --> $DIR/cast_lossless_bool.rs:20:13
+ --> $DIR/cast_lossless_bool.rs:19:13
|
LL | let _ = true as i128;
| ^^^^^^^^^^^^ help: try: `i128::from(true)`
error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)`
- --> $DIR/cast_lossless_bool.rs:21:13
+ --> $DIR/cast_lossless_bool.rs:20:13
|
LL | let _ = true as isize;
| ^^^^^^^^^^^^^ help: try: `isize::from(true)`
error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
- --> $DIR/cast_lossless_bool.rs:24:13
+ --> $DIR/cast_lossless_bool.rs:23:13
|
LL | let _ = (true | false) as u16;
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)`
error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
- --> $DIR/cast_lossless_bool.rs:54:13
+ --> $DIR/cast_lossless_bool.rs:51:13
|
LL | let _ = true as u8;
| ^^^^^^^^^^ help: try: `u8::from(true)`
// run-rustfix
-#![feature(stmt_expr_attributes, custom_inner_attributes)]
+#![feature(stmt_expr_attributes)]
#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
#![warn(clippy::deprecated_cfg_attr)]
pub fn f() {}
}
+#[clippy::msrv = "1.29"]
fn msrv_1_29() {
- #![clippy::msrv = "1.29"]
-
#[cfg_attr(rustfmt, rustfmt::skip)]
1+29;
}
+#[clippy::msrv = "1.30"]
fn msrv_1_30() {
- #![clippy::msrv = "1.30"]
-
#[rustfmt::skip]
1+30;
}
// run-rustfix
-#![feature(stmt_expr_attributes, custom_inner_attributes)]
+#![feature(stmt_expr_attributes)]
#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
#![warn(clippy::deprecated_cfg_attr)]
pub fn f() {}
}
+#[clippy::msrv = "1.29"]
fn msrv_1_29() {
- #![clippy::msrv = "1.29"]
-
#[cfg_attr(rustfmt, rustfmt::skip)]
1+29;
}
+#[clippy::msrv = "1.30"]
fn msrv_1_30() {
- #![clippy::msrv = "1.30"]
-
#[cfg_attr(rustfmt, rustfmt::skip)]
1+30;
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
- --> $DIR/cfg_attr_rustfmt.rs:43:5
+ --> $DIR/cfg_attr_rustfmt.rs:41:5
|
LL | #[cfg_attr(rustfmt, rustfmt::skip)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(
clippy::cast_lossless,
unused,
i <= i32::MAX as u32
}
+#[clippy::msrv = "1.33"]
fn msrv_1_33() {
- #![clippy::msrv = "1.33"]
-
let value: i64 = 33;
let _ = value <= (u32::MAX as i64) && value >= 0;
}
+#[clippy::msrv = "1.34"]
fn msrv_1_34() {
- #![clippy::msrv = "1.34"]
-
let value: i64 = 34;
let _ = u32::try_from(value).is_ok();
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(
clippy::cast_lossless,
unused,
i <= i32::MAX as u32
}
+#[clippy::msrv = "1.33"]
fn msrv_1_33() {
- #![clippy::msrv = "1.33"]
-
let value: i64 = 33;
let _ = value <= (u32::MAX as i64) && value >= 0;
}
+#[clippy::msrv = "1.34"]
fn msrv_1_34() {
- #![clippy::msrv = "1.34"]
-
let value: i64 = 34;
let _ = value <= (u32::MAX as i64) && value >= 0;
}
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:17:13
+ --> $DIR/checked_conversions.rs:16:13
|
LL | let _ = value <= (u32::max_value() as i64) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
= note: `-D clippy::checked-conversions` implied by `-D warnings`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:18:13
+ --> $DIR/checked_conversions.rs:17:13
|
LL | let _ = value <= (u32::MAX as i64) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:22:13
+ --> $DIR/checked_conversions.rs:21:13
|
LL | let _ = value <= i64::from(u16::max_value()) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:23:13
+ --> $DIR/checked_conversions.rs:22:13
|
LL | let _ = value <= i64::from(u16::MAX) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:27:13
+ --> $DIR/checked_conversions.rs:26:13
|
LL | let _ = value <= (u8::max_value() as isize) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:28:13
+ --> $DIR/checked_conversions.rs:27:13
|
LL | let _ = value <= (u8::MAX as isize) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:34:13
+ --> $DIR/checked_conversions.rs:33:13
|
LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:35:13
+ --> $DIR/checked_conversions.rs:34:13
|
LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:39:13
+ --> $DIR/checked_conversions.rs:38:13
|
LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:40:13
+ --> $DIR/checked_conversions.rs:39:13
|
LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:46:13
+ --> $DIR/checked_conversions.rs:45:13
|
LL | let _ = value <= i32::max_value() as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:47:13
+ --> $DIR/checked_conversions.rs:46:13
|
LL | let _ = value <= i32::MAX as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:51:13
+ --> $DIR/checked_conversions.rs:50:13
|
LL | let _ = value <= isize::max_value() as usize && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:52:13
+ --> $DIR/checked_conversions.rs:51:13
|
LL | let _ = value <= isize::MAX as usize && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:56:13
+ --> $DIR/checked_conversions.rs:55:13
|
LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:57:13
+ --> $DIR/checked_conversions.rs:56:13
|
LL | let _ = value <= u16::MAX as u32 && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: checked cast can be simplified
- --> $DIR/checked_conversions.rs:92:13
+ --> $DIR/checked_conversions.rs:89:13
|
LL | let _ = value <= (u32::MAX as i64) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::cloned_instead_of_copied)]
#![allow(unused)]
let _ = Some(&String::new()).cloned();
}
+#[clippy::msrv = "1.34"]
fn msrv_1_34() {
- #![clippy::msrv = "1.34"]
-
let _ = [1].iter().cloned();
let _ = Some(&1).cloned();
}
+#[clippy::msrv = "1.35"]
fn msrv_1_35() {
- #![clippy::msrv = "1.35"]
-
let _ = [1].iter().cloned();
let _ = Some(&1).copied(); // Option::copied needs 1.35
}
+#[clippy::msrv = "1.36"]
fn msrv_1_36() {
- #![clippy::msrv = "1.36"]
-
let _ = [1].iter().copied(); // Iterator::copied needs 1.36
let _ = Some(&1).copied();
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::cloned_instead_of_copied)]
#![allow(unused)]
let _ = Some(&String::new()).cloned();
}
+#[clippy::msrv = "1.34"]
fn msrv_1_34() {
- #![clippy::msrv = "1.34"]
-
let _ = [1].iter().cloned();
let _ = Some(&1).cloned();
}
+#[clippy::msrv = "1.35"]
fn msrv_1_35() {
- #![clippy::msrv = "1.35"]
-
let _ = [1].iter().cloned();
let _ = Some(&1).cloned(); // Option::copied needs 1.35
}
+#[clippy::msrv = "1.36"]
fn msrv_1_36() {
- #![clippy::msrv = "1.36"]
-
let _ = [1].iter().cloned(); // Iterator::copied needs 1.36
let _ = Some(&1).cloned();
}
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:9:24
+ --> $DIR/cloned_instead_of_copied.rs:8:24
|
LL | let _ = [1].iter().cloned();
| ^^^^^^ help: try: `copied`
= note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:10:31
+ --> $DIR/cloned_instead_of_copied.rs:9:31
|
LL | let _ = vec!["hi"].iter().cloned();
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:11:22
+ --> $DIR/cloned_instead_of_copied.rs:10:22
|
LL | let _ = Some(&1).cloned();
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:12:34
+ --> $DIR/cloned_instead_of_copied.rs:11:34
|
LL | let _ = Box::new([1].iter()).cloned();
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:13:32
+ --> $DIR/cloned_instead_of_copied.rs:12:32
|
LL | let _ = Box::new(Some(&1)).cloned();
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:31:22
+ --> $DIR/cloned_instead_of_copied.rs:28:22
|
LL | let _ = Some(&1).cloned(); // Option::copied needs 1.35
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:37:24
+ --> $DIR/cloned_instead_of_copied.rs:33:24
|
LL | let _ = [1].iter().cloned(); // Iterator::copied needs 1.36
| ^^^^^^ help: try: `copied`
error: used `cloned` where `copied` could be used instead
- --> $DIR/cloned_instead_of_copied.rs:38:22
+ --> $DIR/cloned_instead_of_copied.rs:34:22
|
LL | let _ = Some(&1).cloned();
| ^^^^^^ help: try: `copied`
+++ /dev/null
-// aux-build:doc_unsafe_macros.rs
-
-#![allow(clippy::let_unit_value)]
-
-#[macro_use]
-extern crate doc_unsafe_macros;
-
-/// This is has no safety section, and does not need one either
-pub fn destroy_the_planet() {
- unimplemented!();
-}
-
-/// This one does not need a `Safety` section
-///
-/// # Safety
-///
-/// This function shouldn't be called unless the horsemen are ready
-pub fn apocalypse(universe: &mut ()) {
- unimplemented!();
-}
-
-/// This is a private function, skip to match behavior with `missing_safety_doc`.
-///
-/// # Safety
-///
-/// Boo!
-fn you_dont_see_me() {
- unimplemented!();
-}
-
-mod private_mod {
- /// This is public but unexported function, skip to match behavior with `missing_safety_doc`.
- ///
- /// # Safety
- ///
- /// Very safe!
- pub fn only_crate_wide_accessible() {
- unimplemented!();
- }
-
- /// # Safety
- ///
- /// Unnecessary safety!
- pub fn republished() {
- unimplemented!();
- }
-}
-
-pub use private_mod::republished;
-
-pub trait SafeTraitSafeMethods {
- fn woefully_underdocumented(self);
-
- /// # Safety
- ///
- /// Unnecessary!
- fn documented(self);
-}
-
-pub trait SafeTrait {
- fn method();
-}
-
-/// # Safety
-///
-/// Unnecessary!
-pub trait DocumentedSafeTrait {
- fn method2();
-}
-
-pub struct Struct;
-
-impl SafeTraitSafeMethods for Struct {
- fn woefully_underdocumented(self) {
- // all is well
- }
-
- fn documented(self) {
- // all is still well
- }
-}
-
-impl SafeTrait for Struct {
- fn method() {}
-}
-
-impl DocumentedSafeTrait for Struct {
- fn method2() {}
-}
-
-impl Struct {
- /// # Safety
- ///
- /// Unnecessary!
- pub fn documented() -> Self {
- unimplemented!();
- }
-
- pub fn undocumented(&self) {
- unimplemented!();
- }
-
- /// Private, fine again to stay consistent with `missing_safety_doc`.
- ///
- /// # Safety
- ///
- /// Unnecessary!
- fn private(&self) {
- unimplemented!();
- }
-}
-
-macro_rules! very_safe {
- () => {
- pub fn whee() {
- unimplemented!()
- }
-
- /// # Safety
- ///
- /// Driving is very safe already!
- pub fn drive() {
- whee()
- }
- };
-}
-
-very_safe!();
-
-// we don't lint code from external macros
-undocd_safe!();
-
-fn main() {}
-
-// do not lint if any parent has `#[doc(hidden)]` attribute
-// see #7347
-#[doc(hidden)]
-pub mod __macro {
- pub struct T;
- impl T {
- pub unsafe fn f() {}
- }
-}
-
-/// # Implementation safety
-pub trait DocumentedSafeTraitWithImplementationHeader {
- fn method();
-}
+++ /dev/null
-error: safe function's docs have unnecessary `# Safety` section
- --> $DIR/doc_unnecessary_unsafe.rs:18:1
- |
-LL | pub fn apocalypse(universe: &mut ()) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::unnecessary-safety-doc` implied by `-D warnings`
-
-error: safe function's docs have unnecessary `# Safety` section
- --> $DIR/doc_unnecessary_unsafe.rs:44:5
- |
-LL | pub fn republished() {
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: safe function's docs have unnecessary `# Safety` section
- --> $DIR/doc_unnecessary_unsafe.rs:57:5
- |
-LL | fn documented(self);
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: docs for safe trait have unnecessary `# Safety` section
- --> $DIR/doc_unnecessary_unsafe.rs:67:1
- |
-LL | pub trait DocumentedSafeTrait {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: safe function's docs have unnecessary `# Safety` section
- --> $DIR/doc_unnecessary_unsafe.rs:95:5
- |
-LL | pub fn documented() -> Self {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: safe function's docs have unnecessary `# Safety` section
- --> $DIR/doc_unnecessary_unsafe.rs:122:9
- |
-LL | pub fn drive() {
- | ^^^^^^^^^^^^^^
-...
-LL | very_safe!();
- | ------------ in this macro invocation
- |
- = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: docs for safe trait have unnecessary `# Safety` section
- --> $DIR/doc_unnecessary_unsafe.rs:146:1
- |
-LL | pub trait DocumentedSafeTraitWithImplementationHeader {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 7 previous errors
-
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(unused)]
struct MyTypeNonDebug;
test_non_debug.err().expect("Testing non debug type");
}
+#[clippy::msrv = "1.16"]
fn msrv_1_16() {
- #![clippy::msrv = "1.16"]
-
let x: Result<u32, &str> = Ok(16);
x.err().expect("16");
}
+#[clippy::msrv = "1.17"]
fn msrv_1_17() {
- #![clippy::msrv = "1.17"]
-
let x: Result<u32, &str> = Ok(17);
x.expect_err("17");
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(unused)]
struct MyTypeNonDebug;
test_non_debug.err().expect("Testing non debug type");
}
+#[clippy::msrv = "1.16"]
fn msrv_1_16() {
- #![clippy::msrv = "1.16"]
-
let x: Result<u32, &str> = Ok(16);
x.err().expect("16");
}
+#[clippy::msrv = "1.17"]
fn msrv_1_17() {
- #![clippy::msrv = "1.17"]
-
let x: Result<u32, &str> = Ok(17);
x.err().expect("17");
}
error: called `.err().expect()` on a `Result` value
- --> $DIR/err_expect.rs:13:16
+ --> $DIR/err_expect.rs:12:16
|
LL | test_debug.err().expect("Testing debug type");
| ^^^^^^^^^^^^ help: try: `expect_err`
= note: `-D clippy::err-expect` implied by `-D warnings`
error: called `.err().expect()` on a `Result` value
- --> $DIR/err_expect.rs:30:7
+ --> $DIR/err_expect.rs:27:7
|
LL | x.err().expect("17");
| ^^^^^^^^^^^^ help: try: `expect_err`
move || takes_fn_mut(&mut f_used_once)
}
+
+impl dyn TestTrait + '_ {
+ fn method_on_dyn(&self) -> bool {
+ false
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7746
+fn angle_brackets_and_substs() {
+ let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]);
+ array_opt.map(<[u8; 3]>::as_slice);
+
+ let slice_opt: Option<&[u8]> = Some(b"slice");
+ slice_opt.map(<[u8]>::len);
+
+ let ptr_opt: Option<*const usize> = Some(&487);
+ ptr_opt.map(<*const usize>::is_null);
+
+ let test_struct = TestStruct { some_ref: &487 };
+ let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct);
+ dyn_opt.map(<dyn TestTrait>::method_on_dyn);
+}
move || takes_fn_mut(|| f_used_once())
}
+
+impl dyn TestTrait + '_ {
+ fn method_on_dyn(&self) -> bool {
+ false
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7746
+fn angle_brackets_and_substs() {
+ let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]);
+ array_opt.map(|a| a.as_slice());
+
+ let slice_opt: Option<&[u8]> = Some(b"slice");
+ slice_opt.map(|s| s.len());
+
+ let ptr_opt: Option<*const usize> = Some(&487);
+ ptr_opt.map(|p| p.is_null());
+
+ let test_struct = TestStruct { some_ref: &487 };
+ let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct);
+ dyn_opt.map(|d| d.method_on_dyn());
+}
LL | move || takes_fn_mut(|| f_used_once())
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once`
-error: aborting due to 22 previous errors
+error: redundant closure
+ --> $DIR/eta.rs:329:19
+ |
+LL | array_opt.map(|a| a.as_slice());
+ | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice`
+
+error: redundant closure
+ --> $DIR/eta.rs:332:19
+ |
+LL | slice_opt.map(|s| s.len());
+ | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len`
+
+error: redundant closure
+ --> $DIR/eta.rs:335:17
+ |
+LL | ptr_opt.map(|p| p.is_null());
+ | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null`
+
+error: redundant closure
+ --> $DIR/eta.rs:339:17
+ |
+LL | dyn_opt.map(|d| d.method_on_dyn());
+ | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn`
+
+error: aborting due to 26 previous errors
unimplemented!()
}
let _: String = takes_assoc(&*String::new());
+
+ // Issue #9901
+ fn takes_ref(_: &i32) {}
+ takes_ref(*Box::new(&0i32));
}
unimplemented!()
}
let _: String = takes_assoc(&*String::new());
+
+ // Issue #9901
+ fn takes_ref(_: &i32) {}
+ takes_ref(*Box::new(&0i32));
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::all, clippy::pedantic)]
#![allow(unused)]
assert_eq!(element, Some(1));
}
+#[clippy::msrv = "1.29"]
fn msrv_1_29() {
- #![clippy::msrv = "1.29"]
-
let a = ["1", "lol", "3", "NaN", "5"];
let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
}
+#[clippy::msrv = "1.30"]
fn msrv_1_30() {
- #![clippy::msrv = "1.30"]
-
let a = ["1", "lol", "3", "NaN", "5"];
let _: Option<i32> = a.iter().find_map(|s| s.parse().ok());
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::all, clippy::pedantic)]
#![allow(unused)]
assert_eq!(element, Some(1));
}
+#[clippy::msrv = "1.29"]
fn msrv_1_29() {
- #![clippy::msrv = "1.29"]
-
let a = ["1", "lol", "3", "NaN", "5"];
let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
}
+#[clippy::msrv = "1.30"]
fn msrv_1_30() {
- #![clippy::msrv = "1.30"]
-
let a = ["1", "lol", "3", "NaN", "5"];
let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
}
error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
- --> $DIR/filter_map_next_fixable.rs:10:32
+ --> $DIR/filter_map_next_fixable.rs:9:32
|
LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
= note: `-D clippy::filter-map-next` implied by `-D warnings`
error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead
- --> $DIR/filter_map_next_fixable.rs:25:26
+ --> $DIR/filter_map_next_fixable.rs:22:26
|
LL | let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::from_over_into)]
#![allow(unused)]
}
}
+#[clippy::msrv = "1.40"]
fn msrv_1_40() {
- #![clippy::msrv = "1.40"]
-
struct FromOverInto<T>(Vec<T>);
impl<T> Into<FromOverInto<T>> for Vec<T> {
}
}
+#[clippy::msrv = "1.41"]
fn msrv_1_41() {
- #![clippy::msrv = "1.41"]
-
struct FromOverInto<T>(Vec<T>);
impl<T> From<Vec<T>> for FromOverInto<T> {
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::from_over_into)]
#![allow(unused)]
}
}
+#[clippy::msrv = "1.40"]
fn msrv_1_40() {
- #![clippy::msrv = "1.40"]
-
struct FromOverInto<T>(Vec<T>);
impl<T> Into<FromOverInto<T>> for Vec<T> {
}
}
+#[clippy::msrv = "1.41"]
fn msrv_1_41() {
- #![clippy::msrv = "1.41"]
-
struct FromOverInto<T>(Vec<T>);
impl<T> Into<FromOverInto<T>> for Vec<T> {
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:10:1
+ --> $DIR/from_over_into.rs:9:1
|
LL | impl Into<StringWrapper> for String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:18:1
+ --> $DIR/from_over_into.rs:17:1
|
LL | impl Into<SelfType> for String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:33:1
+ --> $DIR/from_over_into.rs:32:1
|
LL | impl Into<SelfKeywords> for X {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:45:1
+ --> $DIR/from_over_into.rs:44:1
|
LL | impl core::convert::Into<bool> for crate::ExplicitPaths {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:80:5
+ --> $DIR/from_over_into.rs:77:5
|
LL | impl<T> Into<FromOverInto<T>> for Vec<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#![warn(clippy::if_then_some_else_none)]
-#![feature(custom_inner_attributes)]
fn main() {
// Should issue an error.
let _ = if foo() { into_some("foo") } else { None };
}
+#[clippy::msrv = "1.49"]
fn _msrv_1_49() {
- #![clippy::msrv = "1.49"]
// `bool::then` was stabilized in 1.50. Do not lint this
let _ = if foo() {
println!("true!");
};
}
+#[clippy::msrv = "1.50"]
fn _msrv_1_50() {
- #![clippy::msrv = "1.50"]
let _ = if foo() {
println!("true!");
Some(150)
error: this could be simplified with `bool::then`
- --> $DIR/if_then_some_else_none.rs:6:13
+ --> $DIR/if_then_some_else_none.rs:5:13
|
LL | let _ = if foo() {
| _____________^
= note: `-D clippy::if-then-some-else-none` implied by `-D warnings`
error: this could be simplified with `bool::then`
- --> $DIR/if_then_some_else_none.rs:14:13
+ --> $DIR/if_then_some_else_none.rs:13:13
|
LL | let _ = if matches!(true, true) {
| _____________^
= help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })`
error: this could be simplified with `bool::then_some`
- --> $DIR/if_then_some_else_none.rs:23:28
+ --> $DIR/if_then_some_else_none.rs:22:28
|
LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider using `bool::then_some` like: `(o < 32).then_some(o)`
error: this could be simplified with `bool::then_some`
- --> $DIR/if_then_some_else_none.rs:27:13
+ --> $DIR/if_then_some_else_none.rs:26:13
|
LL | let _ = if !x { Some(0) } else { None };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider using `bool::then_some` like: `(!x).then_some(0)`
error: this could be simplified with `bool::then`
- --> $DIR/if_then_some_else_none.rs:82:13
+ --> $DIR/if_then_some_else_none.rs:81:13
|
LL | let _ = if foo() {
| _____________^
error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- --> $DIR/macro_use_imports.rs:23:5
+ --> $DIR/macro_use_imports.rs:25:5
|
LL | #[macro_use]
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
|
= note: `-D clippy::macro-use-imports` implied by `-D warnings`
| ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;`
error: `macro_use` attributes are no longer needed in the Rust 2018 edition
- --> $DIR/macro_use_imports.rs:25:5
+ --> $DIR/macro_use_imports.rs:23:5
|
LL | #[macro_use]
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
error: `macro_use` attributes are no longer needed in the Rust 2018 edition
--> $DIR/macro_use_imports.rs:19:5
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_clamp)]
#![allow(
unused,
input * 3
}
+#[clippy::msrv = "1.49"]
fn msrv_1_49() {
- #![clippy::msrv = "1.49"]
-
let (input, min, max) = (0, -1, 2);
let _ = if input < min {
min
};
}
+#[clippy::msrv = "1.50"]
fn msrv_1_50() {
- #![clippy::msrv = "1.50"]
-
let (input, min, max) = (0, -1, 2);
let _ = if input < min {
min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:77:5
+ --> $DIR/manual_clamp.rs:76:5
|
LL | / if x9 < min {
LL | | x9 = min;
= note: `-D clippy::manual-clamp` implied by `-D warnings`
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:92:5
+ --> $DIR/manual_clamp.rs:91:5
|
LL | / if x11 > max {
LL | | x11 = max;
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:100:5
+ --> $DIR/manual_clamp.rs:99:5
|
LL | / if min > x12 {
LL | | x12 = min;
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:108:5
+ --> $DIR/manual_clamp.rs:107:5
|
LL | / if max < x13 {
LL | | x13 = max;
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:162:5
+ --> $DIR/manual_clamp.rs:161:5
|
LL | / if max < x33 {
LL | | x33 = max;
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:22:14
+ --> $DIR/manual_clamp.rs:21:14
|
LL | let x0 = if max < input {
| ______________^
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:30:14
+ --> $DIR/manual_clamp.rs:29:14
|
LL | let x1 = if input > max {
| ______________^
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:38:14
+ --> $DIR/manual_clamp.rs:37:14
|
LL | let x2 = if input < min {
| ______________^
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:46:14
+ --> $DIR/manual_clamp.rs:45:14
|
LL | let x3 = if min > input {
| ______________^
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:54:14
+ --> $DIR/manual_clamp.rs:53:14
|
LL | let x4 = input.max(min).min(max);
| ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)`
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:56:14
+ --> $DIR/manual_clamp.rs:55:14
|
LL | let x5 = input.min(max).max(min);
| ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)`
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:58:14
+ --> $DIR/manual_clamp.rs:57:14
|
LL | let x6 = match input {
| ______________^
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:64:14
+ --> $DIR/manual_clamp.rs:63:14
|
LL | let x7 = match input {
| ______________^
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:70:14
+ --> $DIR/manual_clamp.rs:69:14
|
LL | let x8 = match input {
| ______________^
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:84:15
+ --> $DIR/manual_clamp.rs:83:15
|
LL | let x10 = match input {
| _______________^
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:115:15
+ --> $DIR/manual_clamp.rs:114:15
|
LL | let x14 = if input > CONST_MAX {
| _______________^
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:124:19
+ --> $DIR/manual_clamp.rs:123:19
|
LL | let x15 = if input > max {
| ___________________^
= note: clamp returns NaN if the input is NaN
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:135:19
+ --> $DIR/manual_clamp.rs:134:19
|
LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:136:19
+ --> $DIR/manual_clamp.rs:135:19
|
LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:137:19
+ --> $DIR/manual_clamp.rs:136:19
|
LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:138:19
+ --> $DIR/manual_clamp.rs:137:19
|
LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:139:19
+ --> $DIR/manual_clamp.rs:138:19
|
LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:140:19
+ --> $DIR/manual_clamp.rs:139:19
|
LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:141:19
+ --> $DIR/manual_clamp.rs:140:19
|
LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:142:19
+ --> $DIR/manual_clamp.rs:141:19
|
LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:144:19
+ --> $DIR/manual_clamp.rs:143:19
|
LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
= note: clamp returns NaN if the input is NaN
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:145:19
+ --> $DIR/manual_clamp.rs:144:19
|
LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
= note: clamp returns NaN if the input is NaN
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:146:19
+ --> $DIR/manual_clamp.rs:145:19
|
LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
= note: clamp returns NaN if the input is NaN
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:147:19
+ --> $DIR/manual_clamp.rs:146:19
|
LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
= note: clamp returns NaN if the input is NaN
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:148:19
+ --> $DIR/manual_clamp.rs:147:19
|
LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
= note: clamp returns NaN if the input is NaN
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:149:19
+ --> $DIR/manual_clamp.rs:148:19
|
LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
= note: clamp returns NaN if the input is NaN
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:150:19
+ --> $DIR/manual_clamp.rs:149:19
|
LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
= note: clamp returns NaN if the input is NaN
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:151:19
+ --> $DIR/manual_clamp.rs:150:19
|
LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)`
= note: clamp returns NaN if the input is NaN
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:154:5
+ --> $DIR/manual_clamp.rs:153:5
|
LL | / if x32 < min {
LL | | x32 = min;
= note: clamp will panic if max < min
error: clamp-like pattern without using clamp function
- --> $DIR/manual_clamp.rs:324:13
+ --> $DIR/manual_clamp.rs:321:13
|
LL | let _ = if input < min {
| _____________^
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(unused, dead_code)]
#![warn(clippy::manual_is_ascii_check)]
assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
}
+#[clippy::msrv = "1.23"]
fn msrv_1_23() {
- #![clippy::msrv = "1.23"]
-
assert!(matches!(b'1', b'0'..=b'9'));
assert!(matches!('X', 'A'..='Z'));
assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
}
+#[clippy::msrv = "1.24"]
fn msrv_1_24() {
- #![clippy::msrv = "1.24"]
-
assert!(b'1'.is_ascii_digit());
assert!('X'.is_ascii_uppercase());
assert!('x'.is_ascii_alphabetic());
}
+#[clippy::msrv = "1.46"]
fn msrv_1_46() {
- #![clippy::msrv = "1.46"]
const FOO: bool = matches!('x', '0'..='9');
}
+#[clippy::msrv = "1.47"]
fn msrv_1_47() {
- #![clippy::msrv = "1.47"]
const FOO: bool = 'x'.is_ascii_digit();
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(unused, dead_code)]
#![warn(clippy::manual_is_ascii_check)]
assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
}
+#[clippy::msrv = "1.23"]
fn msrv_1_23() {
- #![clippy::msrv = "1.23"]
-
assert!(matches!(b'1', b'0'..=b'9'));
assert!(matches!('X', 'A'..='Z'));
assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
}
+#[clippy::msrv = "1.24"]
fn msrv_1_24() {
- #![clippy::msrv = "1.24"]
-
assert!(matches!(b'1', b'0'..=b'9'));
assert!(matches!('X', 'A'..='Z'));
assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
}
+#[clippy::msrv = "1.46"]
fn msrv_1_46() {
- #![clippy::msrv = "1.46"]
const FOO: bool = matches!('x', '0'..='9');
}
+#[clippy::msrv = "1.47"]
fn msrv_1_47() {
- #![clippy::msrv = "1.47"]
const FOO: bool = matches!('x', '0'..='9');
}
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:8:13
+ --> $DIR/manual_is_ascii_check.rs:7:13
|
LL | assert!(matches!('x', 'a'..='z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_lowercase()`
= note: `-D clippy::manual-is-ascii-check` implied by `-D warnings`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:9:13
+ --> $DIR/manual_is_ascii_check.rs:8:13
|
LL | assert!(matches!('X', 'A'..='Z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:10:13
+ --> $DIR/manual_is_ascii_check.rs:9:13
|
LL | assert!(matches!(b'x', b'a'..=b'z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'x'.is_ascii_lowercase()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:11:13
+ --> $DIR/manual_is_ascii_check.rs:10:13
|
LL | assert!(matches!(b'X', b'A'..=b'Z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'X'.is_ascii_uppercase()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:14:13
+ --> $DIR/manual_is_ascii_check.rs:13:13
|
LL | assert!(matches!(num, '0'..='9'));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.is_ascii_digit()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:15:13
+ --> $DIR/manual_is_ascii_check.rs:14:13
|
LL | assert!(matches!(b'1', b'0'..=b'9'));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:16:13
+ --> $DIR/manual_is_ascii_check.rs:15:13
|
LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:32:13
+ --> $DIR/manual_is_ascii_check.rs:29:13
|
LL | assert!(matches!(b'1', b'0'..=b'9'));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:33:13
+ --> $DIR/manual_is_ascii_check.rs:30:13
|
LL | assert!(matches!('X', 'A'..='Z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:34:13
+ --> $DIR/manual_is_ascii_check.rs:31:13
|
LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:44:23
+ --> $DIR/manual_is_ascii_check.rs:41:23
|
LL | const FOO: bool = matches!('x', '0'..='9');
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()`
// If a type annotation is present, don't lint as
// expressing the type might be too hard
let v: () = if let Some(v_some) = g() { v_some } else { panic!() };
+
+ // Issue 9940
+ // Suggestion should not expand macros
+ macro_rules! macro_call {
+ () => {
+ return ()
+ };
+ }
+
+ let ff = Some(1);
+ let _ = match ff {
+ Some(value) => value,
+ _ => macro_call!(),
+ };
}
|
= note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 17 previous errors
+error: this could be rewritten as `let...else`
+ --> $DIR/manual_let_else.rs:247:5
+ |
+LL | / let _ = match ff {
+LL | | Some(value) => value,
+LL | | _ => macro_call!(),
+LL | | };
+ | |______^ help: consider writing: `let Some(value) = ff else { macro_call!() };`
+
+error: aborting due to 18 previous errors
// run-rustfix
// aux-build:macro_rules.rs
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_rem_euclid)]
#[macro_use]
num.rem_euclid(4)
}
+#[clippy::msrv = "1.37"]
pub fn msrv_1_37() {
- #![clippy::msrv = "1.37"]
-
let x: i32 = 10;
let _: i32 = ((x % 4) + 4) % 4;
}
+#[clippy::msrv = "1.38"]
pub fn msrv_1_38() {
- #![clippy::msrv = "1.38"]
-
let x: i32 = 10;
let _: i32 = x.rem_euclid(4);
}
// For const fns:
+#[clippy::msrv = "1.51"]
pub const fn msrv_1_51() {
- #![clippy::msrv = "1.51"]
-
let x: i32 = 10;
let _: i32 = ((x % 4) + 4) % 4;
}
+#[clippy::msrv = "1.52"]
pub const fn msrv_1_52() {
- #![clippy::msrv = "1.52"]
-
let x: i32 = 10;
let _: i32 = x.rem_euclid(4);
}
// run-rustfix
// aux-build:macro_rules.rs
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_rem_euclid)]
#[macro_use]
((num % 4) + 4) % 4
}
+#[clippy::msrv = "1.37"]
pub fn msrv_1_37() {
- #![clippy::msrv = "1.37"]
-
let x: i32 = 10;
let _: i32 = ((x % 4) + 4) % 4;
}
+#[clippy::msrv = "1.38"]
pub fn msrv_1_38() {
- #![clippy::msrv = "1.38"]
-
let x: i32 = 10;
let _: i32 = ((x % 4) + 4) % 4;
}
// For const fns:
+#[clippy::msrv = "1.51"]
pub const fn msrv_1_51() {
- #![clippy::msrv = "1.51"]
-
let x: i32 = 10;
let _: i32 = ((x % 4) + 4) % 4;
}
+#[clippy::msrv = "1.52"]
pub const fn msrv_1_52() {
- #![clippy::msrv = "1.52"]
-
let x: i32 = 10;
let _: i32 = ((x % 4) + 4) % 4;
}
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:20:18
+ --> $DIR/manual_rem_euclid.rs:19:18
|
LL | let _: i32 = ((value % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
= note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:21:18
+ --> $DIR/manual_rem_euclid.rs:20:18
|
LL | let _: i32 = (4 + (value % 4)) % 4;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:22:18
+ --> $DIR/manual_rem_euclid.rs:21:18
|
LL | let _: i32 = (value % 4 + 4) % 4;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:23:18
+ --> $DIR/manual_rem_euclid.rs:22:18
|
LL | let _: i32 = (4 + value % 4) % 4;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:24:22
+ --> $DIR/manual_rem_euclid.rs:23:22
|
LL | let _: i32 = 1 + (4 + value % 4) % 4;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:13:22
+ --> $DIR/manual_rem_euclid.rs:12:22
|
LL | let _: i32 = ((value % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
= note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info)
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:50:5
+ --> $DIR/manual_rem_euclid.rs:49:5
|
LL | ((num % 4) + 4) % 4
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:55:5
+ --> $DIR/manual_rem_euclid.rs:54:5
|
LL | ((num % 4) + 4) % 4
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:69:18
+ --> $DIR/manual_rem_euclid.rs:66:18
|
LL | let _: i32 = ((x % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:84:18
+ --> $DIR/manual_rem_euclid.rs:79:18
|
LL | let _: i32 = ((x % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_retain)]
#![allow(unused)]
use std::collections::BTreeMap;
bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
}
+#[clippy::msrv = "1.52"]
fn _msrv_153() {
- #![clippy::msrv = "1.52"]
let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
}
+#[clippy::msrv = "1.25"]
fn _msrv_126() {
- #![clippy::msrv = "1.25"]
let mut s = String::from("foobar");
s = s.chars().filter(|&c| c != 'o').to_owned().collect();
}
+#[clippy::msrv = "1.17"]
fn _msrv_118() {
- #![clippy::msrv = "1.17"]
let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_retain)]
#![allow(unused)]
use std::collections::BTreeMap;
bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
}
+#[clippy::msrv = "1.52"]
fn _msrv_153() {
- #![clippy::msrv = "1.52"]
let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
}
+#[clippy::msrv = "1.25"]
fn _msrv_126() {
- #![clippy::msrv = "1.25"]
let mut s = String::from("foobar");
s = s.chars().filter(|&c| c != 'o').to_owned().collect();
}
+#[clippy::msrv = "1.17"]
fn _msrv_118() {
- #![clippy::msrv = "1.17"]
let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:52:5
+ --> $DIR/manual_retain.rs:51:5
|
LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)`
= note: `-D clippy::manual-retain` implied by `-D warnings`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:53:5
+ --> $DIR/manual_retain.rs:52:5
|
LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:54:5
+ --> $DIR/manual_retain.rs:53:5
|
LL | / btree_map = btree_map
LL | | .into_iter()
| |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:76:5
+ --> $DIR/manual_retain.rs:75:5
|
LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:77:5
+ --> $DIR/manual_retain.rs:76:5
|
LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:78:5
+ --> $DIR/manual_retain.rs:77:5
|
LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:108:5
+ --> $DIR/manual_retain.rs:107:5
|
LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:109:5
+ --> $DIR/manual_retain.rs:108:5
|
LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:110:5
+ --> $DIR/manual_retain.rs:109:5
|
LL | / hash_map = hash_map
LL | | .into_iter()
| |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:131:5
+ --> $DIR/manual_retain.rs:130:5
|
LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:132:5
+ --> $DIR/manual_retain.rs:131:5
|
LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:133:5
+ --> $DIR/manual_retain.rs:132:5
|
LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:162:5
+ --> $DIR/manual_retain.rs:161:5
|
LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:174:5
+ --> $DIR/manual_retain.rs:173:5
|
LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:175:5
+ --> $DIR/manual_retain.rs:174:5
|
LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:176:5
+ --> $DIR/manual_retain.rs:175:5
|
LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:198:5
+ --> $DIR/manual_retain.rs:197:5
|
LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:199:5
+ --> $DIR/manual_retain.rs:198:5
|
LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
error: this expression can be written more simply using `.retain()`
- --> $DIR/manual_retain.rs:200:5
+ --> $DIR/manual_retain.rs:199:5
|
LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_split_once)]
#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
None
}
+#[clippy::msrv = "1.51"]
fn _msrv_1_51() {
- #![clippy::msrv = "1.51"]
// `str::split_once` was stabilized in 1.52. Do not lint this
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
let b = iter.next().unwrap();
}
+#[clippy::msrv = "1.52"]
fn _msrv_1_52() {
- #![clippy::msrv = "1.52"]
let _ = "key=value".split_once('=').unwrap().1;
let (a, b) = "a.b.c".split_once('.').unwrap();
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_split_once)]
#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
None
}
+#[clippy::msrv = "1.51"]
fn _msrv_1_51() {
- #![clippy::msrv = "1.51"]
// `str::split_once` was stabilized in 1.52. Do not lint this
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
let b = iter.next().unwrap();
}
+#[clippy::msrv = "1.52"]
fn _msrv_1_52() {
- #![clippy::msrv = "1.52"]
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
let mut iter = "a.b.c".splitn(2, '.');
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:14:13
+ --> $DIR/manual_split_once.rs:13:13
|
LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
= note: `-D clippy::manual-split-once` implied by `-D warnings`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:15:13
+ --> $DIR/manual_split_once.rs:14:13
|
LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:16:18
+ --> $DIR/manual_split_once.rs:15:18
|
LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:19:13
+ --> $DIR/manual_split_once.rs:18:13
|
LL | let _ = s.splitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:22:13
+ --> $DIR/manual_split_once.rs:21:13
|
LL | let _ = s.splitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:25:13
+ --> $DIR/manual_split_once.rs:24:13
|
LL | let _ = s.splitn(2, '=').skip(1).next().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:28:17
+ --> $DIR/manual_split_once.rs:27:17
|
LL | let _ = s.splitn(2, '=').nth(1)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:29:17
+ --> $DIR/manual_split_once.rs:28:17
|
LL | let _ = s.splitn(2, '=').skip(1).next()?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:30:17
+ --> $DIR/manual_split_once.rs:29:17
|
LL | let _ = s.rsplitn(2, '=').nth(1)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:31:17
+ --> $DIR/manual_split_once.rs:30:17
|
LL | let _ = s.rsplitn(2, '=').skip(1).next()?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:39:13
+ --> $DIR/manual_split_once.rs:38:13
|
LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:40:18
+ --> $DIR/manual_split_once.rs:39:18
|
LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:41:13
+ --> $DIR/manual_split_once.rs:40:13
|
LL | let _ = s.rsplitn(2, '=').nth(1);
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:45:5
+ --> $DIR/manual_split_once.rs:44:5
|
LL | let mut iter = "a.b.c".splitn(2, '.');
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:49:5
+ --> $DIR/manual_split_once.rs:48:5
|
LL | let mut iter = "a.b.c".splitn(2, '.');
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:53:5
+ --> $DIR/manual_split_once.rs:52:5
|
LL | let mut iter = "a.b.c".rsplitn(2, '.');
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:57:5
+ --> $DIR/manual_split_once.rs:56:5
|
LL | let mut iter = "a.b.c".rsplitn(2, '.');
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:142:13
+ --> $DIR/manual_split_once.rs:141:13
|
LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:144:5
+ --> $DIR/manual_split_once.rs:143:5
|
LL | let mut iter = "a.b.c".splitn(2, '.');
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_str_repeat)]
use std::borrow::Cow;
let _: String = repeat(x).take(count).collect();
}
+#[clippy::msrv = "1.15"]
fn _msrv_1_15() {
- #![clippy::msrv = "1.15"]
// `str::repeat` was stabilized in 1.16. Do not lint this
let _: String = std::iter::repeat("test").take(10).collect();
}
+#[clippy::msrv = "1.16"]
fn _msrv_1_16() {
- #![clippy::msrv = "1.16"]
let _: String = "test".repeat(10);
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_str_repeat)]
use std::borrow::Cow;
let _: String = repeat(x).take(count).collect();
}
+#[clippy::msrv = "1.15"]
fn _msrv_1_15() {
- #![clippy::msrv = "1.15"]
// `str::repeat` was stabilized in 1.16. Do not lint this
let _: String = std::iter::repeat("test").take(10).collect();
}
+#[clippy::msrv = "1.16"]
fn _msrv_1_16() {
- #![clippy::msrv = "1.16"]
let _: String = std::iter::repeat("test").take(10).collect();
}
error: manual implementation of `str::repeat` using iterators
- --> $DIR/manual_str_repeat.rs:10:21
+ --> $DIR/manual_str_repeat.rs:9:21
|
LL | let _: String = std::iter::repeat("test").take(10).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)`
= note: `-D clippy::manual-str-repeat` implied by `-D warnings`
error: manual implementation of `str::repeat` using iterators
- --> $DIR/manual_str_repeat.rs:11:21
+ --> $DIR/manual_str_repeat.rs:10:21
|
LL | let _: String = std::iter::repeat('x').take(10).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)`
error: manual implementation of `str::repeat` using iterators
- --> $DIR/manual_str_repeat.rs:12:21
+ --> $DIR/manual_str_repeat.rs:11:21
|
LL | let _: String = std::iter::repeat('/'').take(10).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)`
error: manual implementation of `str::repeat` using iterators
- --> $DIR/manual_str_repeat.rs:13:21
+ --> $DIR/manual_str_repeat.rs:12:21
|
LL | let _: String = std::iter::repeat('"').take(10).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)`
error: manual implementation of `str::repeat` using iterators
- --> $DIR/manual_str_repeat.rs:17:13
+ --> $DIR/manual_str_repeat.rs:16:13
|
LL | let _ = repeat(x).take(count + 2).collect::<String>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)`
error: manual implementation of `str::repeat` using iterators
- --> $DIR/manual_str_repeat.rs:26:21
+ --> $DIR/manual_str_repeat.rs:25:21
|
LL | let _: String = repeat(*x).take(count).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)`
error: manual implementation of `str::repeat` using iterators
- --> $DIR/manual_str_repeat.rs:35:21
+ --> $DIR/manual_str_repeat.rs:34:21
|
LL | let _: String = repeat(x).take(count).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)`
error: manual implementation of `str::repeat` using iterators
- --> $DIR/manual_str_repeat.rs:47:21
+ --> $DIR/manual_str_repeat.rs:46:21
|
LL | let _: String = repeat(Cow::Borrowed("test")).take(count).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Cow::Borrowed("test").repeat(count)`
error: manual implementation of `str::repeat` using iterators
- --> $DIR/manual_str_repeat.rs:50:21
+ --> $DIR/manual_str_repeat.rs:49:21
|
LL | let _: String = repeat(x).take(count).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)`
error: manual implementation of `str::repeat` using iterators
- --> $DIR/manual_str_repeat.rs:65:21
+ --> $DIR/manual_str_repeat.rs:64:21
|
LL | let _: String = std::iter::repeat("test").take(10).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)`
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_strip)]
fn main() {
}
}
+#[clippy::msrv = "1.44"]
fn msrv_1_44() {
- #![clippy::msrv = "1.44"]
-
let s = "abc";
if s.starts_with('a') {
s[1..].to_string();
}
}
+#[clippy::msrv = "1.45"]
fn msrv_1_45() {
- #![clippy::msrv = "1.45"]
-
let s = "abc";
if s.starts_with('a') {
s[1..].to_string();
error: stripping a prefix manually
- --> $DIR/manual_strip.rs:8:24
+ --> $DIR/manual_strip.rs:7:24
|
LL | str::to_string(&s["ab".len()..]);
| ^^^^^^^^^^^^^^^^
|
note: the prefix was tested here
- --> $DIR/manual_strip.rs:7:5
+ --> $DIR/manual_strip.rs:6:5
|
LL | if s.starts_with("ab") {
| ^^^^^^^^^^^^^^^^^^^^^^^
|
error: stripping a suffix manually
- --> $DIR/manual_strip.rs:16:24
+ --> $DIR/manual_strip.rs:15:24
|
LL | str::to_string(&s[..s.len() - "bc".len()]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the suffix was tested here
- --> $DIR/manual_strip.rs:15:5
+ --> $DIR/manual_strip.rs:14:5
|
LL | if s.ends_with("bc") {
| ^^^^^^^^^^^^^^^^^^^^^
|
error: stripping a prefix manually
- --> $DIR/manual_strip.rs:25:24
+ --> $DIR/manual_strip.rs:24:24
|
LL | str::to_string(&s[1..]);
| ^^^^^^^
|
note: the prefix was tested here
- --> $DIR/manual_strip.rs:24:5
+ --> $DIR/manual_strip.rs:23:5
|
LL | if s.starts_with('a') {
| ^^^^^^^^^^^^^^^^^^^^^^
|
error: stripping a prefix manually
- --> $DIR/manual_strip.rs:32:24
+ --> $DIR/manual_strip.rs:31:24
|
LL | str::to_string(&s[prefix.len()..]);
| ^^^^^^^^^^^^^^^^^^
|
note: the prefix was tested here
- --> $DIR/manual_strip.rs:31:5
+ --> $DIR/manual_strip.rs:30:5
|
LL | if s.starts_with(prefix) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: stripping a prefix manually
- --> $DIR/manual_strip.rs:38:24
+ --> $DIR/manual_strip.rs:37:24
|
LL | str::to_string(&s[PREFIX.len()..]);
| ^^^^^^^^^^^^^^^^^^
|
note: the prefix was tested here
- --> $DIR/manual_strip.rs:37:5
+ --> $DIR/manual_strip.rs:36:5
|
LL | if s.starts_with(PREFIX) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: stripping a prefix manually
- --> $DIR/manual_strip.rs:45:24
+ --> $DIR/manual_strip.rs:44:24
|
LL | str::to_string(&TARGET[prefix.len()..]);
| ^^^^^^^^^^^^^^^^^^^^^^^
|
note: the prefix was tested here
- --> $DIR/manual_strip.rs:44:5
+ --> $DIR/manual_strip.rs:43:5
|
LL | if TARGET.starts_with(prefix) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: stripping a prefix manually
- --> $DIR/manual_strip.rs:51:9
+ --> $DIR/manual_strip.rs:50:9
|
LL | s1[2..].to_uppercase();
| ^^^^^^^
|
note: the prefix was tested here
- --> $DIR/manual_strip.rs:50:5
+ --> $DIR/manual_strip.rs:49:5
|
LL | if s1.starts_with("ab") {
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
error: stripping a prefix manually
- --> $DIR/manual_strip.rs:83:9
+ --> $DIR/manual_strip.rs:80:9
|
LL | s[1..].to_string();
| ^^^^^^
|
note: the prefix was tested here
- --> $DIR/manual_strip.rs:82:5
+ --> $DIR/manual_strip.rs:79:5
|
LL | if s.starts_with('a') {
| ^^^^^^^^^^^^^^^^^^^^^^
// aux-build:option_helpers.rs
-#![feature(custom_inner_attributes)]
#![warn(clippy::map_unwrap_or)]
#![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)]
result_methods();
}
+#[clippy::msrv = "1.40"]
fn msrv_1_40() {
- #![clippy::msrv = "1.40"]
-
let res: Result<i32, ()> = Ok(1);
let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
}
+#[clippy::msrv = "1.41"]
fn msrv_1_41() {
- #![clippy::msrv = "1.41"]
-
let res: Result<i32, ()> = Ok(1);
let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:18:13
+ --> $DIR/map_unwrap_or.rs:17:13
|
LL | let _ = opt.map(|x| x + 1)
| _____________^
|
error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:22:13
+ --> $DIR/map_unwrap_or.rs:21:13
|
LL | let _ = opt.map(|x| {
| _____________^
|
error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:26:13
+ --> $DIR/map_unwrap_or.rs:25:13
|
LL | let _ = opt.map(|x| x + 1)
| _____________^
|
error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
- --> $DIR/map_unwrap_or.rs:31:13
+ --> $DIR/map_unwrap_or.rs:30:13
|
LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
- --> $DIR/map_unwrap_or.rs:33:13
+ --> $DIR/map_unwrap_or.rs:32:13
|
LL | let _ = opt.map(|x| {
| _____________^
|
error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
- --> $DIR/map_unwrap_or.rs:37:13
+ --> $DIR/map_unwrap_or.rs:36:13
|
LL | let _ = opt
| _____________^
|
error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:48:13
+ --> $DIR/map_unwrap_or.rs:47:13
|
LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:52:13
+ --> $DIR/map_unwrap_or.rs:51:13
|
LL | let _ = opt.map(|x| {
| _____________^
| |__________________________^
error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:56:13
+ --> $DIR/map_unwrap_or.rs:55:13
|
LL | let _ = opt.map(|x| x + 1)
| _____________^
| |_________^
error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:68:13
+ --> $DIR/map_unwrap_or.rs:67:13
|
LL | let _ = res.map(|x| {
| _____________^
| |____________________________^
error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:72:13
+ --> $DIR/map_unwrap_or.rs:71:13
|
LL | let _ = res.map(|x| x + 1)
| _____________^
| |__________^
error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
- --> $DIR/map_unwrap_or.rs:98:13
+ --> $DIR/map_unwrap_or.rs:95:13
|
LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::match_like_matches_macro)]
#![allow(
unreachable_patterns,
};
}
+#[clippy::msrv = "1.41"]
fn msrv_1_41() {
- #![clippy::msrv = "1.41"]
-
let _y = match Some(5) {
Some(0) => true,
_ => false,
};
}
+#[clippy::msrv = "1.42"]
fn msrv_1_42() {
- #![clippy::msrv = "1.42"]
-
let _y = matches!(Some(5), Some(0));
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::match_like_matches_macro)]
#![allow(
unreachable_patterns,
};
}
+#[clippy::msrv = "1.41"]
fn msrv_1_41() {
- #![clippy::msrv = "1.41"]
-
let _y = match Some(5) {
Some(0) => true,
_ => false,
};
}
+#[clippy::msrv = "1.42"]
fn msrv_1_42() {
- #![clippy::msrv = "1.42"]
-
let _y = match Some(5) {
Some(0) => true,
_ => false,
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:16:14
+ --> $DIR/match_expr_like_matches_macro.rs:15:14
|
LL | let _y = match x {
| ______________^
= note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:22:14
+ --> $DIR/match_expr_like_matches_macro.rs:21:14
|
LL | let _w = match x {
| ______________^
| |_____^ help: try this: `matches!(x, Some(_))`
error: redundant pattern matching, consider using `is_none()`
- --> $DIR/match_expr_like_matches_macro.rs:28:14
+ --> $DIR/match_expr_like_matches_macro.rs:27:14
|
LL | let _z = match x {
| ______________^
= note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:34:15
+ --> $DIR/match_expr_like_matches_macro.rs:33:15
|
LL | let _zz = match x {
| _______________^
| |_____^ help: try this: `!matches!(x, Some(r) if r == 0)`
error: if let .. else expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:40:16
+ --> $DIR/match_expr_like_matches_macro.rs:39:16
|
LL | let _zzz = if let Some(5) = x { true } else { false };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:64:20
+ --> $DIR/match_expr_like_matches_macro.rs:63:20
|
LL | let _ans = match x {
| ____________________^
| |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:74:20
+ --> $DIR/match_expr_like_matches_macro.rs:73:20
|
LL | let _ans = match x {
| ____________________^
| |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:84:20
+ --> $DIR/match_expr_like_matches_macro.rs:83:20
|
LL | let _ans = match x {
| ____________________^
| |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:144:18
+ --> $DIR/match_expr_like_matches_macro.rs:143:18
|
LL | let _z = match &z {
| __________________^
| |_________^ help: try this: `matches!(z, Some(3))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:153:18
+ --> $DIR/match_expr_like_matches_macro.rs:152:18
|
LL | let _z = match &z {
| __________________^
| |_________^ help: try this: `matches!(&z, Some(3))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:170:21
+ --> $DIR/match_expr_like_matches_macro.rs:169:21
|
LL | let _ = match &z {
| _____________________^
| |_____________^ help: try this: `matches!(&z, AnEnum::X)`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:184:20
+ --> $DIR/match_expr_like_matches_macro.rs:183:20
|
LL | let _res = match &val {
| ____________________^
| |_________^ help: try this: `matches!(&val, &Some(ref _a))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:196:20
+ --> $DIR/match_expr_like_matches_macro.rs:195:20
|
LL | let _res = match &val {
| ____________________^
| |_________^ help: try this: `matches!(&val, &Some(ref _a))`
error: match expression looks like `matches!` macro
- --> $DIR/match_expr_like_matches_macro.rs:256:14
+ --> $DIR/match_expr_like_matches_macro.rs:253:14
|
LL | let _y = match Some(5) {
| ______________^
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(unused)]
#![warn(
clippy::all,
dont_lint_primitive();
}
+#[clippy::msrv = "1.39"]
fn msrv_1_39() {
- #![clippy::msrv = "1.39"]
-
let mut s = String::from("foo");
let _ = std::mem::replace(&mut s, String::default());
}
+#[clippy::msrv = "1.40"]
fn msrv_1_40() {
- #![clippy::msrv = "1.40"]
-
let mut s = String::from("foo");
let _ = std::mem::take(&mut s);
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(unused)]
#![warn(
clippy::all,
dont_lint_primitive();
}
+#[clippy::msrv = "1.39"]
fn msrv_1_39() {
- #![clippy::msrv = "1.39"]
-
let mut s = String::from("foo");
let _ = std::mem::replace(&mut s, String::default());
}
+#[clippy::msrv = "1.40"]
fn msrv_1_40() {
- #![clippy::msrv = "1.40"]
-
let mut s = String::from("foo");
let _ = std::mem::replace(&mut s, String::default());
}
error: replacing an `Option` with `None`
- --> $DIR/mem_replace.rs:17:13
+ --> $DIR/mem_replace.rs:16:13
|
LL | let _ = mem::replace(&mut an_option, None);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
= note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
error: replacing an `Option` with `None`
- --> $DIR/mem_replace.rs:19:13
+ --> $DIR/mem_replace.rs:18:13
|
LL | let _ = mem::replace(an_option, None);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:24:13
+ --> $DIR/mem_replace.rs:23:13
|
LL | let _ = std::mem::replace(&mut s, String::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
= note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:27:13
+ --> $DIR/mem_replace.rs:26:13
|
LL | let _ = std::mem::replace(s, String::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:28:13
+ --> $DIR/mem_replace.rs:27:13
|
LL | let _ = std::mem::replace(s, Default::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:31:13
+ --> $DIR/mem_replace.rs:30:13
|
LL | let _ = std::mem::replace(&mut v, Vec::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:32:13
+ --> $DIR/mem_replace.rs:31:13
|
LL | let _ = std::mem::replace(&mut v, Default::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:33:13
+ --> $DIR/mem_replace.rs:32:13
|
LL | let _ = std::mem::replace(&mut v, Vec::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:34:13
+ --> $DIR/mem_replace.rs:33:13
|
LL | let _ = std::mem::replace(&mut v, vec![]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:37:13
+ --> $DIR/mem_replace.rs:36:13
|
LL | let _ = std::mem::replace(&mut hash_map, HashMap::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:40:13
+ --> $DIR/mem_replace.rs:39:13
|
LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:43:13
+ --> $DIR/mem_replace.rs:42:13
|
LL | let _ = std::mem::replace(&mut vd, VecDeque::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:46:13
+ --> $DIR/mem_replace.rs:45:13
|
LL | let _ = std::mem::replace(&mut hash_set, HashSet::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:49:13
+ --> $DIR/mem_replace.rs:48:13
|
LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:52:13
+ --> $DIR/mem_replace.rs:51:13
|
LL | let _ = std::mem::replace(&mut list, LinkedList::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:55:13
+ --> $DIR/mem_replace.rs:54:13
|
LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:58:13
+ --> $DIR/mem_replace.rs:57:13
|
LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:61:13
+ --> $DIR/mem_replace.rs:60:13
|
LL | let _ = std::mem::replace(&mut refstr, "");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:64:13
+ --> $DIR/mem_replace.rs:63:13
|
LL | let _ = std::mem::replace(&mut slice, &[]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:94:13
+ --> $DIR/mem_replace.rs:91:13
|
LL | let _ = std::mem::replace(&mut s, String::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
fn main() {}
+#[clippy::msrv = "1.42.0"]
fn just_under_msrv() {
- #![clippy::msrv = "1.42.0"]
let log2_10 = 3.321928094887362;
}
+#[clippy::msrv = "1.43.0"]
fn meets_msrv() {
- #![clippy::msrv = "1.43.0"]
let log2_10 = 3.321928094887362;
}
+#[clippy::msrv = "1.44.0"]
fn just_above_msrv() {
- #![clippy::msrv = "1.44.0"]
let log2_10 = 3.321928094887362;
}
+#[clippy::msrv = "1.42"]
fn no_patch_under() {
- #![clippy::msrv = "1.42"]
let log2_10 = 3.321928094887362;
}
+#[clippy::msrv = "1.43"]
fn no_patch_meets() {
+ let log2_10 = 3.321928094887362;
+}
+
+fn inner_attr_under() {
+ #![clippy::msrv = "1.42"]
+ let log2_10 = 3.321928094887362;
+}
+
+fn inner_attr_meets() {
#![clippy::msrv = "1.43"]
let log2_10 = 3.321928094887362;
}
+
+// https://github.com/rust-lang/rust-clippy/issues/6920
+fn scoping() {
+ mod m {
+ #![clippy::msrv = "1.42.0"]
+ }
+
+ // Should warn
+ let log2_10 = 3.321928094887362;
+
+ mod a {
+ #![clippy::msrv = "1.42.0"]
+
+ fn should_warn() {
+ #![clippy::msrv = "1.43.0"]
+ let log2_10 = 3.321928094887362;
+ }
+
+ fn should_not_warn() {
+ let log2_10 = 3.321928094887362;
+ }
+ }
+}
|
= help: consider using the constant directly
-error: aborting due to 3 previous errors
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
+ --> $DIR/min_rust_version_attr.rs:38:19
+ |
+LL | let log2_10 = 3.321928094887362;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
+ --> $DIR/min_rust_version_attr.rs:48:19
+ |
+LL | let log2_10 = 3.321928094887362;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
+ --> $DIR/min_rust_version_attr.rs:55:27
+ |
+LL | let log2_10 = 3.321928094887362;
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using the constant directly
+
+error: aborting due to 6 previous errors
LL | #![clippy::msrv = "invalid.version"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: `msrv` cannot be an outer attribute
+error: `invalid.version` is not a valid Rust version
--> $DIR/min_rust_version_invalid_attr.rs:6:1
|
LL | #[clippy::msrv = "invalid.version"]
--- /dev/null
+#![allow(unused)]
+#![warn(clippy::misnamed_getters)]
+
+struct A {
+ a: u8,
+ b: u8,
+ c: u8,
+}
+
+impl A {
+ fn a(&self) -> &u8 {
+ &self.b
+ }
+ fn a_mut(&mut self) -> &mut u8 {
+ &mut self.b
+ }
+
+ fn b(self) -> u8 {
+ self.a
+ }
+
+ fn b_mut(&mut self) -> &mut u8 {
+ &mut self.a
+ }
+
+ fn c(&self) -> &u8 {
+ &self.b
+ }
+
+ fn c_mut(&mut self) -> &mut u8 {
+ &mut self.a
+ }
+}
+
+union B {
+ a: u8,
+ b: u8,
+}
+
+impl B {
+ unsafe fn a(&self) -> &u8 {
+ &self.b
+ }
+ unsafe fn a_mut(&mut self) -> &mut u8 {
+ &mut self.b
+ }
+
+ unsafe fn b(self) -> u8 {
+ self.a
+ }
+
+ unsafe fn b_mut(&mut self) -> &mut u8 {
+ &mut self.a
+ }
+
+ unsafe fn c(&self) -> &u8 {
+ &self.b
+ }
+
+ unsafe fn c_mut(&mut self) -> &mut u8 {
+ &mut self.a
+ }
+
+ unsafe fn a_unchecked(&self) -> &u8 {
+ &self.b
+ }
+ unsafe fn a_unchecked_mut(&mut self) -> &mut u8 {
+ &mut self.b
+ }
+
+ unsafe fn b_unchecked(self) -> u8 {
+ self.a
+ }
+
+ unsafe fn b_unchecked_mut(&mut self) -> &mut u8 {
+ &mut self.a
+ }
+
+ unsafe fn c_unchecked(&self) -> &u8 {
+ &self.b
+ }
+
+ unsafe fn c_unchecked_mut(&mut self) -> &mut u8 {
+ &mut self.a
+ }
+}
+
+struct D {
+ d: u8,
+ inner: A,
+}
+
+impl core::ops::Deref for D {
+ type Target = A;
+ fn deref(&self) -> &A {
+ &self.inner
+ }
+}
+
+impl core::ops::DerefMut for D {
+ fn deref_mut(&mut self) -> &mut A {
+ &mut self.inner
+ }
+}
+
+impl D {
+ fn a(&self) -> &u8 {
+ &self.b
+ }
+ fn a_mut(&mut self) -> &mut u8 {
+ &mut self.b
+ }
+
+ fn d(&self) -> &u8 {
+ &self.b
+ }
+ fn d_mut(&mut self) -> &mut u8 {
+ &mut self.b
+ }
+}
+
+fn main() {
+ // test code goes here
+}
--- /dev/null
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:11:5
+ |
+LL | / fn a(&self) -> &u8 {
+LL | | &self.b
+ | | ------- help: consider using: `&self.a`
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::misnamed-getters` implied by `-D warnings`
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:14:5
+ |
+LL | / fn a_mut(&mut self) -> &mut u8 {
+LL | | &mut self.b
+ | | ----------- help: consider using: `&mut self.a`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:18:5
+ |
+LL | / fn b(self) -> u8 {
+LL | | self.a
+ | | ------ help: consider using: `self.b`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:22:5
+ |
+LL | / fn b_mut(&mut self) -> &mut u8 {
+LL | | &mut self.a
+ | | ----------- help: consider using: `&mut self.b`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:26:5
+ |
+LL | / fn c(&self) -> &u8 {
+LL | | &self.b
+ | | ------- help: consider using: `&self.c`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:30:5
+ |
+LL | / fn c_mut(&mut self) -> &mut u8 {
+LL | | &mut self.a
+ | | ----------- help: consider using: `&mut self.c`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:41:5
+ |
+LL | / unsafe fn a(&self) -> &u8 {
+LL | | &self.b
+ | | ------- help: consider using: `&self.a`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:44:5
+ |
+LL | / unsafe fn a_mut(&mut self) -> &mut u8 {
+LL | | &mut self.b
+ | | ----------- help: consider using: `&mut self.a`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:48:5
+ |
+LL | / unsafe fn b(self) -> u8 {
+LL | | self.a
+ | | ------ help: consider using: `self.b`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:52:5
+ |
+LL | / unsafe fn b_mut(&mut self) -> &mut u8 {
+LL | | &mut self.a
+ | | ----------- help: consider using: `&mut self.b`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:64:5
+ |
+LL | / unsafe fn a_unchecked(&self) -> &u8 {
+LL | | &self.b
+ | | ------- help: consider using: `&self.a`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:67:5
+ |
+LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 {
+LL | | &mut self.b
+ | | ----------- help: consider using: `&mut self.a`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:71:5
+ |
+LL | / unsafe fn b_unchecked(self) -> u8 {
+LL | | self.a
+ | | ------ help: consider using: `self.b`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:75:5
+ |
+LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 {
+LL | | &mut self.a
+ | | ----------- help: consider using: `&mut self.b`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:107:5
+ |
+LL | / fn a(&self) -> &u8 {
+LL | | &self.b
+ | | ------- help: consider using: `&self.a`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:110:5
+ |
+LL | / fn a_mut(&mut self) -> &mut u8 {
+LL | | &mut self.b
+ | | ----------- help: consider using: `&mut self.a`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:114:5
+ |
+LL | / fn d(&self) -> &u8 {
+LL | | &self.b
+ | | ------- help: consider using: `&self.d`
+LL | | }
+ | |_____^
+
+error: getter function appears to return the wrong field
+ --> $DIR/misnamed_getters.rs:117:5
+ |
+LL | / fn d_mut(&mut self) -> &mut u8 {
+LL | | &mut self.b
+ | | ----------- help: consider using: `&mut self.d`
+LL | | }
+ | |_____^
+
+error: aborting due to 18 previous errors
+
#![warn(clippy::missing_const_for_fn)]
#![feature(start)]
-#![feature(custom_inner_attributes)]
extern crate helper;
extern crate proc_macro_with_span;
helper::unstably_const_fn()
}
+#[clippy::msrv = "1.46.0"]
mod const_fn_stabilized_after_msrv {
- #![clippy::msrv = "1.46.0"]
-
// Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0.
fn const_fn_stabilized_after_msrv(byte: u8) {
byte.is_ascii_digit();
#![warn(clippy::missing_const_for_fn)]
#![allow(incomplete_features, clippy::let_and_return)]
-#![feature(custom_inner_attributes)]
use std::mem::transmute;
}
}
+#[clippy::msrv = "1.47.0"]
mod const_fn_stabilized_before_msrv {
- #![clippy::msrv = "1.47.0"]
-
// This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
fn const_fn_stabilized_before_msrv(byte: u8) {
byte.is_ascii_digit();
}
}
+#[clippy::msrv = "1.45"]
fn msrv_1_45() -> i32 {
- #![clippy::msrv = "1.45"]
-
45
}
+#[clippy::msrv = "1.46"]
fn msrv_1_46() -> i32 {
- #![clippy::msrv = "1.46"]
-
46
}
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:13:5
+ --> $DIR/could_be_const.rs:12:5
|
LL | / pub fn new() -> Self {
LL | | Self { guess: 42 }
= note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:17:5
+ --> $DIR/could_be_const.rs:16:5
|
LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
LL | | b
| |_____^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:23:1
+ --> $DIR/could_be_const.rs:22:1
|
LL | / fn one() -> i32 {
LL | | 1
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:28:1
+ --> $DIR/could_be_const.rs:27:1
|
LL | / fn two() -> i32 {
LL | | let abc = 2;
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:34:1
+ --> $DIR/could_be_const.rs:33:1
|
LL | / fn string() -> String {
LL | | String::new()
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:39:1
+ --> $DIR/could_be_const.rs:38:1
|
LL | / unsafe fn four() -> i32 {
LL | | 4
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:44:1
+ --> $DIR/could_be_const.rs:43:1
|
LL | / fn generic<T>(t: T) -> T {
LL | | t
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:52:1
+ --> $DIR/could_be_const.rs:51:1
|
LL | / fn generic_arr<T: Copy>(t: [T; 1]) -> T {
LL | | t[0]
| |_^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:65:9
+ --> $DIR/could_be_const.rs:64:9
|
LL | / pub fn b(self, a: &A) -> B {
LL | | B
| |_________^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:75:5
+ --> $DIR/could_be_const.rs:73:5
|
LL | / fn const_fn_stabilized_before_msrv(byte: u8) {
LL | | byte.is_ascii_digit();
| |_____^
error: this could be a `const fn`
- --> $DIR/could_be_const.rs:86:1
+ --> $DIR/could_be_const.rs:84:1
|
LL | / fn msrv_1_46() -> i32 {
-LL | | #![clippy::msrv = "1.46"]
-LL | |
LL | | 46
LL | | }
| |_^
// run-rustfix
-#![feature(custom_inner_attributes, lint_reasons)]
-
-#[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
-#[allow(
+#![feature(lint_reasons)]
+#![allow(
+ unused,
clippy::uninlined_format_args,
clippy::unnecessary_mut_passed,
clippy::unnecessary_to_owned
)]
+#![warn(clippy::needless_borrow)]
+
fn main() {
let a = 5;
let ref_a = &a;
fn h(_: &dyn Trait) {}
-#[allow(dead_code)]
fn check_expect_suppression() {
let a = 5;
#[expect(clippy::needless_borrow)]
let _ = x(&&a);
}
-#[allow(dead_code)]
mod issue9160 {
pub struct S<F> {
f: F,
}
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-#[allow(dead_code)]
mod copyable_iterator {
#[derive(Clone, Copy)]
struct Iter;
}
}
+#[clippy::msrv = "1.52.0"]
mod under_msrv {
- #![allow(dead_code)]
- #![clippy::msrv = "1.52.0"]
-
fn foo() {
let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
}
}
+#[clippy::msrv = "1.53.0"]
mod meets_msrv {
- #![allow(dead_code)]
- #![clippy::msrv = "1.53.0"]
-
fn foo() {
let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
}
}
-#[allow(unused)]
fn issue9383() {
// Should not lint because unions need explicit deref when accessing field
use std::mem::ManuallyDrop;
}
}
-#[allow(dead_code)]
fn closure_test() {
let env = "env".to_owned();
let arg = "arg".to_owned();
f(arg);
}
-#[allow(dead_code)]
mod significant_drop {
#[derive(Debug)]
struct X;
fn debug(_: impl std::fmt::Debug) {}
}
-#[allow(dead_code)]
mod used_exactly_once {
fn foo(x: String) {
use_x(x);
fn use_x(_: impl AsRef<str>) {}
}
-#[allow(dead_code)]
mod used_more_than_once {
fn foo(x: String) {
use_x(&x);
}
// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
-#[allow(dead_code)]
mod issue_9111 {
struct A;
}
}
-#[allow(dead_code)]
mod issue_9710 {
fn main() {
let string = String::new();
fn f<T: AsRef<str>>(_: T) {}
}
-#[allow(dead_code)]
mod issue_9739 {
fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
}
}
-#[allow(dead_code)]
mod issue_9739_method_variant {
struct S;
}
}
-#[allow(dead_code)]
mod issue_9782 {
fn foo<T: AsRef<[u8]>>(t: T) {
println!("{}", std::mem::size_of::<T>());
}
}
-#[allow(dead_code)]
mod issue_9782_type_relative_variant {
struct S;
}
}
-#[allow(dead_code)]
mod issue_9782_method_variant {
struct S;
// run-rustfix
-#![feature(custom_inner_attributes, lint_reasons)]
-
-#[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
-#[allow(
+#![feature(lint_reasons)]
+#![allow(
+ unused,
clippy::uninlined_format_args,
clippy::unnecessary_mut_passed,
clippy::unnecessary_to_owned
)]
+#![warn(clippy::needless_borrow)]
+
fn main() {
let a = 5;
let ref_a = &a;
fn h(_: &dyn Trait) {}
-#[allow(dead_code)]
fn check_expect_suppression() {
let a = 5;
#[expect(clippy::needless_borrow)]
let _ = x(&&a);
}
-#[allow(dead_code)]
mod issue9160 {
pub struct S<F> {
f: F,
}
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-#[allow(dead_code)]
mod copyable_iterator {
#[derive(Clone, Copy)]
struct Iter;
}
}
+#[clippy::msrv = "1.52.0"]
mod under_msrv {
- #![allow(dead_code)]
- #![clippy::msrv = "1.52.0"]
-
fn foo() {
let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
}
}
+#[clippy::msrv = "1.53.0"]
mod meets_msrv {
- #![allow(dead_code)]
- #![clippy::msrv = "1.53.0"]
-
fn foo() {
let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
}
}
-#[allow(unused)]
fn issue9383() {
// Should not lint because unions need explicit deref when accessing field
use std::mem::ManuallyDrop;
}
}
-#[allow(dead_code)]
fn closure_test() {
let env = "env".to_owned();
let arg = "arg".to_owned();
f(arg);
}
-#[allow(dead_code)]
mod significant_drop {
#[derive(Debug)]
struct X;
fn debug(_: impl std::fmt::Debug) {}
}
-#[allow(dead_code)]
mod used_exactly_once {
fn foo(x: String) {
use_x(&x);
fn use_x(_: impl AsRef<str>) {}
}
-#[allow(dead_code)]
mod used_more_than_once {
fn foo(x: String) {
use_x(&x);
}
// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
-#[allow(dead_code)]
mod issue_9111 {
struct A;
}
}
-#[allow(dead_code)]
mod issue_9710 {
fn main() {
let string = String::new();
fn f<T: AsRef<str>>(_: T) {}
}
-#[allow(dead_code)]
mod issue_9739 {
fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
}
}
-#[allow(dead_code)]
mod issue_9739_method_variant {
struct S;
}
}
-#[allow(dead_code)]
mod issue_9782 {
fn foo<T: AsRef<[u8]>>(t: T) {
println!("{}", std::mem::size_of::<T>());
}
}
-#[allow(dead_code)]
mod issue_9782_type_relative_variant {
struct S;
}
}
-#[allow(dead_code)]
mod issue_9782_method_variant {
struct S;
| ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()`
error: this expression borrows a value the compiler would automatically borrow
- --> $DIR/needless_borrow.rs:192:13
+ --> $DIR/needless_borrow.rs:190:13
|
LL | (&self.f)()
| ^^^^^^^^^ help: change this to: `(self.f)`
error: this expression borrows a value the compiler would automatically borrow
- --> $DIR/needless_borrow.rs:201:13
+ --> $DIR/needless_borrow.rs:199:13
|
LL | (&mut self.f)()
| ^^^^^^^^^^^^^ help: change this to: `(self.f)`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:286:20
+ --> $DIR/needless_borrow.rs:283:20
|
LL | takes_iter(&mut x)
| ^^^^^^ help: change this to: `x`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:304:55
+ --> $DIR/needless_borrow.rs:297:55
|
LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
| ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:344:37
+ --> $DIR/needless_borrow.rs:335:37
|
LL | let _ = std::fs::write("x", &arg);
| ^^^^ help: change this to: `arg`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:345:37
+ --> $DIR/needless_borrow.rs:336:37
|
LL | let _ = std::fs::write("x", &loc);
| ^^^^ help: change this to: `loc`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:364:15
+ --> $DIR/needless_borrow.rs:354:15
|
LL | debug(&x);
| ^^ help: change this to: `x`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:374:15
+ --> $DIR/needless_borrow.rs:363:15
|
LL | use_x(&x);
| ^^ help: change this to: `x`
error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:474:13
+ --> $DIR/needless_borrow.rs:457:13
|
LL | foo(&a);
| ^^ help: change this to: `a`
dead_code,
unused_must_use
)]
-#![feature(custom_inner_attributes)]
struct TO {
magic: Option<usize>,
dead_code,
unused_must_use
)]
-#![feature(custom_inner_attributes)]
struct TO {
magic: Option<usize>,
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:23:12
+ --> $DIR/needless_question_mark.rs:22:12
|
LL | return Some(to.magic?);
| ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
= note: `-D clippy::needless-question-mark` implied by `-D warnings`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:31:12
+ --> $DIR/needless_question_mark.rs:30:12
|
LL | return Some(to.magic?)
| ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:36:5
+ --> $DIR/needless_question_mark.rs:35:5
|
LL | Some(to.magic?)
| ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:41:21
+ --> $DIR/needless_question_mark.rs:40:21
|
LL | to.and_then(|t| Some(t.magic?))
| ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:50:9
+ --> $DIR/needless_question_mark.rs:49:9
|
LL | Some(t.magic?)
| ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:55:12
+ --> $DIR/needless_question_mark.rs:54:12
|
LL | return Ok(tr.magic?);
| ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:62:12
+ --> $DIR/needless_question_mark.rs:61:12
|
LL | return Ok(tr.magic?)
| ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:66:5
+ --> $DIR/needless_question_mark.rs:65:5
|
LL | Ok(tr.magic?)
| ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:70:21
+ --> $DIR/needless_question_mark.rs:69:21
|
LL | tr.and_then(|t| Ok(t.magic?))
| ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:78:9
+ --> $DIR/needless_question_mark.rs:77:9
|
LL | Ok(t.magic?)
| ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:85:16
+ --> $DIR/needless_question_mark.rs:84:16
|
LL | return Ok(t.magic?);
| ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:120:27
+ --> $DIR/needless_question_mark.rs:119:27
|
LL | || -> Option<_> { Some(Some($expr)?) }()
| ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)`
= note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:131:5
+ --> $DIR/needless_question_mark.rs:130:5
|
LL | Some(to.magic?)
| ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
error: question mark operator is useless here
- --> $DIR/needless_question_mark.rs:139:5
+ --> $DIR/needless_question_mark.rs:138:5
|
LL | Ok(s.magic?)
| ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic`
}
fn test_void_fun() {
-
}
fn test_void_if_fun(b: bool) {
if b {
-
} else {
-
}
}
0 => (),
1 => {
let _ = 42;
-
},
_ => (),
}
fn test_closure() {
let _ = || {
-
};
let _ = || {};
}
}
async fn async_test_void_fun() {
-
}
async fn async_test_void_if_fun(b: bool) {
if b {
-
} else {
-
}
}
}
}
+mod issue9416 {
+ pub fn with_newline() {
+ let _ = 42;
+ }
+
+ #[rustfmt::skip]
+ pub fn oneline() {
+ let _ = 42;
+ }
+}
+
fn main() {}
};
}
+mod issue9416 {
+ pub fn with_newline() {
+ let _ = 42;
+
+ return;
+ }
+
+ #[rustfmt::skip]
+ pub fn oneline() {
+ let _ = 42; return;
+ }
+}
+
fn main() {}
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:62:5
+ --> $DIR/needless_return.rs:61:21
|
-LL | return;
- | ^^^^^^
+LL | fn test_void_fun() {
+ | _____________________^
+LL | | return;
+ | |__________^
|
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:67:9
+ --> $DIR/needless_return.rs:66:11
|
-LL | return;
- | ^^^^^^
+LL | if b {
+ | ___________^
+LL | | return;
+ | |______________^
|
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:69:9
+ --> $DIR/needless_return.rs:68:13
|
-LL | return;
- | ^^^^^^
+LL | } else {
+ | _____________^
+LL | | return;
+ | |______________^
|
= help: remove `return`
= help: replace `return` with a unit value
error: unneeded `return` statement
- --> $DIR/needless_return.rs:85:13
+ --> $DIR/needless_return.rs:84:24
|
-LL | return;
- | ^^^^^^
+LL | let _ = 42;
+ | ________________________^
+LL | | return;
+ | |__________________^
|
= help: remove `return`
= help: replace `return` with an empty block
error: unneeded `return` statement
- --> $DIR/needless_return.rs:129:13
+ --> $DIR/needless_return.rs:128:21
|
-LL | return;
- | ^^^^^^
+LL | let _ = || {
+ | _____________________^
+LL | | return;
+ | |__________________^
|
= help: remove `return`
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:182:5
+ --> $DIR/needless_return.rs:181:33
|
-LL | return;
- | ^^^^^^
+LL | async fn async_test_void_fun() {
+ | _________________________________^
+LL | | return;
+ | |__________^
|
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:187:9
+ --> $DIR/needless_return.rs:186:11
|
-LL | return;
- | ^^^^^^
+LL | if b {
+ | ___________^
+LL | | return;
+ | |______________^
|
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:189:9
+ --> $DIR/needless_return.rs:188:13
|
-LL | return;
- | ^^^^^^
+LL | } else {
+ | _____________^
+LL | | return;
+ | |______________^
|
= help: remove `return`
|
= help: remove `return`
-error: aborting due to 44 previous errors
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:274:20
+ |
+LL | let _ = 42;
+ | ____________________^
+LL | |
+LL | | return;
+ | |______________^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:281:20
+ |
+LL | let _ = 42; return;
+ | ^^^^^^^
+ |
+ = help: remove `return`
+
+error: aborting due to 46 previous errors
// run-rustfix
// edition:2018
-#![feature(custom_inner_attributes)]
#![warn(clippy::needless_splitn)]
#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
Some(())
}
+#[clippy::msrv = "1.51"]
fn _test_msrv() {
- #![clippy::msrv = "1.51"]
// `manual_split_once` MSRV shouldn't apply to `needless_splitn`
let _ = "key=value".split('=').nth(0).unwrap();
}
// run-rustfix
// edition:2018
-#![feature(custom_inner_attributes)]
#![warn(clippy::needless_splitn)]
#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
Some(())
}
+#[clippy::msrv = "1.51"]
fn _test_msrv() {
- #![clippy::msrv = "1.51"]
// `manual_split_once` MSRV shouldn't apply to `needless_splitn`
let _ = "key=value".splitn(2, '=').nth(0).unwrap();
}
error: unnecessary use of `splitn`
- --> $DIR/needless_splitn.rs:15:13
+ --> $DIR/needless_splitn.rs:14:13
|
LL | let _ = str.splitn(2, '=').next();
| ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
= note: `-D clippy::needless-splitn` implied by `-D warnings`
error: unnecessary use of `splitn`
- --> $DIR/needless_splitn.rs:16:13
+ --> $DIR/needless_splitn.rs:15:13
|
LL | let _ = str.splitn(2, '=').nth(0);
| ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
error: unnecessary use of `splitn`
- --> $DIR/needless_splitn.rs:19:18
+ --> $DIR/needless_splitn.rs:18:18
|
LL | let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
| ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
error: unnecessary use of `rsplitn`
- --> $DIR/needless_splitn.rs:22:13
+ --> $DIR/needless_splitn.rs:21:13
|
LL | let _ = str.rsplitn(2, '=').next();
| ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
error: unnecessary use of `rsplitn`
- --> $DIR/needless_splitn.rs:23:13
+ --> $DIR/needless_splitn.rs:22:13
|
LL | let _ = str.rsplitn(2, '=').nth(0);
| ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
error: unnecessary use of `rsplitn`
- --> $DIR/needless_splitn.rs:26:18
+ --> $DIR/needless_splitn.rs:25:18
|
LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
| ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
error: unnecessary use of `splitn`
- --> $DIR/needless_splitn.rs:28:13
+ --> $DIR/needless_splitn.rs:27:13
|
LL | let _ = str.splitn(5, '=').next();
| ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
error: unnecessary use of `splitn`
- --> $DIR/needless_splitn.rs:29:13
+ --> $DIR/needless_splitn.rs:28:13
|
LL | let _ = str.splitn(5, '=').nth(3);
| ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
error: unnecessary use of `splitn`
- --> $DIR/needless_splitn.rs:35:13
+ --> $DIR/needless_splitn.rs:34:13
|
LL | let _ = s.splitn(2, '=').next()?;
| ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
error: unnecessary use of `splitn`
- --> $DIR/needless_splitn.rs:36:13
+ --> $DIR/needless_splitn.rs:35:13
|
LL | let _ = s.splitn(2, '=').nth(0)?;
| ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
error: unnecessary use of `rsplitn`
- --> $DIR/needless_splitn.rs:37:13
+ --> $DIR/needless_splitn.rs:36:13
|
LL | let _ = s.rsplitn(2, '=').next()?;
| ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
error: unnecessary use of `rsplitn`
- --> $DIR/needless_splitn.rs:38:13
+ --> $DIR/needless_splitn.rs:37:13
|
LL | let _ = s.rsplitn(2, '=').nth(0)?;
| ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
error: unnecessary use of `splitn`
- --> $DIR/needless_splitn.rs:46:13
+ --> $DIR/needless_splitn.rs:45:13
|
LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(unused, clippy::redundant_clone)]
#![warn(clippy::option_as_ref_deref)]
let _ = opt.as_deref();
}
+#[clippy::msrv = "1.39"]
fn msrv_1_39() {
- #![clippy::msrv = "1.39"]
-
let opt = Some(String::from("123"));
let _ = opt.as_ref().map(String::as_str);
}
+#[clippy::msrv = "1.40"]
fn msrv_1_40() {
- #![clippy::msrv = "1.40"]
-
let opt = Some(String::from("123"));
let _ = opt.as_deref();
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(unused, clippy::redundant_clone)]
#![warn(clippy::option_as_ref_deref)]
let _ = opt.as_ref().map(std::ops::Deref::deref);
}
+#[clippy::msrv = "1.39"]
fn msrv_1_39() {
- #![clippy::msrv = "1.39"]
-
let opt = Some(String::from("123"));
let _ = opt.as_ref().map(String::as_str);
}
+#[clippy::msrv = "1.40"]
fn msrv_1_40() {
- #![clippy::msrv = "1.40"]
-
let opt = Some(String::from("123"));
let _ = opt.as_ref().map(String::as_str);
}
error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:14:13
+ --> $DIR/option_as_ref_deref.rs:13:13
|
LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()`
= note: `-D clippy::option-as-ref-deref` implied by `-D warnings`
error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:17:13
+ --> $DIR/option_as_ref_deref.rs:16:13
|
LL | let _ = opt.clone()
| _____________^
| |_________^ help: try using as_deref instead: `opt.clone().as_deref()`
error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
- --> $DIR/option_as_ref_deref.rs:23:13
+ --> $DIR/option_as_ref_deref.rs:22:13
|
LL | let _ = opt.as_mut().map(DerefMut::deref_mut);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:25:13
+ --> $DIR/option_as_ref_deref.rs:24:13
|
LL | let _ = opt.as_ref().map(String::as_str);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:26:13
+ --> $DIR/option_as_ref_deref.rs:25:13
|
LL | let _ = opt.as_ref().map(|x| x.as_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
- --> $DIR/option_as_ref_deref.rs:27:13
+ --> $DIR/option_as_ref_deref.rs:26:13
|
LL | let _ = opt.as_mut().map(String::as_mut_str);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
- --> $DIR/option_as_ref_deref.rs:28:13
+ --> $DIR/option_as_ref_deref.rs:27:13
|
LL | let _ = opt.as_mut().map(|x| x.as_mut_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:29:13
+ --> $DIR/option_as_ref_deref.rs:28:13
|
LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()`
error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:30:13
+ --> $DIR/option_as_ref_deref.rs:29:13
|
LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()`
error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:31:13
+ --> $DIR/option_as_ref_deref.rs:30:13
|
LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()`
error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:32:13
+ --> $DIR/option_as_ref_deref.rs:31:13
|
LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()`
error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead
- --> $DIR/option_as_ref_deref.rs:33:13
+ --> $DIR/option_as_ref_deref.rs:32:13
|
LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()`
error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:35:13
+ --> $DIR/option_as_ref_deref.rs:34:13
|
LL | let _ = opt.as_ref().map(|x| x.deref());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead
- --> $DIR/option_as_ref_deref.rs:36:13
+ --> $DIR/option_as_ref_deref.rs:35:13
|
LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()`
error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:43:13
+ --> $DIR/option_as_ref_deref.rs:42:13
|
LL | let _ = opt.as_ref().map(|x| &**x);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
- --> $DIR/option_as_ref_deref.rs:44:13
+ --> $DIR/option_as_ref_deref.rs:43:13
|
LL | let _ = opt.as_mut().map(|x| &mut **x);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:47:13
+ --> $DIR/option_as_ref_deref.rs:46:13
|
LL | let _ = opt.as_ref().map(std::ops::Deref::deref);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
- --> $DIR/option_as_ref_deref.rs:61:13
+ --> $DIR/option_as_ref_deref.rs:58:13
|
LL | let _ = opt.as_ref().map(String::as_str);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
// aux-build:macro_rules.rs
#![warn(clippy::ptr_as_ptr)]
-#![feature(custom_inner_attributes)]
extern crate macro_rules;
let _ = macro_rules::ptr_as_ptr_cast!(ptr);
}
+#[clippy::msrv = "1.37"]
fn _msrv_1_37() {
- #![clippy::msrv = "1.37"]
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
let _ = mut_ptr as *mut i32;
}
+#[clippy::msrv = "1.38"]
fn _msrv_1_38() {
- #![clippy::msrv = "1.38"]
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
// aux-build:macro_rules.rs
#![warn(clippy::ptr_as_ptr)]
-#![feature(custom_inner_attributes)]
extern crate macro_rules;
let _ = macro_rules::ptr_as_ptr_cast!(ptr);
}
+#[clippy::msrv = "1.37"]
fn _msrv_1_37() {
- #![clippy::msrv = "1.37"]
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
let _ = mut_ptr as *mut i32;
}
+#[clippy::msrv = "1.38"]
fn _msrv_1_38() {
- #![clippy::msrv = "1.38"]
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:19:13
+ --> $DIR/ptr_as_ptr.rs:18:13
|
LL | let _ = ptr as *const i32;
| ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
= note: `-D clippy::ptr-as-ptr` implied by `-D warnings`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:20:13
+ --> $DIR/ptr_as_ptr.rs:19:13
|
LL | let _ = mut_ptr as *mut i32;
| ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:25:17
+ --> $DIR/ptr_as_ptr.rs:24:17
|
LL | let _ = *ptr_ptr as *const i32;
| ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::<i32>()`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:38:25
+ --> $DIR/ptr_as_ptr.rs:37:25
|
LL | let _: *const i32 = ptr as *const _;
| ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:39:23
+ --> $DIR/ptr_as_ptr.rs:38:23
|
LL | let _: *mut i32 = mut_ptr as _;
| ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:11:9
+ --> $DIR/ptr_as_ptr.rs:10:9
|
LL | $ptr as *const i32
| ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::<i32>()`
= note: this error originates in the macro `cast_it` (in Nightly builds, run with -Z macro-backtrace for more info)
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:63:13
+ --> $DIR/ptr_as_ptr.rs:62:13
|
LL | let _ = ptr as *const i32;
| ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:64:13
+ --> $DIR/ptr_as_ptr.rs:63:13
|
LL | let _ = mut_ptr as *mut i32;
| ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_range_contains)]
#![allow(unused)]
#![allow(clippy::no_effect)]
3 <= a && a <= 20
}
+#[clippy::msrv = "1.34"]
fn msrv_1_34() {
- #![clippy::msrv = "1.34"]
-
let x = 5;
x >= 8 && x < 34;
}
+#[clippy::msrv = "1.35"]
fn msrv_1_35() {
- #![clippy::msrv = "1.35"]
-
let x = 5;
(8..35).contains(&x);
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::manual_range_contains)]
#![allow(unused)]
#![allow(clippy::no_effect)]
3 <= a && a <= 20
}
+#[clippy::msrv = "1.34"]
fn msrv_1_34() {
- #![clippy::msrv = "1.34"]
-
let x = 5;
x >= 8 && x < 34;
}
+#[clippy::msrv = "1.35"]
fn msrv_1_35() {
- #![clippy::msrv = "1.35"]
-
let x = 5;
x >= 8 && x < 35;
}
error: manual `Range::contains` implementation
- --> $DIR/range_contains.rs:14:5
+ --> $DIR/range_contains.rs:13:5
|
LL | x >= 8 && x < 12;
| ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)`
= note: `-D clippy::manual-range-contains` implied by `-D warnings`
error: manual `Range::contains` implementation
- --> $DIR/range_contains.rs:15:5
+ --> $DIR/range_contains.rs:14:5
|
LL | x < 42 && x >= 21;
| ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)`
error: manual `Range::contains` implementation
- --> $DIR/range_contains.rs:16:5
+ --> $DIR/range_contains.rs:15:5
|
LL | 100 > x && 1 <= x;
| ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)`
error: manual `RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:19:5
+ --> $DIR/range_contains.rs:18:5
|
LL | x >= 9 && x <= 99;
| ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)`
error: manual `RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:20:5
+ --> $DIR/range_contains.rs:19:5
|
LL | x <= 33 && x >= 1;
| ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)`
error: manual `RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:21:5
+ --> $DIR/range_contains.rs:20:5
|
LL | 999 >= x && 1 <= x;
| ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)`
error: manual `!Range::contains` implementation
- --> $DIR/range_contains.rs:24:5
+ --> $DIR/range_contains.rs:23:5
|
LL | x < 8 || x >= 12;
| ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)`
error: manual `!Range::contains` implementation
- --> $DIR/range_contains.rs:25:5
+ --> $DIR/range_contains.rs:24:5
|
LL | x >= 42 || x < 21;
| ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)`
error: manual `!Range::contains` implementation
- --> $DIR/range_contains.rs:26:5
+ --> $DIR/range_contains.rs:25:5
|
LL | 100 <= x || 1 > x;
| ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)`
error: manual `!RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:29:5
+ --> $DIR/range_contains.rs:28:5
|
LL | x < 9 || x > 99;
| ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)`
error: manual `!RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:30:5
+ --> $DIR/range_contains.rs:29:5
|
LL | x > 33 || x < 1;
| ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)`
error: manual `!RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:31:5
+ --> $DIR/range_contains.rs:30:5
|
LL | 999 < x || 1 > x;
| ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)`
error: manual `Range::contains` implementation
- --> $DIR/range_contains.rs:46:5
+ --> $DIR/range_contains.rs:45:5
|
LL | y >= 0. && y < 1.;
| ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)`
error: manual `!RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:47:5
+ --> $DIR/range_contains.rs:46:5
|
LL | y < 0. || y > 1.;
| ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)`
error: manual `RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:50:5
+ --> $DIR/range_contains.rs:49:5
|
LL | x >= -10 && x <= 10;
| ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)`
error: manual `RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:52:5
+ --> $DIR/range_contains.rs:51:5
|
LL | y >= -3. && y <= 3.;
| ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)`
error: manual `RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:57:30
+ --> $DIR/range_contains.rs:56:30
|
LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
| ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)`
error: manual `RangeInclusive::contains` implementation
- --> $DIR/range_contains.rs:57:5
+ --> $DIR/range_contains.rs:56:5
|
LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
| ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)`
error: manual `!Range::contains` implementation
- --> $DIR/range_contains.rs:58:29
+ --> $DIR/range_contains.rs:57:29
|
LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
| ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)`
error: manual `!Range::contains` implementation
- --> $DIR/range_contains.rs:58:5
+ --> $DIR/range_contains.rs:57:5
|
LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
| ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)`
error: manual `Range::contains` implementation
- --> $DIR/range_contains.rs:79:5
+ --> $DIR/range_contains.rs:76:5
|
LL | x >= 8 && x < 35;
| ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)`
x * y
};
let d = async { something().await };
+
+ macro_rules! m {
+ () => {
+ 0
+ };
+ }
+ macro_rules! m2 {
+ () => {
+ m!()
+ };
+ }
+ m2!();
}
x * y
})();
let d = (async || something().await)();
+
+ macro_rules! m {
+ () => {
+ (|| 0)()
+ };
+ }
+ macro_rules! m2 {
+ () => {
+ (|| m!())()
+ };
+ }
+ m2!();
}
LL | let d = (async || something().await)();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }`
-error: aborting due to 4 previous errors
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:36:13
+ |
+LL | (|| m!())()
+ | ^^^^^^^^^^^ help: try doing something like: `m!()`
+...
+LL | m2!();
+ | ----- in this macro invocation
+ |
+ = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: try not to call a closure in the expression where it is declared
+ --> $DIR/redundant_closure_call_fixable.rs:31:13
+ |
+LL | (|| 0)()
+ | ^^^^^^^^ help: try doing something like: `0`
+...
+LL | m2!();
+ | ----- in this macro invocation
+ |
+ = note: this error originates in the macro `m` which comes from the expansion of the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 6 previous errors
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::redundant_field_names)]
#![allow(clippy::no_effect, dead_code, unused_variables)]
S { foo: foo::<i32> };
}
+#[clippy::msrv = "1.16"]
fn msrv_1_16() {
- #![clippy::msrv = "1.16"]
-
let start = 0;
let _ = RangeFrom { start: start };
}
+#[clippy::msrv = "1.17"]
fn msrv_1_17() {
- #![clippy::msrv = "1.17"]
-
let start = 0;
let _ = RangeFrom { start };
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::redundant_field_names)]
#![allow(clippy::no_effect, dead_code, unused_variables)]
S { foo: foo::<i32> };
}
+#[clippy::msrv = "1.16"]
fn msrv_1_16() {
- #![clippy::msrv = "1.16"]
-
let start = 0;
let _ = RangeFrom { start: start };
}
+#[clippy::msrv = "1.17"]
fn msrv_1_17() {
- #![clippy::msrv = "1.17"]
-
let start = 0;
let _ = RangeFrom { start: start };
}
error: redundant field names in struct initialization
- --> $DIR/redundant_field_names.rs:36:9
+ --> $DIR/redundant_field_names.rs:35:9
|
LL | gender: gender,
| ^^^^^^^^^^^^^^ help: replace it with: `gender`
= note: `-D clippy::redundant-field-names` implied by `-D warnings`
error: redundant field names in struct initialization
- --> $DIR/redundant_field_names.rs:37:9
+ --> $DIR/redundant_field_names.rs:36:9
|
LL | age: age,
| ^^^^^^^^ help: replace it with: `age`
error: redundant field names in struct initialization
- --> $DIR/redundant_field_names.rs:58:25
+ --> $DIR/redundant_field_names.rs:57:25
|
LL | let _ = RangeFrom { start: start };
| ^^^^^^^^^^^^ help: replace it with: `start`
error: redundant field names in struct initialization
- --> $DIR/redundant_field_names.rs:59:23
+ --> $DIR/redundant_field_names.rs:58:23
|
LL | let _ = RangeTo { end: end };
| ^^^^^^^^ help: replace it with: `end`
error: redundant field names in struct initialization
- --> $DIR/redundant_field_names.rs:60:21
+ --> $DIR/redundant_field_names.rs:59:21
|
LL | let _ = Range { start: start, end: end };
| ^^^^^^^^^^^^ help: replace it with: `start`
error: redundant field names in struct initialization
- --> $DIR/redundant_field_names.rs:60:35
+ --> $DIR/redundant_field_names.rs:59:35
|
LL | let _ = Range { start: start, end: end };
| ^^^^^^^^ help: replace it with: `end`
error: redundant field names in struct initialization
- --> $DIR/redundant_field_names.rs:62:32
+ --> $DIR/redundant_field_names.rs:61:32
|
LL | let _ = RangeToInclusive { end: end };
| ^^^^^^^^ help: replace it with: `end`
error: redundant field names in struct initialization
- --> $DIR/redundant_field_names.rs:86:25
+ --> $DIR/redundant_field_names.rs:83:25
|
LL | let _ = RangeFrom { start: start };
| ^^^^^^^^^^^^ help: replace it with: `start`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(unused)]
#[derive(Debug)]
const TRAIT_VAR: &'static str = "foo";
}
+#[clippy::msrv = "1.16"]
fn msrv_1_16() {
- #![clippy::msrv = "1.16"]
-
static V: &'static u8 = &16;
}
+#[clippy::msrv = "1.17"]
fn msrv_1_17() {
- #![clippy::msrv = "1.17"]
-
static V: &u8 = &17;
}
// run-rustfix
-#![feature(custom_inner_attributes)]
#![allow(unused)]
#[derive(Debug)]
const TRAIT_VAR: &'static str = "foo";
}
+#[clippy::msrv = "1.16"]
fn msrv_1_16() {
- #![clippy::msrv = "1.16"]
-
static V: &'static u8 = &16;
}
+#[clippy::msrv = "1.17"]
fn msrv_1_17() {
- #![clippy::msrv = "1.17"]
-
static V: &'static u8 = &17;
}
error: constants have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:9:17
+ --> $DIR/redundant_static_lifetimes.rs:8:17
|
LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
| -^^^^^^^---- help: consider removing `'static`: `&str`
= note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
error: constants have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:13:21
+ --> $DIR/redundant_static_lifetimes.rs:12:21
|
LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: constants have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:15:32
+ --> $DIR/redundant_static_lifetimes.rs:14:32
|
LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: constants have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:15:47
+ --> $DIR/redundant_static_lifetimes.rs:14:47
|
LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: constants have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:17:17
+ --> $DIR/redundant_static_lifetimes.rs:16:17
|
LL | const VAR_SIX: &'static u8 = &5;
| -^^^^^^^--- help: consider removing `'static`: `&u8`
error: constants have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:19:20
+ --> $DIR/redundant_static_lifetimes.rs:18:20
|
LL | const VAR_HEIGHT: &'static Foo = &Foo {};
| -^^^^^^^---- help: consider removing `'static`: `&Foo`
error: constants have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:21:19
+ --> $DIR/redundant_static_lifetimes.rs:20:19
|
LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
| -^^^^^^^----- help: consider removing `'static`: `&[u8]`
error: constants have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:23:19
+ --> $DIR/redundant_static_lifetimes.rs:22:19
|
LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
| -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
error: constants have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:25:19
+ --> $DIR/redundant_static_lifetimes.rs:24:19
|
LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
| -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
error: statics have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:27:25
+ --> $DIR/redundant_static_lifetimes.rs:26:25
|
LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: statics have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:31:29
+ --> $DIR/redundant_static_lifetimes.rs:30:29
|
LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
| -^^^^^^^---- help: consider removing `'static`: `&str`
error: statics have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:33:25
+ --> $DIR/redundant_static_lifetimes.rs:32:25
|
LL | static STATIC_VAR_SIX: &'static u8 = &5;
| -^^^^^^^--- help: consider removing `'static`: `&u8`
error: statics have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:35:28
+ --> $DIR/redundant_static_lifetimes.rs:34:28
|
LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
| -^^^^^^^---- help: consider removing `'static`: `&Foo`
error: statics have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:37:27
+ --> $DIR/redundant_static_lifetimes.rs:36:27
|
LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
| -^^^^^^^----- help: consider removing `'static`: `&[u8]`
error: statics have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:39:27
+ --> $DIR/redundant_static_lifetimes.rs:38:27
|
LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
| -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
error: statics have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:41:27
+ --> $DIR/redundant_static_lifetimes.rs:40:27
|
LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
| -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
error: statics have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:68:16
+ --> $DIR/redundant_static_lifetimes.rs:65:16
|
LL | static V: &'static u8 = &17;
| -^^^^^^^--- help: consider removing `'static`: `&u8`
Ok(())
}
+// Issue #10005
+enum Empty {}
+fn _empty_error() -> Result<(), Empty> {
+ Ok(())
+}
+
fn main() {}
// run-rustfix
#![warn(clippy::seek_from_current)]
-#![feature(custom_inner_attributes)]
use std::fs::File;
use std::io::{self, Seek, SeekFrom, Write};
+#[clippy::msrv = "1.50"]
fn _msrv_1_50() -> io::Result<()> {
- #![clippy::msrv = "1.50"]
let mut f = File::create("foo.txt")?;
f.write_all(b"Hi!")?;
f.seek(SeekFrom::Current(0))?;
Ok(())
}
+#[clippy::msrv = "1.51"]
fn _msrv_1_51() -> io::Result<()> {
- #![clippy::msrv = "1.51"]
let mut f = File::create("foo.txt")?;
f.write_all(b"Hi!")?;
f.stream_position()?;
// run-rustfix
#![warn(clippy::seek_from_current)]
-#![feature(custom_inner_attributes)]
use std::fs::File;
use std::io::{self, Seek, SeekFrom, Write};
+#[clippy::msrv = "1.50"]
fn _msrv_1_50() -> io::Result<()> {
- #![clippy::msrv = "1.50"]
let mut f = File::create("foo.txt")?;
f.write_all(b"Hi!")?;
f.seek(SeekFrom::Current(0))?;
Ok(())
}
+#[clippy::msrv = "1.51"]
fn _msrv_1_51() -> io::Result<()> {
- #![clippy::msrv = "1.51"]
let mut f = File::create("foo.txt")?;
f.write_all(b"Hi!")?;
f.seek(SeekFrom::Current(0))?;
error: using `SeekFrom::Current` to start from current position
- --> $DIR/seek_from_current.rs:21:5
+ --> $DIR/seek_from_current.rs:20:5
|
LL | f.seek(SeekFrom::Current(0))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `f.stream_position()`
// run-rustfix
#![allow(unused)]
-#![feature(custom_inner_attributes)]
#![warn(clippy::seek_to_start_instead_of_rewind)]
use std::fs::OpenOptions;
assert_eq!(&buf, hello);
}
+#[clippy::msrv = "1.54"]
fn msrv_1_54() {
- #![clippy::msrv = "1.54"]
-
let mut f = OpenOptions::new()
.write(true)
.read(true)
assert_eq!(&buf, hello);
}
+#[clippy::msrv = "1.55"]
fn msrv_1_55() {
- #![clippy::msrv = "1.55"]
-
let mut f = OpenOptions::new()
.write(true)
.read(true)
// run-rustfix
#![allow(unused)]
-#![feature(custom_inner_attributes)]
#![warn(clippy::seek_to_start_instead_of_rewind)]
use std::fs::OpenOptions;
assert_eq!(&buf, hello);
}
+#[clippy::msrv = "1.54"]
fn msrv_1_54() {
- #![clippy::msrv = "1.54"]
-
let mut f = OpenOptions::new()
.write(true)
.read(true)
assert_eq!(&buf, hello);
}
+#[clippy::msrv = "1.55"]
fn msrv_1_55() {
- #![clippy::msrv = "1.55"]
-
let mut f = OpenOptions::new()
.write(true)
.read(true)
error: used `seek` to go to the start of the stream
- --> $DIR/seek_to_start_instead_of_rewind.rs:54:7
+ --> $DIR/seek_to_start_instead_of_rewind.rs:53:7
|
LL | t.seek(SeekFrom::Start(0));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
= note: `-D clippy::seek-to-start-instead-of-rewind` implied by `-D warnings`
error: used `seek` to go to the start of the stream
- --> $DIR/seek_to_start_instead_of_rewind.rs:59:7
+ --> $DIR/seek_to_start_instead_of_rewind.rs:58:7
|
LL | t.seek(SeekFrom::Start(0));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
error: used `seek` to go to the start of the stream
- --> $DIR/seek_to_start_instead_of_rewind.rs:131:7
+ --> $DIR/seek_to_start_instead_of_rewind.rs:128:7
|
LL | f.seek(SeekFrom::Start(0));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::transmute_ptr_to_ref)]
#![allow(clippy::match_single_binding)]
}
}
+#[clippy::msrv = "1.38"]
unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
- #![clippy::msrv = "1.38"]
let a = 0u32;
let a = &a as *const u32;
let _: &u32 = &*a;
}
}
+#[clippy::msrv = "1.37"]
unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
- #![clippy::msrv = "1.37"]
let a = 0u32;
let a = &a as *const u32;
let _: &u32 = &*a;
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::transmute_ptr_to_ref)]
#![allow(clippy::match_single_binding)]
}
}
+#[clippy::msrv = "1.38"]
unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
- #![clippy::msrv = "1.38"]
let a = 0u32;
let a = &a as *const u32;
let _: &u32 = std::mem::transmute(a);
}
}
+#[clippy::msrv = "1.37"]
unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
- #![clippy::msrv = "1.37"]
let a = 0u32;
let a = &a as *const u32;
let _: &u32 = std::mem::transmute(a);
error: transmute from a pointer type (`*const T`) to a reference type (`&T`)
- --> $DIR/transmute_ptr_to_ref.rs:8:17
+ --> $DIR/transmute_ptr_to_ref.rs:7:17
|
LL | let _: &T = std::mem::transmute(p);
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p`
= note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings`
error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
- --> $DIR/transmute_ptr_to_ref.rs:11:21
+ --> $DIR/transmute_ptr_to_ref.rs:10:21
|
LL | let _: &mut T = std::mem::transmute(m);
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m`
error: transmute from a pointer type (`*mut T`) to a reference type (`&T`)
- --> $DIR/transmute_ptr_to_ref.rs:14:17
+ --> $DIR/transmute_ptr_to_ref.rs:13:17
|
LL | let _: &T = std::mem::transmute(m);
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m`
error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
- --> $DIR/transmute_ptr_to_ref.rs:17:21
+ --> $DIR/transmute_ptr_to_ref.rs:16:21
|
LL | let _: &mut T = std::mem::transmute(p as *mut T);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)`
error: transmute from a pointer type (`*const U`) to a reference type (`&T`)
- --> $DIR/transmute_ptr_to_ref.rs:20:17
+ --> $DIR/transmute_ptr_to_ref.rs:19:17
|
LL | let _: &T = std::mem::transmute(o);
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)`
error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`)
- --> $DIR/transmute_ptr_to_ref.rs:23:21
+ --> $DIR/transmute_ptr_to_ref.rs:22:21
|
LL | let _: &mut T = std::mem::transmute(om);
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)`
error: transmute from a pointer type (`*mut U`) to a reference type (`&T`)
- --> $DIR/transmute_ptr_to_ref.rs:26:17
+ --> $DIR/transmute_ptr_to_ref.rs:25:17
|
LL | let _: &T = std::mem::transmute(om);
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)`
error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`)
- --> $DIR/transmute_ptr_to_ref.rs:36:32
+ --> $DIR/transmute_ptr_to_ref.rs:35:32
|
LL | let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<_>>()`
error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`)
- --> $DIR/transmute_ptr_to_ref.rs:38:33
+ --> $DIR/transmute_ptr_to_ref.rs:37:33
|
LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<&_>>()`
error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`)
- --> $DIR/transmute_ptr_to_ref.rs:42:14
+ --> $DIR/transmute_ptr_to_ref.rs:41:14
|
LL | unsafe { std::mem::transmute::<_, Bar>(raw) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)`
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:47:14
+ --> $DIR/transmute_ptr_to_ref.rs:46:14
|
LL | 0 => std::mem::transmute(x),
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:48:14
+ --> $DIR/transmute_ptr_to_ref.rs:47:14
|
LL | 1 => std::mem::transmute(y),
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()`
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:49:14
+ --> $DIR/transmute_ptr_to_ref.rs:48:14
|
LL | 2 => std::mem::transmute::<_, &&'b u32>(x),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:50:14
+ --> $DIR/transmute_ptr_to_ref.rs:49:14
|
LL | _ => std::mem::transmute::<_, &&'b u32>(y),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()`
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:58:19
+ --> $DIR/transmute_ptr_to_ref.rs:57:19
|
LL | let _: &u32 = std::mem::transmute(a);
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:59:19
+ --> $DIR/transmute_ptr_to_ref.rs:58:19
|
LL | let _: &u32 = std::mem::transmute::<_, &u32>(a);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::<u32>()`
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:61:14
+ --> $DIR/transmute_ptr_to_ref.rs:60:14
|
LL | 0 => std::mem::transmute(x),
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:62:14
+ --> $DIR/transmute_ptr_to_ref.rs:61:14
|
LL | _ => std::mem::transmute::<_, &&'b u32>(x),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:70:19
+ --> $DIR/transmute_ptr_to_ref.rs:69:19
|
LL | let _: &u32 = std::mem::transmute(a);
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:71:19
+ --> $DIR/transmute_ptr_to_ref.rs:70:19
|
LL | let _: &u32 = std::mem::transmute::<_, &u32>(a);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)`
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:73:14
+ --> $DIR/transmute_ptr_to_ref.rs:72:14
|
LL | 0 => std::mem::transmute(x),
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)`
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
- --> $DIR/transmute_ptr_to_ref.rs:74:14
+ --> $DIR/transmute_ptr_to_ref.rs:73:14
|
LL | _ => std::mem::transmute::<_, &&'b u32>(x),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)`
// aux-build:proc_macro_unsafe.rs
-#![warn(clippy::undocumented_unsafe_blocks)]
+#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
#![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
extern crate proc_macro_unsafe;
|
= help: consider adding a safety comment on the preceding line
+error: constant item has unnecessary safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:471:5
+ |
+LL | const BIG_NUMBER: i32 = 1000000;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider removing the safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:470:5
+ |
+LL | // SAFETY:
+ | ^^^^^^^^^^
+ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings`
+
error: unsafe impl missing a safety comment
--> $DIR/undocumented_unsafe_blocks.rs:472:5
|
|
= help: consider adding a safety comment on the preceding line
+error: statement has unnecessary safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:501:5
+ |
+LL | / let _ = {
+LL | | if unsafe { true } {
+LL | | todo!();
+LL | | } else {
+... |
+LL | | }
+LL | | };
+ | |______^
+ |
+help: consider removing the safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:500:5
+ |
+LL | // SAFETY: this is more than one level away, so it should warn
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
error: unsafe block missing a safety comment
--> $DIR/undocumented_unsafe_blocks.rs:502:12
|
|
= help: consider adding a safety comment on the preceding line
-error: aborting due to 34 previous errors
+error: aborting due to 36 previous errors
// aux-build:proc_macro_with_span.rs
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::uninlined_format_args)]
#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
println!("val='{local_i32}'"); // space+tab
println!("val='{local_i32}'"); // tab+space
println!(
- "val='{
- }'",
- local_i32
+ "val='{local_i32}'"
);
println!("{local_i32}");
println!("{fn_arg}");
println!("{local_i32:<3}");
println!("{local_i32:#010x}");
println!("{local_f64:.1}");
- println!("Hello {} is {local_f64:.local_i32$}", "x");
- println!("Hello {local_i32} is {local_f64:.*}", 5);
- println!("Hello {local_i32} is {local_f64:.*}", 5);
+ println!("Hello {} is {:.*}", "x", local_i32, local_f64);
+ println!("Hello {} is {:.*}", local_i32, 5, local_f64);
+ println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
println!("{local_i32} {local_f64}");
- println!("{local_i32}, {}", local_opt.unwrap());
+ println!("{}, {}", local_i32, local_opt.unwrap());
println!("{val}");
println!("{val}");
println!("{} {1}", local_i32, 42);
println!("{local_f64:width$.prec$}");
println!("{local_f64:width$.prec$} {local_f64} {width} {prec}");
println!(
- "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}",
- local_i32, width, prec,
+ "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}",
);
println!(
"{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}",
println!(no_param_str!(), local_i32);
println!(
- "{}",
- // comment with a comma , in it
- val,
+ "{val}",
);
println!("{val}");
tester(42);
}
+#[clippy::msrv = "1.57"]
fn _under_msrv() {
- #![clippy::msrv = "1.57"]
let local_i32 = 1;
println!("don't expand='{}'", local_i32);
}
+#[clippy::msrv = "1.58"]
fn _meets_msrv() {
- #![clippy::msrv = "1.58"]
let local_i32 = 1;
println!("expand='{local_i32}'");
}
// aux-build:proc_macro_with_span.rs
// run-rustfix
-#![feature(custom_inner_attributes)]
#![warn(clippy::uninlined_format_args)]
#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
tester(42);
}
+#[clippy::msrv = "1.57"]
fn _under_msrv() {
- #![clippy::msrv = "1.57"]
let local_i32 = 1;
println!("don't expand='{}'", local_i32);
}
+#[clippy::msrv = "1.58"]
fn _meets_msrv() {
- #![clippy::msrv = "1.58"]
let local_i32 = 1;
println!("expand='{}'", local_i32);
}
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:41:5
+ --> $DIR/uninlined_format_args.rs:40:5
|
LL | println!("val='{}'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:42:5
+ --> $DIR/uninlined_format_args.rs:41:5
|
LL | println!("val='{ }'", local_i32); // 3 spaces
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:43:5
+ --> $DIR/uninlined_format_args.rs:42:5
|
LL | println!("val='{ }'", local_i32); // tab
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:44:5
+ --> $DIR/uninlined_format_args.rs:43:5
|
LL | println!("val='{ }'", local_i32); // space+tab
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:45:5
+ --> $DIR/uninlined_format_args.rs:44:5
|
LL | println!("val='{ }'", local_i32); // tab+space
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:51:5
+ --> $DIR/uninlined_format_args.rs:45:5
+ |
+LL | / println!(
+LL | | "val='{
+LL | | }'",
+LL | | local_i32
+LL | | );
+ | |_____^
+
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args.rs:50:5
|
LL | println!("{}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:52:5
+ --> $DIR/uninlined_format_args.rs:51:5
|
LL | println!("{}", fn_arg);
| ^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:53:5
+ --> $DIR/uninlined_format_args.rs:52:5
|
LL | println!("{:?}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:54:5
+ --> $DIR/uninlined_format_args.rs:53:5
|
LL | println!("{:#?}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:55:5
+ --> $DIR/uninlined_format_args.rs:54:5
|
LL | println!("{:4}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:56:5
+ --> $DIR/uninlined_format_args.rs:55:5
|
LL | println!("{:04}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:57:5
+ --> $DIR/uninlined_format_args.rs:56:5
|
LL | println!("{:<3}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:58:5
+ --> $DIR/uninlined_format_args.rs:57:5
|
LL | println!("{:#010x}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:59:5
+ --> $DIR/uninlined_format_args.rs:58:5
|
LL | println!("{:.1}", local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL + println!("{local_f64:.1}");
|
-error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:60:5
- |
-LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: change this to
- |
-LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-LL + println!("Hello {} is {local_f64:.local_i32$}", "x");
- |
-
-error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:61:5
- |
-LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: change this to
- |
-LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64);
-LL + println!("Hello {local_i32} is {local_f64:.*}", 5);
- |
-
error: variables can be used directly in the `format!` string
--> $DIR/uninlined_format_args.rs:62:5
|
-LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: change this to
- |
-LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
-LL + println!("Hello {local_i32} is {local_f64:.*}", 5);
- |
-
-error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:63:5
- |
LL | println!("{} {}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
--> $DIR/uninlined_format_args.rs:64:5
|
-LL | println!("{}, {}", local_i32, local_opt.unwrap());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: change this to
- |
-LL - println!("{}, {}", local_i32, local_opt.unwrap());
-LL + println!("{local_i32}, {}", local_opt.unwrap());
- |
-
-error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:65:5
- |
LL | println!("{}", val);
| ^^^^^^^^^^^^^^^^^^^
|
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:66:5
+ --> $DIR/uninlined_format_args.rs:65:5
|
LL | println!("{}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:68:5
+ --> $DIR/uninlined_format_args.rs:67:5
|
LL | println!("val='{/t }'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:69:5
+ --> $DIR/uninlined_format_args.rs:68:5
|
LL | println!("val='{/n }'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:70:5
+ --> $DIR/uninlined_format_args.rs:69:5
|
LL | println!("val='{local_i32}'", local_i32 = local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:71:5
+ --> $DIR/uninlined_format_args.rs:70:5
|
LL | println!("val='{local_i32}'", local_i32 = fn_arg);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:72:5
+ --> $DIR/uninlined_format_args.rs:71:5
|
LL | println!("{0}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:73:5
+ --> $DIR/uninlined_format_args.rs:72:5
|
LL | println!("{0:?}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:74:5
+ --> $DIR/uninlined_format_args.rs:73:5
|
LL | println!("{0:#?}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:75:5
+ --> $DIR/uninlined_format_args.rs:74:5
|
LL | println!("{0:04}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:76:5
+ --> $DIR/uninlined_format_args.rs:75:5
|
LL | println!("{0:<3}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:77:5
+ --> $DIR/uninlined_format_args.rs:76:5
|
LL | println!("{0:#010x}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:78:5
+ --> $DIR/uninlined_format_args.rs:77:5
|
LL | println!("{0:.1}", local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:79:5
+ --> $DIR/uninlined_format_args.rs:78:5
|
LL | println!("{0} {0}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:80:5
+ --> $DIR/uninlined_format_args.rs:79:5
|
LL | println!("{1} {} {0} {}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:81:5
+ --> $DIR/uninlined_format_args.rs:80:5
|
LL | println!("{0} {1}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:82:5
+ --> $DIR/uninlined_format_args.rs:81:5
|
LL | println!("{1} {0}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:83:5
+ --> $DIR/uninlined_format_args.rs:82:5
|
LL | println!("{1} {0} {1} {0}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:85:5
+ --> $DIR/uninlined_format_args.rs:84:5
|
LL | println!("{v}", v = local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:86:5
+ --> $DIR/uninlined_format_args.rs:85:5
|
LL | println!("{local_i32:0$}", width);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:87:5
+ --> $DIR/uninlined_format_args.rs:86:5
|
LL | println!("{local_i32:w$}", w = width);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:88:5
+ --> $DIR/uninlined_format_args.rs:87:5
|
LL | println!("{local_i32:.0$}", prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:89:5
+ --> $DIR/uninlined_format_args.rs:88:5
|
LL | println!("{local_i32:.p$}", p = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:90:5
+ --> $DIR/uninlined_format_args.rs:89:5
|
LL | println!("{:0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:91:5
+ --> $DIR/uninlined_format_args.rs:90:5
|
LL | println!("{0:0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:92:5
+ --> $DIR/uninlined_format_args.rs:91:5
|
LL | println!("{:0$.0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:93:5
+ --> $DIR/uninlined_format_args.rs:92:5
|
LL | println!("{0:0$.0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:94:5
+ --> $DIR/uninlined_format_args.rs:93:5
|
LL | println!("{0:0$.v$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:95:5
+ --> $DIR/uninlined_format_args.rs:94:5
|
LL | println!("{0:v$.0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:96:5
+ --> $DIR/uninlined_format_args.rs:95:5
|
LL | println!("{v:0$.0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:97:5
+ --> $DIR/uninlined_format_args.rs:96:5
|
LL | println!("{v:v$.0$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:98:5
+ --> $DIR/uninlined_format_args.rs:97:5
|
LL | println!("{v:0$.v$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:99:5
+ --> $DIR/uninlined_format_args.rs:98:5
|
LL | println!("{v:v$.v$}", v = val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:100:5
+ --> $DIR/uninlined_format_args.rs:99:5
|
LL | println!("{:0$}", width);
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:101:5
+ --> $DIR/uninlined_format_args.rs:100:5
|
LL | println!("{:1$}", local_i32, width);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:102:5
+ --> $DIR/uninlined_format_args.rs:101:5
|
LL | println!("{:w$}", w = width);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:103:5
+ --> $DIR/uninlined_format_args.rs:102:5
|
LL | println!("{:w$}", local_i32, w = width);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:104:5
+ --> $DIR/uninlined_format_args.rs:103:5
|
LL | println!("{:.0$}", prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:105:5
+ --> $DIR/uninlined_format_args.rs:104:5
|
LL | println!("{:.1$}", local_i32, prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:106:5
+ --> $DIR/uninlined_format_args.rs:105:5
|
LL | println!("{:.p$}", p = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:107:5
+ --> $DIR/uninlined_format_args.rs:106:5
|
LL | println!("{:.p$}", local_i32, p = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:108:5
+ --> $DIR/uninlined_format_args.rs:107:5
|
LL | println!("{:0$.1$}", width, prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:109:5
+ --> $DIR/uninlined_format_args.rs:108:5
|
LL | println!("{:0$.w$}", width, w = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:110:5
+ --> $DIR/uninlined_format_args.rs:109:5
|
LL | println!("{:1$.2$}", local_f64, width, prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:111:5
+ --> $DIR/uninlined_format_args.rs:110:5
|
LL | println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:123:5
+ --> $DIR/uninlined_format_args.rs:111:5
+ |
+LL | / println!(
+LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}",
+LL | | local_i32, width, prec,
+LL | | );
+ | |_____^
+
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args.rs:122:5
|
LL | println!("Width = {}, value with width = {:0$}", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:124:5
+ --> $DIR/uninlined_format_args.rs:123:5
|
LL | println!("{:w$.p$}", local_i32, w = width, p = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:125:5
+ --> $DIR/uninlined_format_args.rs:124:5
|
LL | println!("{:w$.p$}", w = width, p = prec);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:126:20
+ --> $DIR/uninlined_format_args.rs:125:20
|
LL | println!("{}", format!("{}", local_i32));
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:149:5
+ --> $DIR/uninlined_format_args.rs:143:5
+ |
+LL | / println!(
+LL | | "{}",
+LL | | // comment with a comma , in it
+LL | | val,
+LL | | );
+ | |_____^
+
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args.rs:148:5
|
LL | println!("{}", /* comment with a comma , in it */ val);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:155:9
+ --> $DIR/uninlined_format_args.rs:154:9
|
LL | panic!("p1 {}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:158:9
+ --> $DIR/uninlined_format_args.rs:157:9
|
LL | panic!("p2 {0}", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:161:9
+ --> $DIR/uninlined_format_args.rs:160:9
|
LL | panic!("p3 {local_i32}", local_i32 = local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:181:5
+ --> $DIR/uninlined_format_args.rs:180:5
|
LL | println!("expand='{}'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL + println!("expand='{local_i32}'");
|
-error: aborting due to 73 previous errors
+error: aborting due to 72 previous errors
// do not lint cast to alias type
1 as I32Alias;
&1 as &I32Alias;
+
+ // issue #9960
+ macro_rules! bind_var {
+ ($id:ident, $e:expr) => {{
+ let $id = 0usize;
+ let _ = $e != 0usize;
+ let $id = 0isize;
+ let _ = $e != 0usize;
+ }}
+ }
+ bind_var!(x, (x as usize) + 1);
}
type I32Alias = i32;
let _ = 1 as I32Alias;
let _ = &1 as &I32Alias;
+
+ let x = 1i32;
+ let _ = &{ x };
}
type I32Alias = i32;
// do not lint cast to alias type
1 as I32Alias;
&1 as &I32Alias;
+
+ // issue #9960
+ macro_rules! bind_var {
+ ($id:ident, $e:expr) => {{
+ let $id = 0usize;
+ let _ = $e != 0usize;
+ let $id = 0isize;
+ let _ = $e != 0usize;
+ }}
+ }
+ bind_var!(x, (x as usize) + 1);
}
type I32Alias = i32;
let _ = 1 as I32Alias;
let _ = &1 as &I32Alias;
+
+ let x = 1i32;
+ let _ = &(x as i32);
}
type I32Alias = i32;
| ^^^^^^^^^^^^ help: try: `1_f32`
error: casting integer literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:53:9
+ --> $DIR/unnecessary_cast.rs:64:9
|
LL | 100 as f32;
| ^^^^^^^^^^ help: try: `100_f32`
error: casting integer literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:54:9
+ --> $DIR/unnecessary_cast.rs:65:9
|
LL | 100 as f64;
| ^^^^^^^^^^ help: try: `100_f64`
error: casting integer literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:55:9
+ --> $DIR/unnecessary_cast.rs:66:9
|
LL | 100_i32 as f64;
| ^^^^^^^^^^^^^^ help: try: `100_f64`
error: casting integer literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:56:17
+ --> $DIR/unnecessary_cast.rs:67:17
|
LL | let _ = -100 as f32;
| ^^^^^^^^^^^ help: try: `-100_f32`
error: casting integer literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:57:17
+ --> $DIR/unnecessary_cast.rs:68:17
|
LL | let _ = -100 as f64;
| ^^^^^^^^^^^ help: try: `-100_f64`
error: casting integer literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:58:17
+ --> $DIR/unnecessary_cast.rs:69:17
|
LL | let _ = -100_i32 as f64;
| ^^^^^^^^^^^^^^^ help: try: `-100_f64`
error: casting float literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:59:9
+ --> $DIR/unnecessary_cast.rs:70:9
|
LL | 100. as f32;
| ^^^^^^^^^^^ help: try: `100_f32`
error: casting float literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:60:9
+ --> $DIR/unnecessary_cast.rs:71:9
|
LL | 100. as f64;
| ^^^^^^^^^^^ help: try: `100_f64`
error: casting integer literal to `u32` is unnecessary
- --> $DIR/unnecessary_cast.rs:72:9
+ --> $DIR/unnecessary_cast.rs:83:9
|
LL | 1 as u32;
| ^^^^^^^^ help: try: `1_u32`
error: casting integer literal to `i32` is unnecessary
- --> $DIR/unnecessary_cast.rs:73:9
+ --> $DIR/unnecessary_cast.rs:84:9
|
LL | 0x10 as i32;
| ^^^^^^^^^^^ help: try: `0x10_i32`
error: casting integer literal to `usize` is unnecessary
- --> $DIR/unnecessary_cast.rs:74:9
+ --> $DIR/unnecessary_cast.rs:85:9
|
LL | 0b10 as usize;
| ^^^^^^^^^^^^^ help: try: `0b10_usize`
error: casting integer literal to `u16` is unnecessary
- --> $DIR/unnecessary_cast.rs:75:9
+ --> $DIR/unnecessary_cast.rs:86:9
|
LL | 0o73 as u16;
| ^^^^^^^^^^^ help: try: `0o73_u16`
error: casting integer literal to `u32` is unnecessary
- --> $DIR/unnecessary_cast.rs:76:9
+ --> $DIR/unnecessary_cast.rs:87:9
|
LL | 1_000_000_000 as u32;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32`
error: casting float literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:78:9
+ --> $DIR/unnecessary_cast.rs:89:9
|
LL | 1.0 as f64;
| ^^^^^^^^^^ help: try: `1.0_f64`
error: casting float literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:79:9
+ --> $DIR/unnecessary_cast.rs:90:9
|
LL | 0.5 as f32;
| ^^^^^^^^^^ help: try: `0.5_f32`
error: casting integer literal to `i32` is unnecessary
- --> $DIR/unnecessary_cast.rs:83:17
+ --> $DIR/unnecessary_cast.rs:94:17
|
LL | let _ = -1 as i32;
| ^^^^^^^^^ help: try: `-1_i32`
error: casting float literal to `f32` is unnecessary
- --> $DIR/unnecessary_cast.rs:84:17
+ --> $DIR/unnecessary_cast.rs:95:17
|
LL | let _ = -1.0 as f32;
| ^^^^^^^^^^^ help: try: `-1.0_f32`
+error: casting to the same type is unnecessary (`i32` -> `i32`)
+ --> $DIR/unnecessary_cast.rs:101:18
+ |
+LL | let _ = &(x as i32);
+ | ^^^^^^^^^^ help: try: `{ x }`
+
error: casting integer literal to `i32` is unnecessary
- --> $DIR/unnecessary_cast.rs:93:22
+ --> $DIR/unnecessary_cast.rs:107:22
|
LL | let _: i32 = -(1) as i32;
| ^^^^^^^^^^^ help: try: `-1_i32`
error: casting integer literal to `i64` is unnecessary
- --> $DIR/unnecessary_cast.rs:95:22
+ --> $DIR/unnecessary_cast.rs:109:22
|
LL | let _: i64 = -(1) as i64;
| ^^^^^^^^^^^ help: try: `-1_i64`
error: casting float literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:102:22
+ --> $DIR/unnecessary_cast.rs:116:22
|
LL | let _: f64 = (-8.0 as f64).exp();
| ^^^^^^^^^^^^^ help: try: `(-8.0_f64)`
error: casting float literal to `f64` is unnecessary
- --> $DIR/unnecessary_cast.rs:104:23
+ --> $DIR/unnecessary_cast.rs:118:23
|
LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior
| ^^^^^^^^^^^^ help: try: `8.0_f64`
error: casting to the same type is unnecessary (`f32` -> `f32`)
- --> $DIR/unnecessary_cast.rs:112:20
+ --> $DIR/unnecessary_cast.rs:126:20
|
LL | let _num = foo() as f32;
| ^^^^^^^^^^^^ help: try: `foo()`
-error: aborting due to 30 previous errors
+error: aborting due to 31 previous errors
}
}
+struct Issue9427FollowUp;
+
+impl Drop for Issue9427FollowUp {
+ fn drop(&mut self) {
+ panic!("side effect drop");
+ }
+}
+
fn main() {
let astronomers_pi = 10;
let ext_arr: [usize; 1] = [2];
// Should not lint - bool
let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop
+ let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop
// should not lint, bind_instead_of_map takes priority
let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
let _: Result<usize, usize> = res.
- // some lines
- // some lines
- // some lines
- // some lines
- // some lines
- // some lines
- or(Ok(ext_str.some_field));
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ or(Ok(ext_str.some_field));
// neither bind_instead_of_map nor unnecessary_lazy_eval applies here
let _: Result<usize, usize> = res.and_then(|x| Err(x));
}
}
+struct Issue9427FollowUp;
+
+impl Drop for Issue9427FollowUp {
+ fn drop(&mut self) {
+ panic!("side effect drop");
+ }
+}
+
fn main() {
let astronomers_pi = 10;
let ext_arr: [usize; 1] = [2];
// Should not lint - bool
let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop
+ let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop
// should not lint, bind_instead_of_map takes priority
let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
let _: Result<usize, usize> = res.
- // some lines
- // some lines
- // some lines
- // some lines
- // some lines
- // some lines
- or_else(|_| Ok(ext_str.some_field));
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ // some lines
+ or_else(|_| Ok(ext_str.some_field));
// neither bind_instead_of_map nor unnecessary_lazy_eval applies here
let _: Result<usize, usize> = res.and_then(|x| Err(x));
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:48:13
+ --> $DIR/unnecessary_lazy_eval.rs:56:13
|
LL | let _ = opt.unwrap_or_else(|| 2);
| ^^^^--------------------
= note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:49:13
+ --> $DIR/unnecessary_lazy_eval.rs:57:13
|
LL | let _ = opt.unwrap_or_else(|| astronomers_pi);
| ^^^^---------------------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:50:13
+ --> $DIR/unnecessary_lazy_eval.rs:58:13
|
LL | let _ = opt.unwrap_or_else(|| ext_str.some_field);
| ^^^^-------------------------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:52:13
+ --> $DIR/unnecessary_lazy_eval.rs:60:13
|
LL | let _ = opt.and_then(|_| ext_opt);
| ^^^^---------------------
| help: use `and(..)` instead: `and(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:53:13
+ --> $DIR/unnecessary_lazy_eval.rs:61:13
|
LL | let _ = opt.or_else(|| ext_opt);
| ^^^^-------------------
| help: use `or(..)` instead: `or(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:54:13
+ --> $DIR/unnecessary_lazy_eval.rs:62:13
|
LL | let _ = opt.or_else(|| None);
| ^^^^----------------
| help: use `or(..)` instead: `or(None)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:55:13
+ --> $DIR/unnecessary_lazy_eval.rs:63:13
|
LL | let _ = opt.get_or_insert_with(|| 2);
| ^^^^------------------------
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:56:13
+ --> $DIR/unnecessary_lazy_eval.rs:64:13
|
LL | let _ = opt.ok_or_else(|| 2);
| ^^^^----------------
| help: use `ok_or(..)` instead: `ok_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:57:13
+ --> $DIR/unnecessary_lazy_eval.rs:65:13
|
LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
| ^^^^^^^^^^^^^^^^^-------------------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
error: unnecessary closure used with `bool::then`
- --> $DIR/unnecessary_lazy_eval.rs:58:13
+ --> $DIR/unnecessary_lazy_eval.rs:66:13
|
LL | let _ = cond.then(|| astronomers_pi);
| ^^^^^-----------------------
| help: use `then_some(..)` instead: `then_some(astronomers_pi)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:61:13
+ --> $DIR/unnecessary_lazy_eval.rs:69:13
|
LL | let _ = Some(10).unwrap_or_else(|| 2);
| ^^^^^^^^^--------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:62:13
+ --> $DIR/unnecessary_lazy_eval.rs:70:13
|
LL | let _ = Some(10).and_then(|_| ext_opt);
| ^^^^^^^^^---------------------
| help: use `and(..)` instead: `and(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:63:28
+ --> $DIR/unnecessary_lazy_eval.rs:71:28
|
LL | let _: Option<usize> = None.or_else(|| ext_opt);
| ^^^^^-------------------
| help: use `or(..)` instead: `or(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:64:13
+ --> $DIR/unnecessary_lazy_eval.rs:72:13
|
LL | let _ = None.get_or_insert_with(|| 2);
| ^^^^^------------------------
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:65:35
+ --> $DIR/unnecessary_lazy_eval.rs:73:35
|
LL | let _: Result<usize, usize> = None.ok_or_else(|| 2);
| ^^^^^----------------
| help: use `ok_or(..)` instead: `ok_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:66:28
+ --> $DIR/unnecessary_lazy_eval.rs:74:28
|
LL | let _: Option<usize> = None.or_else(|| None);
| ^^^^^----------------
| help: use `or(..)` instead: `or(None)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:69:13
+ --> $DIR/unnecessary_lazy_eval.rs:77:13
|
LL | let _ = deep.0.unwrap_or_else(|| 2);
| ^^^^^^^--------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:70:13
+ --> $DIR/unnecessary_lazy_eval.rs:78:13
|
LL | let _ = deep.0.and_then(|_| ext_opt);
| ^^^^^^^---------------------
| help: use `and(..)` instead: `and(ext_opt)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:71:13
+ --> $DIR/unnecessary_lazy_eval.rs:79:13
|
LL | let _ = deep.0.or_else(|| None);
| ^^^^^^^----------------
| help: use `or(..)` instead: `or(None)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:72:13
+ --> $DIR/unnecessary_lazy_eval.rs:80:13
|
LL | let _ = deep.0.get_or_insert_with(|| 2);
| ^^^^^^^------------------------
| help: use `get_or_insert(..)` instead: `get_or_insert(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:73:13
+ --> $DIR/unnecessary_lazy_eval.rs:81:13
|
LL | let _ = deep.0.ok_or_else(|| 2);
| ^^^^^^^----------------
| help: use `ok_or(..)` instead: `ok_or(2)`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:96:28
+ --> $DIR/unnecessary_lazy_eval.rs:105:28
|
LL | let _: Option<usize> = None.or_else(|| Some(3));
| ^^^^^-------------------
| help: use `or(..)` instead: `or(Some(3))`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:97:13
+ --> $DIR/unnecessary_lazy_eval.rs:106:13
|
LL | let _ = deep.0.or_else(|| Some(3));
| ^^^^^^^-------------------
| help: use `or(..)` instead: `or(Some(3))`
error: unnecessary closure used to substitute value for `Option::None`
- --> $DIR/unnecessary_lazy_eval.rs:98:13
+ --> $DIR/unnecessary_lazy_eval.rs:107:13
|
LL | let _ = opt.or_else(|| Some(3));
| ^^^^-------------------
| help: use `or(..)` instead: `or(Some(3))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:104:13
+ --> $DIR/unnecessary_lazy_eval.rs:113:13
|
LL | let _ = res2.unwrap_or_else(|_| 2);
| ^^^^^---------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(2)`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:105:13
+ --> $DIR/unnecessary_lazy_eval.rs:114:13
|
LL | let _ = res2.unwrap_or_else(|_| astronomers_pi);
| ^^^^^----------------------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:106:13
+ --> $DIR/unnecessary_lazy_eval.rs:115:13
|
LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field);
| ^^^^^--------------------------------------
| help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:128:35
+ --> $DIR/unnecessary_lazy_eval.rs:137:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(2));
| ^^^^--------------------
| help: use `and(..)` instead: `and(Err(2))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:129:35
+ --> $DIR/unnecessary_lazy_eval.rs:138:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
| ^^^^---------------------------------
| help: use `and(..)` instead: `and(Err(astronomers_pi))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:130:35
+ --> $DIR/unnecessary_lazy_eval.rs:139:35
|
LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
| ^^^^-------------------------------------
| help: use `and(..)` instead: `and(Err(ext_str.some_field))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:132:35
+ --> $DIR/unnecessary_lazy_eval.rs:141:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2));
| ^^^^------------------
| help: use `or(..)` instead: `or(Ok(2))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:133:35
+ --> $DIR/unnecessary_lazy_eval.rs:142:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
| ^^^^-------------------------------
| help: use `or(..)` instead: `or(Ok(astronomers_pi))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:134:35
+ --> $DIR/unnecessary_lazy_eval.rs:143:35
|
LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
| ^^^^-----------------------------------
| help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
error: unnecessary closure used to substitute value for `Result::Err`
- --> $DIR/unnecessary_lazy_eval.rs:135:35
+ --> $DIR/unnecessary_lazy_eval.rs:144:35
|
LL | let _: Result<usize, usize> = res.
| ___________________________________^
-LL | | // some lines
-LL | | // some lines
-LL | | // some lines
+LL | | // some lines
+LL | | // some lines
+LL | | // some lines
... |
-LL | | // some lines
-LL | | or_else(|_| Ok(ext_str.some_field));
- | |_________----------------------------------^
- | |
- | help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
+LL | | // some lines
+LL | | or_else(|_| Ok(ext_str.some_field));
+ | |_____----------------------------------^
+ | |
+ | help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
error: aborting due to 34 previous errors
DropStruct { ..get_drop_struct() };
DropEnum::Tuple(get_number());
DropEnum::Struct { field: get_number() };
+
+ // Issue #9954
+ fn one() -> i8 {
+ 1
+ }
+ macro_rules! use_expr {
+ ($($e:expr),*) => {{ $($e;)* }}
+ }
+ use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one());
}
DropStruct { ..get_drop_struct() };
DropEnum::Tuple(get_number());
DropEnum::Struct { field: get_number() };
+
+ // Issue #9954
+ fn one() -> i8 {
+ 1
+ }
+ macro_rules! use_expr {
+ ($($e:expr),*) => {{ $($e;)* }}
+ }
+ use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one());
}
--- /dev/null
+#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
+#![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
+
+mod unsafe_items_invalid_comment {
+ // SAFETY:
+ const CONST: u32 = 0;
+ // SAFETY:
+ static STATIC: u32 = 0;
+ // SAFETY:
+ struct Struct;
+ // SAFETY:
+ enum Enum {}
+ // SAFETY:
+ mod module {}
+}
+
+mod unnecessary_from_macro {
+ trait T {}
+
+ macro_rules! no_safety_comment {
+ ($t:ty) => {
+ impl T for $t {}
+ };
+ }
+
+ // FIXME: This is not caught
+ // Safety: unnecessary
+ no_safety_comment!(());
+
+ macro_rules! with_safety_comment {
+ ($t:ty) => {
+ // Safety: unnecessary
+ impl T for $t {}
+ };
+ }
+
+ with_safety_comment!(i32);
+}
+
+fn unnecessary_on_stmt_and_expr() -> u32 {
+ // SAFETY: unnecessary
+ let num = 42;
+
+ // SAFETY: unnecessary
+ if num > 24 {}
+
+ // SAFETY: unnecessary
+ 24
+}
+
+fn main() {}
--- /dev/null
+error: constant item has unnecessary safety comment
+ --> $DIR/unnecessary_safety_comment.rs:6:5
+ |
+LL | const CONST: u32 = 0;
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider removing the safety comment
+ --> $DIR/unnecessary_safety_comment.rs:5:5
+ |
+LL | // SAFETY:
+ | ^^^^^^^^^^
+ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings`
+
+error: static item has unnecessary safety comment
+ --> $DIR/unnecessary_safety_comment.rs:8:5
+ |
+LL | static STATIC: u32 = 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider removing the safety comment
+ --> $DIR/unnecessary_safety_comment.rs:7:5
+ |
+LL | // SAFETY:
+ | ^^^^^^^^^^
+
+error: struct has unnecessary safety comment
+ --> $DIR/unnecessary_safety_comment.rs:10:5
+ |
+LL | struct Struct;
+ | ^^^^^^^^^^^^^^
+ |
+help: consider removing the safety comment
+ --> $DIR/unnecessary_safety_comment.rs:9:5
+ |
+LL | // SAFETY:
+ | ^^^^^^^^^^
+
+error: enum has unnecessary safety comment
+ --> $DIR/unnecessary_safety_comment.rs:12:5
+ |
+LL | enum Enum {}
+ | ^^^^^^^^^^^^
+ |
+help: consider removing the safety comment
+ --> $DIR/unnecessary_safety_comment.rs:11:5
+ |
+LL | // SAFETY:
+ | ^^^^^^^^^^
+
+error: module has unnecessary safety comment
+ --> $DIR/unnecessary_safety_comment.rs:14:5
+ |
+LL | mod module {}
+ | ^^^^^^^^^^^^^
+ |
+help: consider removing the safety comment
+ --> $DIR/unnecessary_safety_comment.rs:13:5
+ |
+LL | // SAFETY:
+ | ^^^^^^^^^^
+
+error: impl has unnecessary safety comment
+ --> $DIR/unnecessary_safety_comment.rs:33:13
+ |
+LL | impl T for $t {}
+ | ^^^^^^^^^^^^^^^^
+...
+LL | with_safety_comment!(i32);
+ | ------------------------- in this macro invocation
+ |
+help: consider removing the safety comment
+ --> $DIR/unnecessary_safety_comment.rs:32:13
+ |
+LL | // Safety: unnecessary
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: expression has unnecessary safety comment
+ --> $DIR/unnecessary_safety_comment.rs:48:5
+ |
+LL | 24
+ | ^^
+ |
+help: consider removing the safety comment
+ --> $DIR/unnecessary_safety_comment.rs:47:5
+ |
+LL | // SAFETY: unnecessary
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: statement has unnecessary safety comment
+ --> $DIR/unnecessary_safety_comment.rs:42:5
+ |
+LL | let num = 42;
+ | ^^^^^^^^^^^^^
+ |
+help: consider removing the safety comment
+ --> $DIR/unnecessary_safety_comment.rs:41:5
+ |
+LL | // SAFETY: unnecessary
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: statement has unnecessary safety comment
+ --> $DIR/unnecessary_safety_comment.rs:45:5
+ |
+LL | if num > 24 {}
+ | ^^^^^^^^^^^^^^
+ |
+help: consider removing the safety comment
+ --> $DIR/unnecessary_safety_comment.rs:44:5
+ |
+LL | // SAFETY: unnecessary
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
#![allow(clippy::needless_borrow, clippy::ptr_arg)]
#![warn(clippy::unnecessary_to_owned)]
-#![feature(custom_inner_attributes)]
use std::borrow::Cow;
use std::ffi::{CStr, CString, OsStr, OsString};
fn require_string(_: &String) {}
+#[clippy::msrv = "1.35"]
fn _msrv_1_35() {
- #![clippy::msrv = "1.35"]
// `copied` was stabilized in 1.36, so clippy should use `cloned`.
let _ = &["x"][..].iter().cloned();
}
+#[clippy::msrv = "1.36"]
fn _msrv_1_36() {
- #![clippy::msrv = "1.36"]
let _ = &["x"][..].iter().copied();
}
foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
}
}
+
+mod issue_9771a {
+ #![allow(dead_code)]
+
+ use std::marker::PhantomData;
+
+ pub struct Key<K: AsRef<[u8]>, V: ?Sized>(K, PhantomData<V>);
+
+ impl<K: AsRef<[u8]>, V: ?Sized> Key<K, V> {
+ pub fn new(key: K) -> Key<K, V> {
+ Key(key, PhantomData)
+ }
+ }
+
+ pub fn pkh(pkh: &[u8]) -> Key<Vec<u8>, String> {
+ Key::new([b"pkh-", pkh].concat().to_vec())
+ }
+}
+
+mod issue_9771b {
+ #![allow(dead_code)]
+
+ pub struct Key<K: AsRef<[u8]>>(K);
+
+ pub fn from(c: &[u8]) -> Key<Vec<u8>> {
+ let v = [c].concat();
+ Key(v.to_vec())
+ }
+}
#![allow(clippy::needless_borrow, clippy::ptr_arg)]
#![warn(clippy::unnecessary_to_owned)]
-#![feature(custom_inner_attributes)]
use std::borrow::Cow;
use std::ffi::{CStr, CString, OsStr, OsString};
fn require_string(_: &String) {}
+#[clippy::msrv = "1.35"]
fn _msrv_1_35() {
- #![clippy::msrv = "1.35"]
// `copied` was stabilized in 1.36, so clippy should use `cloned`.
let _ = &["x"][..].to_vec().into_iter();
}
+#[clippy::msrv = "1.36"]
fn _msrv_1_36() {
- #![clippy::msrv = "1.36"]
let _ = &["x"][..].to_vec().into_iter();
}
foo(std::path::PathBuf::new().to_string_lossy().to_string()).await;
}
}
+
+mod issue_9771a {
+ #![allow(dead_code)]
+
+ use std::marker::PhantomData;
+
+ pub struct Key<K: AsRef<[u8]>, V: ?Sized>(K, PhantomData<V>);
+
+ impl<K: AsRef<[u8]>, V: ?Sized> Key<K, V> {
+ pub fn new(key: K) -> Key<K, V> {
+ Key(key, PhantomData)
+ }
+ }
+
+ pub fn pkh(pkh: &[u8]) -> Key<Vec<u8>, String> {
+ Key::new([b"pkh-", pkh].concat().to_vec())
+ }
+}
+
+mod issue_9771b {
+ #![allow(dead_code)]
+
+ pub struct Key<K: AsRef<[u8]>>(K);
+
+ pub fn from(c: &[u8]) -> Key<Vec<u8>> {
+ let v = [c].concat();
+ Key(v.to_vec())
+ }
+}
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:151:64
+ --> $DIR/unnecessary_to_owned.rs:150:64
|
LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
| ^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:151:20
+ --> $DIR/unnecessary_to_owned.rs:150:20
|
LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: `-D clippy::redundant-clone` implied by `-D warnings`
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:152:40
+ --> $DIR/unnecessary_to_owned.rs:151:40
|
LL | require_os_str(&OsString::from("x").to_os_string());
| ^^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:152:21
+ --> $DIR/unnecessary_to_owned.rs:151:21
|
LL | require_os_str(&OsString::from("x").to_os_string());
| ^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:153:48
+ --> $DIR/unnecessary_to_owned.rs:152:48
|
LL | require_path(&std::path::PathBuf::from("x").to_path_buf());
| ^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:153:19
+ --> $DIR/unnecessary_to_owned.rs:152:19
|
LL | require_path(&std::path::PathBuf::from("x").to_path_buf());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:154:35
+ --> $DIR/unnecessary_to_owned.rs:153:35
|
LL | require_str(&String::from("x").to_string());
| ^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:154:18
+ --> $DIR/unnecessary_to_owned.rs:153:18
|
LL | require_str(&String::from("x").to_string());
| ^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:155:39
+ --> $DIR/unnecessary_to_owned.rs:154:39
|
LL | require_slice(&[String::from("x")].to_owned());
| ^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:155:20
+ --> $DIR/unnecessary_to_owned.rs:154:20
|
LL | require_slice(&[String::from("x")].to_owned());
| ^^^^^^^^^^^^^^^^^^^
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:60:36
+ --> $DIR/unnecessary_to_owned.rs:59:36
|
LL | require_c_str(&Cow::from(c_str).into_owned());
| ^^^^^^^^^^^^^ help: remove this
= note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:61:19
+ --> $DIR/unnecessary_to_owned.rs:60:19
|
LL | require_c_str(&c_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_os_string`
- --> $DIR/unnecessary_to_owned.rs:63:20
+ --> $DIR/unnecessary_to_owned.rs:62:20
|
LL | require_os_str(&os_str.to_os_string());
| ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:64:38
+ --> $DIR/unnecessary_to_owned.rs:63:38
|
LL | require_os_str(&Cow::from(os_str).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:65:20
+ --> $DIR/unnecessary_to_owned.rs:64:20
|
LL | require_os_str(&os_str.to_owned());
| ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_path_buf`
- --> $DIR/unnecessary_to_owned.rs:67:18
+ --> $DIR/unnecessary_to_owned.rs:66:18
|
LL | require_path(&path.to_path_buf());
| ^^^^^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:68:34
+ --> $DIR/unnecessary_to_owned.rs:67:34
|
LL | require_path(&Cow::from(path).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:69:18
+ --> $DIR/unnecessary_to_owned.rs:68:18
|
LL | require_path(&path.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:71:17
+ --> $DIR/unnecessary_to_owned.rs:70:17
|
LL | require_str(&s.to_string());
| ^^^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:72:30
+ --> $DIR/unnecessary_to_owned.rs:71:30
|
LL | require_str(&Cow::from(s).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:73:17
+ --> $DIR/unnecessary_to_owned.rs:72:17
|
LL | require_str(&s.to_owned());
| ^^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:74:17
+ --> $DIR/unnecessary_to_owned.rs:73:17
|
LL | require_str(&x_ref.to_string());
| ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:76:19
+ --> $DIR/unnecessary_to_owned.rs:75:19
|
LL | require_slice(&slice.to_vec());
| ^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:77:36
+ --> $DIR/unnecessary_to_owned.rs:76:36
|
LL | require_slice(&Cow::from(slice).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:78:19
+ --> $DIR/unnecessary_to_owned.rs:77:19
|
LL | require_slice(&array.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:79:19
+ --> $DIR/unnecessary_to_owned.rs:78:19
|
LL | require_slice(&array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:80:19
+ --> $DIR/unnecessary_to_owned.rs:79:19
|
LL | require_slice(&slice.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:83:42
+ --> $DIR/unnecessary_to_owned.rs:82:42
|
LL | require_x(&Cow::<X>::Owned(x.clone()).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:86:25
+ --> $DIR/unnecessary_to_owned.rs:85:25
|
LL | require_deref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:87:26
+ --> $DIR/unnecessary_to_owned.rs:86:26
|
LL | require_deref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:88:24
+ --> $DIR/unnecessary_to_owned.rs:87:24
|
LL | require_deref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:89:23
+ --> $DIR/unnecessary_to_owned.rs:88:23
|
LL | require_deref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:90:25
+ --> $DIR/unnecessary_to_owned.rs:89:25
|
LL | require_deref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:92:30
+ --> $DIR/unnecessary_to_owned.rs:91:30
|
LL | require_impl_deref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:93:31
+ --> $DIR/unnecessary_to_owned.rs:92:31
|
LL | require_impl_deref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:94:29
+ --> $DIR/unnecessary_to_owned.rs:93:29
|
LL | require_impl_deref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:95:28
+ --> $DIR/unnecessary_to_owned.rs:94:28
|
LL | require_impl_deref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:96:30
+ --> $DIR/unnecessary_to_owned.rs:95:30
|
LL | require_impl_deref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:98:29
+ --> $DIR/unnecessary_to_owned.rs:97:29
|
LL | require_deref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:98:43
+ --> $DIR/unnecessary_to_owned.rs:97:43
|
LL | require_deref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:99:29
+ --> $DIR/unnecessary_to_owned.rs:98:29
|
LL | require_deref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:99:47
+ --> $DIR/unnecessary_to_owned.rs:98:47
|
LL | require_deref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:101:26
+ --> $DIR/unnecessary_to_owned.rs:100:26
|
LL | require_as_ref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:102:27
+ --> $DIR/unnecessary_to_owned.rs:101:27
|
LL | require_as_ref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:103:25
+ --> $DIR/unnecessary_to_owned.rs:102:25
|
LL | require_as_ref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:104:24
+ --> $DIR/unnecessary_to_owned.rs:103:24
|
LL | require_as_ref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:105:24
+ --> $DIR/unnecessary_to_owned.rs:104:24
|
LL | require_as_ref_str(x.to_owned());
| ^^^^^^^^^^^^ help: use: `&x`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:106:26
+ --> $DIR/unnecessary_to_owned.rs:105:26
|
LL | require_as_ref_slice(array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:107:26
+ --> $DIR/unnecessary_to_owned.rs:106:26
|
LL | require_as_ref_slice(array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:108:26
+ --> $DIR/unnecessary_to_owned.rs:107:26
|
LL | require_as_ref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:110:31
+ --> $DIR/unnecessary_to_owned.rs:109:31
|
LL | require_impl_as_ref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:111:32
+ --> $DIR/unnecessary_to_owned.rs:110:32
|
LL | require_impl_as_ref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:112:30
+ --> $DIR/unnecessary_to_owned.rs:111:30
|
LL | require_impl_as_ref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:113:29
+ --> $DIR/unnecessary_to_owned.rs:112:29
|
LL | require_impl_as_ref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:114:29
+ --> $DIR/unnecessary_to_owned.rs:113:29
|
LL | require_impl_as_ref_str(x.to_owned());
| ^^^^^^^^^^^^ help: use: `&x`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:115:31
+ --> $DIR/unnecessary_to_owned.rs:114:31
|
LL | require_impl_as_ref_slice(array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:116:31
+ --> $DIR/unnecessary_to_owned.rs:115:31
|
LL | require_impl_as_ref_slice(array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:117:31
+ --> $DIR/unnecessary_to_owned.rs:116:31
|
LL | require_impl_as_ref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:119:30
+ --> $DIR/unnecessary_to_owned.rs:118:30
|
LL | require_as_ref_str_slice(s.to_owned(), array.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:119:44
+ --> $DIR/unnecessary_to_owned.rs:118:44
|
LL | require_as_ref_str_slice(s.to_owned(), array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:120:30
+ --> $DIR/unnecessary_to_owned.rs:119:30
|
LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:120:44
+ --> $DIR/unnecessary_to_owned.rs:119:44
|
LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:121:30
+ --> $DIR/unnecessary_to_owned.rs:120:30
|
LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:121:44
+ --> $DIR/unnecessary_to_owned.rs:120:44
|
LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:122:30
+ --> $DIR/unnecessary_to_owned.rs:121:30
|
LL | require_as_ref_slice_str(array.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:122:48
+ --> $DIR/unnecessary_to_owned.rs:121:48
|
LL | require_as_ref_slice_str(array.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:123:30
+ --> $DIR/unnecessary_to_owned.rs:122:30
|
LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:123:52
+ --> $DIR/unnecessary_to_owned.rs:122:52
|
LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:124:30
+ --> $DIR/unnecessary_to_owned.rs:123:30
|
LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:124:48
+ --> $DIR/unnecessary_to_owned.rs:123:48
|
LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:126:20
+ --> $DIR/unnecessary_to_owned.rs:125:20
|
LL | let _ = x.join(&x_ref.to_string());
| ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:128:13
+ --> $DIR/unnecessary_to_owned.rs:127:13
|
LL | let _ = slice.to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:129:13
+ --> $DIR/unnecessary_to_owned.rs:128:13
|
LL | let _ = slice.to_owned().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:130:13
+ --> $DIR/unnecessary_to_owned.rs:129:13
|
LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:131:13
+ --> $DIR/unnecessary_to_owned.rs:130:13
|
LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:133:13
+ --> $DIR/unnecessary_to_owned.rs:132:13
|
LL | let _ = IntoIterator::into_iter(slice.to_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:134:13
+ --> $DIR/unnecessary_to_owned.rs:133:13
|
LL | let _ = IntoIterator::into_iter(slice.to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:135:13
+ --> $DIR/unnecessary_to_owned.rs:134:13
|
LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:136:13
+ --> $DIR/unnecessary_to_owned.rs:135:13
|
LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:198:14
+ --> $DIR/unnecessary_to_owned.rs:197:14
|
LL | for t in file_types.to_vec() {
| ^^^^^^^^^^^^^^^^^^^
|
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:221:14
+ --> $DIR/unnecessary_to_owned.rs:220:14
|
LL | let _ = &["x"][..].to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:226:14
+ --> $DIR/unnecessary_to_owned.rs:225:14
|
LL | let _ = &["x"][..].to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:273:24
+ --> $DIR/unnecessary_to_owned.rs:272:24
|
LL | Box::new(build(y.to_string()))
| ^^^^^^^^^^^^^ help: use: `y`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:381:12
+ --> $DIR/unnecessary_to_owned.rs:380:12
|
LL | id("abc".to_string())
| ^^^^^^^^^^^^^^^^^ help: use: `"abc"`
--- /dev/null
+// aux-build:doc_unsafe_macros.rs
+
+#![allow(clippy::let_unit_value)]
+#![warn(clippy::unnecessary_safety_doc)]
+
+#[macro_use]
+extern crate doc_unsafe_macros;
+
+/// This is has no safety section, and does not need one either
+pub fn destroy_the_planet() {
+ unimplemented!();
+}
+
+/// This one does not need a `Safety` section
+///
+/// # Safety
+///
+/// This function shouldn't be called unless the horsemen are ready
+pub fn apocalypse(universe: &mut ()) {
+ unimplemented!();
+}
+
+/// This is a private function, skip to match behavior with `missing_safety_doc`.
+///
+/// # Safety
+///
+/// Boo!
+fn you_dont_see_me() {
+ unimplemented!();
+}
+
+mod private_mod {
+ /// This is public but unexported function, skip to match behavior with `missing_safety_doc`.
+ ///
+ /// # Safety
+ ///
+ /// Very safe!
+ pub fn only_crate_wide_accessible() {
+ unimplemented!();
+ }
+
+ /// # Safety
+ ///
+ /// Unnecessary safety!
+ pub fn republished() {
+ unimplemented!();
+ }
+}
+
+pub use private_mod::republished;
+
+pub trait SafeTraitSafeMethods {
+ fn woefully_underdocumented(self);
+
+ /// # Safety
+ ///
+ /// Unnecessary!
+ fn documented(self);
+}
+
+pub trait SafeTrait {
+ fn method();
+}
+
+/// # Safety
+///
+/// Unnecessary!
+pub trait DocumentedSafeTrait {
+ fn method2();
+}
+
+pub struct Struct;
+
+impl SafeTraitSafeMethods for Struct {
+ fn woefully_underdocumented(self) {
+ // all is well
+ }
+
+ fn documented(self) {
+ // all is still well
+ }
+}
+
+impl SafeTrait for Struct {
+ fn method() {}
+}
+
+impl DocumentedSafeTrait for Struct {
+ fn method2() {}
+}
+
+impl Struct {
+ /// # Safety
+ ///
+ /// Unnecessary!
+ pub fn documented() -> Self {
+ unimplemented!();
+ }
+
+ pub fn undocumented(&self) {
+ unimplemented!();
+ }
+
+ /// Private, fine again to stay consistent with `missing_safety_doc`.
+ ///
+ /// # Safety
+ ///
+ /// Unnecessary!
+ fn private(&self) {
+ unimplemented!();
+ }
+}
+
+macro_rules! very_safe {
+ () => {
+ pub fn whee() {
+ unimplemented!()
+ }
+
+ /// # Safety
+ ///
+ /// Driving is very safe already!
+ pub fn drive() {
+ whee()
+ }
+ };
+}
+
+very_safe!();
+
+// we don't lint code from external macros
+undocd_safe!();
+
+fn main() {}
+
+// do not lint if any parent has `#[doc(hidden)]` attribute
+// see #7347
+#[doc(hidden)]
+pub mod __macro {
+ pub struct T;
+ impl T {
+ pub unsafe fn f() {}
+ }
+}
+
+/// # Implementation safety
+pub trait DocumentedSafeTraitWithImplementationHeader {
+ fn method();
+}
--- /dev/null
+error: safe function's docs have unnecessary `# Safety` section
+ --> $DIR/unnecessary_unsafety_doc.rs:19:1
+ |
+LL | pub fn apocalypse(universe: &mut ()) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unnecessary-safety-doc` implied by `-D warnings`
+
+error: safe function's docs have unnecessary `# Safety` section
+ --> $DIR/unnecessary_unsafety_doc.rs:45:5
+ |
+LL | pub fn republished() {
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: safe function's docs have unnecessary `# Safety` section
+ --> $DIR/unnecessary_unsafety_doc.rs:58:5
+ |
+LL | fn documented(self);
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: docs for safe trait have unnecessary `# Safety` section
+ --> $DIR/unnecessary_unsafety_doc.rs:68:1
+ |
+LL | pub trait DocumentedSafeTrait {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: safe function's docs have unnecessary `# Safety` section
+ --> $DIR/unnecessary_unsafety_doc.rs:96:5
+ |
+LL | pub fn documented() -> Self {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: safe function's docs have unnecessary `# Safety` section
+ --> $DIR/unnecessary_unsafety_doc.rs:123:9
+ |
+LL | pub fn drive() {
+ | ^^^^^^^^^^^^^^
+...
+LL | very_safe!();
+ | ------------ in this macro invocation
+ |
+ = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: docs for safe trait have unnecessary `# Safety` section
+ --> $DIR/unnecessary_unsafety_doc.rs:147:1
+ |
+LL | pub trait DocumentedSafeTraitWithImplementationHeader {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
// run-rustfix
-#![feature(box_patterns, custom_inner_attributes)]
+#![feature(box_patterns)]
#![warn(clippy::unnested_or_patterns)]
#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
#![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
}
+#[clippy::msrv = "1.52"]
fn msrv_1_52() {
- #![clippy::msrv = "1.52"]
-
if let [1] | [52] = [0] {}
}
+#[clippy::msrv = "1.53"]
fn msrv_1_53() {
- #![clippy::msrv = "1.53"]
-
if let [1 | 53] = [0] {}
}
// run-rustfix
-#![feature(box_patterns, custom_inner_attributes)]
+#![feature(box_patterns)]
#![warn(clippy::unnested_or_patterns)]
#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)]
#![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
}
+#[clippy::msrv = "1.52"]
fn msrv_1_52() {
- #![clippy::msrv = "1.52"]
-
if let [1] | [52] = [0] {}
}
+#[clippy::msrv = "1.53"]
fn msrv_1_53() {
- #![clippy::msrv = "1.53"]
-
if let [1] | [53] = [0] {}
}
| ~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:46:12
+ --> $DIR/unnested_or_patterns.rs:44:12
|
LL | if let [1] | [53] = [0] {}
| ^^^^^^^^^^
let _ = 3.3_f32.round();
let _ = 3.3_f64.round();
let _ = 3.0_f32;
+
+ let _ = 3_3.0_0_f32;
+ let _ = 3_3.0_1_f64.round();
}
let _ = 3.3_f32.round();
let _ = 3.3_f64.round();
let _ = 3.0_f32.round();
+
+ let _ = 3_3.0_0_f32.round();
+ let _ = 3_3.0_1_f64.round();
}
LL | let _ = 3.0_f32.round();
| ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32`
-error: aborting due to 4 previous errors
+error: used the `round` method with a whole number float
+ --> $DIR/unused_rounding.rs:15:13
+ |
+LL | let _ = 3_3.0_0_f32.round();
+ | ^^^^^^^^^^^^^^^^^^^ help: remove the `round` method call: `3_3.0_0_f32`
+
+error: aborting due to 5 previous errors
// run-rustfix
// aux-build:proc_macro_derive.rs
-#![feature(custom_inner_attributes)]
#![warn(clippy::use_self)]
#![allow(dead_code, unreachable_code)]
#![allow(
}
}
+#[clippy::msrv = "1.36"]
fn msrv_1_36() {
- #![clippy::msrv = "1.36"]
-
enum E {
A,
}
}
}
+#[clippy::msrv = "1.37"]
fn msrv_1_37() {
- #![clippy::msrv = "1.37"]
-
enum E {
A,
}
// run-rustfix
// aux-build:proc_macro_derive.rs
-#![feature(custom_inner_attributes)]
#![warn(clippy::use_self)]
#![allow(dead_code, unreachable_code)]
#![allow(
}
}
+#[clippy::msrv = "1.36"]
fn msrv_1_36() {
- #![clippy::msrv = "1.36"]
-
enum E {
A,
}
}
}
+#[clippy::msrv = "1.37"]
fn msrv_1_37() {
- #![clippy::msrv = "1.37"]
-
enum E {
A,
}
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:23:21
+ --> $DIR/use_self.rs:22:21
|
LL | fn new() -> Foo {
| ^^^ help: use the applicable keyword: `Self`
= note: `-D clippy::use-self` implied by `-D warnings`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:24:13
+ --> $DIR/use_self.rs:23:13
|
LL | Foo {}
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:26:22
+ --> $DIR/use_self.rs:25:22
|
LL | fn test() -> Foo {
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:27:13
+ --> $DIR/use_self.rs:26:13
|
LL | Foo::new()
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:32:25
+ --> $DIR/use_self.rs:31:25
|
LL | fn default() -> Foo {
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:33:13
+ --> $DIR/use_self.rs:32:13
|
LL | Foo::new()
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:98:24
+ --> $DIR/use_self.rs:97:24
|
LL | fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:98:55
+ --> $DIR/use_self.rs:97:55
|
LL | fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:113:13
+ --> $DIR/use_self.rs:112:13
|
LL | TS(0)
| ^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:148:29
+ --> $DIR/use_self.rs:147:29
|
LL | fn bar() -> Bar {
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:149:21
+ --> $DIR/use_self.rs:148:21
|
LL | Bar { foo: Foo {} }
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:160:21
+ --> $DIR/use_self.rs:159:21
|
LL | fn baz() -> Foo {
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:161:13
+ --> $DIR/use_self.rs:160:13
|
LL | Foo {}
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:178:21
+ --> $DIR/use_self.rs:177:21
|
LL | let _ = Enum::B(42);
| ^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:179:21
+ --> $DIR/use_self.rs:178:21
|
LL | let _ = Enum::C { field: true };
| ^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:180:21
+ --> $DIR/use_self.rs:179:21
|
LL | let _ = Enum::A;
| ^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:222:13
+ --> $DIR/use_self.rs:221:13
|
LL | nested::A::fun_1();
| ^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:223:13
+ --> $DIR/use_self.rs:222:13
|
LL | nested::A::A;
| ^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:225:13
+ --> $DIR/use_self.rs:224:13
|
LL | nested::A {};
| ^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:244:13
+ --> $DIR/use_self.rs:243:13
|
LL | TestStruct::from_something()
| ^^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:258:25
+ --> $DIR/use_self.rs:257:25
|
LL | async fn g() -> S {
| ^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:259:13
+ --> $DIR/use_self.rs:258:13
|
LL | S {}
| ^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:263:16
+ --> $DIR/use_self.rs:262:16
|
LL | &p[S::A..S::B]
| ^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:263:22
+ --> $DIR/use_self.rs:262:22
|
LL | &p[S::A..S::B]
| ^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:286:29
+ --> $DIR/use_self.rs:285:29
|
LL | fn foo(value: T) -> Foo<T> {
| ^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:287:13
+ --> $DIR/use_self.rs:286:13
|
LL | Foo::<T> { value }
| ^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:459:13
+ --> $DIR/use_self.rs:458:13
|
LL | A::new::<submod::B>(submod::B {})
| ^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:496:13
+ --> $DIR/use_self.rs:495:13
|
LL | S2::new()
| ^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:533:17
+ --> $DIR/use_self.rs:532:17
|
LL | Foo::Bar => unimplemented!(),
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:534:17
+ --> $DIR/use_self.rs:533:17
|
LL | Foo::Baz => unimplemented!(),
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:540:20
+ --> $DIR/use_self.rs:539:20
|
LL | if let Foo::Bar = self {
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:564:17
+ --> $DIR/use_self.rs:563:17
|
LL | Something::Num(n) => *n,
| ^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:565:17
+ --> $DIR/use_self.rs:564:17
|
LL | Something::TupleNums(n, _m) => *n,
| ^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:566:17
+ --> $DIR/use_self.rs:565:17
|
LL | Something::StructNums { one, two: _ } => *one,
| ^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:572:17
+ --> $DIR/use_self.rs:571:17
|
LL | crate::issue8845::Something::Num(n) => *n,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:573:17
+ --> $DIR/use_self.rs:572:17
|
LL | crate::issue8845::Something::TupleNums(n, _m) => *n,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:574:17
+ --> $DIR/use_self.rs:573:17
|
LL | crate::issue8845::Something::StructNums { one, two: _ } => *one,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:590:17
+ --> $DIR/use_self.rs:589:17
|
LL | let Foo(x) = self;
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:595:17
+ --> $DIR/use_self.rs:594:17
|
LL | let crate::issue8845::Foo(x) = self;
| ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:602:17
+ --> $DIR/use_self.rs:601:17
|
LL | let Bar { x, .. } = self;
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:607:17
+ --> $DIR/use_self.rs:606:17
|
LL | let crate::issue8845::Bar { x, .. } = self;
| ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
- --> $DIR/use_self.rs:648:17
+ --> $DIR/use_self.rs:645:17
|
LL | E::A => {},
| ^ help: use the applicable keyword: `Self`
"good-first-issue"
]
-[assign]
-
# Allows shortcuts like `@rustbot ready`
#
# See https://github.com/rust-lang/triagebot/wiki/Shortcuts
[shortcut]
+
+[autolabel."S-waiting-on-review"]
+new_pr = true
+
+[assign]
+contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
+
+[assign.owners]
+"/.github" = ["@flip1995"]
+"*" = [
+ "@flip1995",
+ "@Manishearth",
+ "@llogiq",
+ "@giraffate",
+ "@xFrednet",
+ "@Alexendoo",
+ "@dswij",
+ "@Jarcho",
+]
--- /dev/null
+[package]
+name = "collect-license-metadata"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.65"
+serde = { version = "1.0.147", features = ["derive"] }
+serde_json = "1.0.85"
+spdx-rs = "0.5.1"
--- /dev/null
+use std::collections::HashMap;
+
+const COPYRIGHT_PREFIXES: &[&str] = &["SPDX-FileCopyrightText:", "Copyright", "(c)", "(C)", "©"];
+
+pub(crate) struct LicensesInterner {
+ by_id: Vec<License>,
+ by_struct: HashMap<License, usize>,
+}
+
+impl LicensesInterner {
+ pub(crate) fn new() -> Self {
+ LicensesInterner { by_id: Vec::new(), by_struct: HashMap::new() }
+ }
+
+ pub(crate) fn intern(&mut self, mut license: License) -> LicenseId {
+ license.simplify();
+ if let Some(id) = self.by_struct.get(&license) {
+ LicenseId(*id)
+ } else {
+ let id = self.by_id.len();
+ self.by_id.push(license.clone());
+ self.by_struct.insert(license, id);
+ LicenseId(id)
+ }
+ }
+
+ pub(crate) fn resolve(&self, id: LicenseId) -> &License {
+ &self.by_id[id.0]
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
+#[serde(transparent)]
+pub(crate) struct LicenseId(usize);
+
+#[derive(Clone, Hash, PartialEq, Eq, serde::Serialize)]
+pub(crate) struct License {
+ pub(crate) spdx: String,
+ pub(crate) copyright: Vec<String>,
+}
+
+impl License {
+ fn simplify(&mut self) {
+ self.remove_copyright_prefixes();
+ self.copyright.sort();
+ self.copyright.dedup();
+ }
+
+ fn remove_copyright_prefixes(&mut self) {
+ for copyright in &mut self.copyright {
+ let mut stripped = copyright.trim();
+ let mut previous_stripped;
+ loop {
+ previous_stripped = stripped;
+ for pattern in COPYRIGHT_PREFIXES {
+ stripped = stripped.trim_start_matches(pattern).trim_start();
+ }
+ if stripped == previous_stripped {
+ break;
+ }
+ }
+ *copyright = stripped.into();
+ }
+ }
+}
--- /dev/null
+mod licenses;
+mod path_tree;
+mod reuse;
+
+use crate::licenses::LicensesInterner;
+use anyhow::Error;
+use std::path::PathBuf;
+
+fn main() -> Result<(), Error> {
+ let reuse_exe: PathBuf = std::env::var_os("REUSE_EXE").expect("Missing REUSE_EXE").into();
+ let dest: PathBuf = std::env::var_os("DEST").expect("Missing DEST").into();
+
+ let mut interner = LicensesInterner::new();
+ let paths = crate::reuse::collect(&reuse_exe, &mut interner)?;
+
+ let mut tree = crate::path_tree::build(paths);
+ tree.simplify();
+
+ if let Some(parent) = dest.parent() {
+ std::fs::create_dir_all(parent)?;
+ }
+ std::fs::write(
+ &dest,
+ &serde_json::to_vec_pretty(&serde_json::json!({
+ "files": crate::path_tree::expand_interned_licenses(tree, &interner),
+ }))?,
+ )?;
+
+ Ok(())
+}
--- /dev/null
+//! Tools like REUSE output per-file licensing information, but we need to condense it in the
+//! minimum amount of data that still represents the same licensing metadata. This module is
+//! responsible for that, by turning the list of paths into a tree and executing simplification
+//! passes over the tree to remove redundant information.
+
+use crate::licenses::{License, LicenseId, LicensesInterner};
+use std::collections::BTreeMap;
+use std::path::{Path, PathBuf};
+
+#[derive(serde::Serialize)]
+#[serde(rename_all = "kebab-case", tag = "type")]
+pub(crate) enum Node<L> {
+ Root { childs: Vec<Node<L>> },
+ Directory { name: PathBuf, childs: Vec<Node<L>>, license: Option<L> },
+ File { name: PathBuf, license: L },
+ FileGroup { names: Vec<PathBuf>, license: L },
+ Empty,
+}
+
+impl Node<LicenseId> {
+ pub(crate) fn simplify(&mut self) {
+ self.merge_directories();
+ self.collapse_in_licensed_directories();
+ self.merge_directory_licenses();
+ self.merge_file_groups();
+ self.remove_empty();
+ }
+
+ /// Initially, the build() function constructs a list of separate paths from the file
+ /// system root down to each file, like so:
+ ///
+ /// ```text
+ /// ┌─► ./ ──► compiler/ ──► rustc/ ──► src/ ──► main.rs
+ /// │
+ /// <root> ─┼─► ./ ──► compiler/ ──► rustc/ ──► Cargo.toml
+ /// │
+ /// └─► ./ ──► library/ ───► std/ ──► Cargo.toml
+ /// ```
+ ///
+ /// This pass is responsible for turning that into a proper directory tree:
+ ///
+ /// ```text
+ /// ┌─► compiler/ ──► rustc/ ──┬─► src/ ──► main.rs
+ /// │ │
+ /// <root> ──► ./ ──┤ └─► Cargo.toml
+ /// │
+ /// └─► library/ ───► std/ ──► Cargo.toml
+ /// ```
+ fn merge_directories(&mut self) {
+ match self {
+ Node::Root { childs } | Node::Directory { childs, license: None, .. } => {
+ let mut directories = BTreeMap::new();
+ let mut files = Vec::new();
+
+ for child in childs.drain(..) {
+ match child {
+ Node::Directory { name, mut childs, license: None } => {
+ directories.entry(name).or_insert_with(Vec::new).append(&mut childs);
+ }
+ file @ Node::File { .. } => {
+ files.push(file);
+ }
+ Node::Empty => {}
+ Node::Root { .. } => {
+ panic!("can't have a root inside another element");
+ }
+ Node::FileGroup { .. } => {
+ panic!("FileGroup should not be present at this stage");
+ }
+ Node::Directory { license: Some(_), .. } => {
+ panic!("license should not be set at this stage");
+ }
+ }
+ }
+
+ childs.extend(directories.into_iter().map(|(name, childs)| Node::Directory {
+ name,
+ childs,
+ license: None,
+ }));
+ childs.append(&mut files);
+
+ for child in &mut *childs {
+ child.merge_directories();
+ }
+ }
+ Node::Empty => {}
+ Node::File { .. } => {}
+ Node::FileGroup { .. } => {
+ panic!("FileGroup should not be present at this stage");
+ }
+ Node::Directory { license: Some(_), .. } => {
+ panic!("license should not be set at this stage");
+ }
+ }
+ }
+
+ /// In our codebase, most files in a directory have the same license as the other files in that
+ /// same directory, so it's redundant to store licensing metadata for all the files. Instead,
+ /// we can add a license for a whole directory, and only record the exceptions to a directory
+ /// licensing metadata.
+ ///
+ /// We cannot instead record only the difference to Rust's standard licensing, as the majority
+ /// of the files in our repository are *not* licensed under Rust's standard licensing due to
+ /// our inclusion of LLVM.
+ fn collapse_in_licensed_directories(&mut self) {
+ match self {
+ Node::Directory { childs, license, .. } => {
+ for child in &mut *childs {
+ child.collapse_in_licensed_directories();
+ }
+
+ let mut licenses_count = BTreeMap::new();
+ for child in &*childs {
+ let Some(license) = child.license() else { continue };
+ *licenses_count.entry(license).or_insert(0) += 1;
+ }
+
+ let most_popular_license = licenses_count
+ .into_iter()
+ .max_by_key(|(_, count)| *count)
+ .map(|(license, _)| license);
+
+ if let Some(most_popular_license) = most_popular_license {
+ childs.retain(|child| child.license() != Some(most_popular_license));
+ *license = Some(most_popular_license);
+ }
+ }
+ Node::Root { childs } => {
+ for child in &mut *childs {
+ child.collapse_in_licensed_directories();
+ }
+ }
+ Node::File { .. } => {}
+ Node::FileGroup { .. } => {}
+ Node::Empty => {}
+ }
+ }
+
+ /// Reduce the depth of the tree by merging subdirectories with the same license as their
+ /// parent directory into their parent, and adjusting the paths of the childs accordingly.
+ fn merge_directory_licenses(&mut self) {
+ match self {
+ Node::Root { childs } => {
+ for child in &mut *childs {
+ child.merge_directory_licenses();
+ }
+ }
+ Node::Directory { childs, license, .. } => {
+ let mut to_add = Vec::new();
+ for child in &mut *childs {
+ child.merge_directory_licenses();
+
+ let Node::Directory {
+ name: child_name,
+ childs: child_childs,
+ license: child_license,
+ } = child else { continue };
+
+ if child_license != license {
+ continue;
+ }
+ for mut child_child in child_childs.drain(..) {
+ match &mut child_child {
+ Node::Root { .. } => {
+ panic!("can't have a root inside another element");
+ }
+ Node::FileGroup { .. } => {
+ panic!("FileGroup should not be present at this stage");
+ }
+ Node::Directory { name: child_child_name, .. } => {
+ *child_child_name = child_name.join(&child_child_name);
+ }
+ Node::File { name: child_child_name, .. } => {
+ *child_child_name = child_name.join(&child_child_name);
+ }
+ Node::Empty => {}
+ }
+ to_add.push(child_child);
+ }
+
+ *child = Node::Empty;
+ }
+ childs.append(&mut to_add);
+ }
+ Node::Empty => {}
+ Node::File { .. } => {}
+ Node::FileGroup { .. } => {}
+ }
+ }
+
+ /// This pass groups multiple files in a directory with the same license into a single
+ /// "FileGroup", so that the license of all those files can be reported as a group.
+ ///
+ /// Crucially this pass runs after collapse_in_licensed_directories, so the most common license
+ /// will already be marked as the directory's license and won't be turned into a group.
+ fn merge_file_groups(&mut self) {
+ match self {
+ Node::Root { childs } | Node::Directory { childs, .. } => {
+ let mut grouped = BTreeMap::new();
+
+ for child in &mut *childs {
+ child.merge_file_groups();
+ if let Node::File { name, license } = child {
+ grouped.entry(*license).or_insert_with(Vec::new).push(name.clone());
+ *child = Node::Empty;
+ }
+ }
+
+ for (license, mut names) in grouped.into_iter() {
+ if names.len() == 1 {
+ childs.push(Node::File { license, name: names.pop().unwrap() });
+ } else {
+ childs.push(Node::FileGroup { license, names });
+ }
+ }
+ }
+ Node::File { .. } => {}
+ Node::FileGroup { .. } => panic!("FileGroup should not be present at this stage"),
+ Node::Empty => {}
+ }
+ }
+
+ /// Some nodes were replaced with Node::Empty to mark them for deletion. As the last step, make
+ /// sure to remove them from the tree.
+ fn remove_empty(&mut self) {
+ match self {
+ Node::Root { childs } | Node::Directory { childs, .. } => {
+ for child in &mut *childs {
+ child.remove_empty();
+ }
+ childs.retain(|child| !matches!(child, Node::Empty));
+ }
+ Node::FileGroup { .. } => {}
+ Node::File { .. } => {}
+ Node::Empty => {}
+ }
+ }
+
+ fn license(&self) -> Option<LicenseId> {
+ match self {
+ Node::Directory { childs, license: Some(license), .. } if childs.is_empty() => {
+ Some(*license)
+ }
+ Node::File { license, .. } => Some(*license),
+ _ => None,
+ }
+ }
+}
+
+pub(crate) fn build(mut input: Vec<(PathBuf, LicenseId)>) -> Node<LicenseId> {
+ let mut childs = Vec::new();
+
+ // Ensure reproducibility of all future steps.
+ input.sort();
+
+ for (path, license) in input {
+ let mut node = Node::File { name: path.file_name().unwrap().into(), license };
+ for component in path.parent().unwrap_or_else(|| Path::new(".")).components().rev() {
+ node = Node::Directory {
+ name: component.as_os_str().into(),
+ childs: vec![node],
+ license: None,
+ };
+ }
+
+ childs.push(node);
+ }
+
+ Node::Root { childs }
+}
+
+/// Convert a `Node<LicenseId>` into a `Node<&License>`, expanding all interned license IDs with a
+/// reference to the actual license metadata.
+pub(crate) fn expand_interned_licenses(
+ node: Node<LicenseId>,
+ interner: &LicensesInterner,
+) -> Node<&License> {
+ match node {
+ Node::Root { childs } => Node::Root {
+ childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(),
+ },
+ Node::Directory { name, childs, license } => Node::Directory {
+ childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(),
+ license: license.map(|license| interner.resolve(license)),
+ name,
+ },
+ Node::File { name, license } => Node::File { name, license: interner.resolve(license) },
+ Node::FileGroup { names, license } => {
+ Node::FileGroup { names, license: interner.resolve(license) }
+ }
+ Node::Empty => Node::Empty,
+ }
+}
--- /dev/null
+use crate::licenses::{License, LicenseId, LicensesInterner};
+use anyhow::Error;
+use std::path::{Path, PathBuf};
+use std::process::{Command, Stdio};
+use std::time::Instant;
+
+pub(crate) fn collect(
+ reuse_exe: &Path,
+ interner: &mut LicensesInterner,
+) -> Result<Vec<(PathBuf, LicenseId)>, Error> {
+ eprintln!("gathering license information from REUSE");
+ let start = Instant::now();
+ let raw = &obtain_spdx_document(reuse_exe)?;
+ eprintln!("finished gathering the license information from REUSE in {:.2?}", start.elapsed());
+
+ let document = spdx_rs::parsers::spdx_from_tag_value(&raw)?;
+
+ let mut result = Vec::new();
+ for file in document.file_information {
+ let license = interner.intern(License {
+ spdx: file.concluded_license.to_string(),
+ copyright: file.copyright_text.split('\n').map(|s| s.into()).collect(),
+ });
+
+ result.push((file.file_name.into(), license));
+ }
+
+ Ok(result)
+}
+
+fn obtain_spdx_document(reuse_exe: &Path) -> Result<String, Error> {
+ let output = Command::new(reuse_exe)
+ .args(&["spdx", "--add-license-concluded", "--creator-person=bors"])
+ .stdout(Stdio::piped())
+ .spawn()?
+ .wait_with_output()?;
+
+ if !output.status.success() {
+ eprintln!();
+ eprintln!("Note that Rust requires some REUSE features that might not be present in the");
+ eprintln!("release you're using. Make sure your REUSE release includes these PRs:");
+ eprintln!();
+ eprintln!(" - https://github.com/fsfe/reuse-tool/pull/623");
+ eprintln!();
+ anyhow::bail!("collecting licensing information with REUSE failed");
+ }
+
+ Ok(String::from_utf8(output.stdout)?)
+}
props.load_from(testfile, cfg, config);
match (props.pass_mode, props.fail_mode) {
- (None, None) => props.fail_mode = Some(FailMode::Check),
- (Some(_), None) | (None, Some(_)) => {}
+ (None, None) if config.mode == Mode::Ui => props.fail_mode = Some(FailMode::Check),
(Some(_), Some(_)) => panic!("cannot use a *-fail and *-pass mode together"),
+ _ => {}
}
props
}
pub fn pass_mode(&self, config: &Config) -> Option<PassMode> {
- if !self.ignore_pass && self.fail_mode.is_none() && config.mode == Mode::Ui {
- if let (mode @ Some(_), Some(_)) = (config.force_pass_mode, self.pass_mode) {
+ if !self.ignore_pass && self.fail_mode.is_none() {
+ if let mode @ Some(_) = config.force_pass_mode {
return mode;
}
}
let has_asm_support = config.has_asm_support();
let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_cfi = util::CFI_SUPPORTED_TARGETS.contains(&&*config.target);
+ let has_kcfi = util::KCFI_SUPPORTED_TARGETS.contains(&&*config.target);
let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
&& config.parse_name_directive(ln, "needs-sanitizer-support");
ignore |= !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address");
ignore |= !has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi");
+ ignore |= !has_kcfi && config.parse_name_directive(ln, "needs-sanitizer-kcfi");
ignore |= !has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak");
ignore |= !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory");
ignore |= !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread");
options: test::Options::new(),
time_options: None,
force_run_in_process: false,
+ fail_fast: std::env::var_os("RUSTC_TEST_FAIL_FAST").is_some(),
}
}
option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from),
// Virtual `/rustc/$sha` coming from download-rustc:
std::env::var_os("FAKE_DOWNLOAD_RUSTC_PREFIX").map(PathBuf::from),
+ // Tests using -Zsimulate-remapped-rust-src-base should use this fake path
+ Some("/rustc/FAKE_PREFIX".into()),
];
for base_dir in source_bases {
if let Some(base_dir) = base_dir {
"x86_64-unknown-netbsd",
];
+pub const KCFI_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-none", "x86_64-linux-none"];
+
pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[
// FIXME: currently broken, see #88132
// "aarch64-apple-darwin",
--- /dev/null
+[package]
+name = "generate-copyright"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1.0.65"
+serde = { version = "1.0.147", features = ["derive"] }
+serde_json = "1.0.85"
--- /dev/null
+use anyhow::Error;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() -> Result<(), Error> {
+ let dest = env_path("DEST")?;
+ let license_metadata = env_path("LICENSE_METADATA")?;
+
+ let metadata: Metadata = serde_json::from_slice(&std::fs::read(&license_metadata)?)?;
+
+ let mut buffer = Vec::new();
+ render_recursive(&metadata.files, &mut buffer, 0)?;
+
+ std::fs::write(&dest, &buffer)?;
+
+ Ok(())
+}
+
+fn render_recursive(node: &Node, buffer: &mut Vec<u8>, depth: usize) -> Result<(), Error> {
+ let prefix = std::iter::repeat("> ").take(depth + 1).collect::<String>();
+
+ match node {
+ Node::Root { childs } => {
+ for child in childs {
+ render_recursive(child, buffer, depth)?;
+ }
+ }
+ Node::Directory { name, childs, license } => {
+ render_license(&prefix, std::iter::once(name), license, buffer)?;
+ if !childs.is_empty() {
+ writeln!(buffer, "{prefix}")?;
+ writeln!(buffer, "{prefix}*Exceptions:*")?;
+ for child in childs {
+ writeln!(buffer, "{prefix}")?;
+ render_recursive(child, buffer, depth + 1)?;
+ }
+ }
+ }
+ Node::FileGroup { names, license } => {
+ render_license(&prefix, names.iter(), license, buffer)?;
+ }
+ Node::File { name, license } => {
+ render_license(&prefix, std::iter::once(name), license, buffer)?;
+ }
+ }
+
+ Ok(())
+}
+
+fn render_license<'a>(
+ prefix: &str,
+ names: impl Iterator<Item = &'a String>,
+ license: &License,
+ buffer: &mut Vec<u8>,
+) -> Result<(), Error> {
+ for name in names {
+ writeln!(buffer, "{prefix}**`{name}`** ")?;
+ }
+ writeln!(buffer, "{prefix}License: `{}` ", license.spdx)?;
+ for (i, copyright) in license.copyright.iter().enumerate() {
+ let suffix = if i == license.copyright.len() - 1 { "" } else { " " };
+ writeln!(buffer, "{prefix}Copyright: {copyright}{suffix}")?;
+ }
+
+ Ok(())
+}
+
+#[derive(serde::Deserialize)]
+struct Metadata {
+ files: Node,
+}
+
+#[derive(serde::Deserialize)]
+#[serde(rename_all = "kebab-case", tag = "type")]
+pub(crate) enum Node {
+ Root { childs: Vec<Node> },
+ Directory { name: String, childs: Vec<Node>, license: License },
+ File { name: String, license: License },
+ FileGroup { names: Vec<String>, license: License },
+}
+
+#[derive(serde::Deserialize)]
+struct License {
+ spdx: String,
+ copyright: Vec<String>,
+}
+
+fn env_path(var: &str) -> Result<PathBuf, Error> {
+ if let Some(var) = std::env::var_os(var) {
+ Ok(var.into())
+ } else {
+ anyhow::bail!("missing environment variable {var}")
+ }
+}
fn check_item(&mut self, id: &'a Id) {
if let Some(item) = &self.krate.index.get(id) {
+ item.links.values().for_each(|id| self.add_any_id(id));
+
match &item.inner {
ItemEnum::Import(x) => self.check_import(x),
ItemEnum::Union(x) => self.check_union(x),
}
}
+ fn add_any_id(&mut self, id: &'a Id) {
+ self.add_id_checked(id, |_| true, "any kind of item");
+ }
+
fn add_field_id(&mut self, id: &'a Id) {
self.add_id_checked(id, Kind::is_struct_field, "StructField");
}
None
}
}
+
+#[cfg(test)]
+mod tests;
--- /dev/null
+use std::collections::HashMap;
+
+use rustdoc_json_types::{Crate, Item, Visibility};
+
+use super::*;
+
+#[track_caller]
+fn check(krate: &Crate, errs: &[Error]) {
+ let mut validator = Validator::new(krate);
+ validator.check_crate();
+
+ assert_eq!(errs, &validator.errs[..]);
+}
+
+fn id(s: &str) -> Id {
+ Id(s.to_owned())
+}
+
+#[test]
+fn errors_on_missing_links() {
+ let k = Crate {
+ root: id("0"),
+ crate_version: None,
+ includes_private: false,
+ index: HashMap::from_iter([(
+ id("0"),
+ Item {
+ name: Some("root".to_owned()),
+ id: id(""),
+ crate_id: 0,
+ span: None,
+ visibility: Visibility::Public,
+ docs: None,
+ links: HashMap::from_iter([("Not Found".to_owned(), id("1"))]),
+ attrs: vec![],
+ deprecation: None,
+ inner: ItemEnum::Module(Module {
+ is_crate: true,
+ items: vec![],
+ is_stripped: false,
+ }),
+ },
+ )]),
+ paths: HashMap::new(),
+ external_crates: HashMap::new(),
+ format_version: rustdoc_json_types::FORMAT_VERSION,
+ };
+
+ check(&k, &[Error { kind: ErrorKind::NotFound, id: id("1") }]);
+}
[rdg-r-a]: https://rustc-dev-guide.rust-lang.org/building/suggested.html#configuring-rust-analyzer-for-rustc
-## Advanced topic: other build environments
+## Advanced topic: Working on Miri in the rustc tree
We described above the simplest way to get a working build environment for Miri,
which is to use the version of rustc indicated by `rustc-version`. But
sometimes, that is not enough.
-### Building Miri with a locally built rustc
+A big part of the Miri driver is shared with rustc, so working on Miri will
+sometimes require also working on rustc itself. In this case, you should *not*
+work in a clone of the Miri repository, but in a clone of the
+[main Rust repository](https://github.com/rust-lang/rust/). There is a copy of
+Miri located at `src/tools/miri` that you can work on directly. A maintainer
+will eventually sync those changes back into this repository.
-[building Miri with a locally built rustc]: #building-miri-with-a-locally-built-rustc
+When working on Miri in the rustc tree, here's how you can run tests:
-A big part of the Miri driver lives in rustc, so working on Miri will sometimes
-require using a locally built rustc. The bug you want to fix may actually be on
-the rustc side, or you just need to get more detailed trace of the execution
-than what is possible with release builds -- in both cases, you should develop
-Miri against a rustc you compiled yourself, with debug assertions (and hence
-tracing) enabled.
-
-The setup for a local rustc works as follows:
-```sh
-# Clone the rust-lang/rust repo.
-git clone https://github.com/rust-lang/rust rustc
-cd rustc
-# Create a config.toml with defaults for working on Miri.
-./x.py setup compiler
- # Now edit `config.toml` and under `[rust]` set `debug-assertions = true`.
-
-# Build a stage 2 rustc, and build the rustc libraries with that rustc.
-# This step can take 30 minutes or more.
-./x.py build --stage 2 compiler/rustc
-# If you change something, you can get a faster rebuild by doing
-./x.py build --keep-stage 0 --stage 2 compiler/rustc
-# You may have to change the architecture in the next command
-rustup toolchain link stage2 build/x86_64-unknown-linux-gnu/stage2
-# Now cd to your Miri directory, then configure rustup
-rustup override set stage2
```
-
-Note: When you are working with a locally built rustc or any other toolchain that
-is not the same as the one in `rust-version`, you should not have `.auto-everything` or
-`.auto-toolchain` as that will keep resetting your toolchain.
-
-```sh
-rm -f .auto-everything .auto-toolchain
+./x.py test miri --stage 0
```
-Important: You need to delete the Miri cache when you change the stdlib; otherwise the
-old, chached version will be used. On Linux, the cache is located at `~/.cache/miri`,
-and on Windows, it is located at `%LOCALAPPDATA%\rust-lang\miri\cache`; the exact
-location is printed after the library build: "A libstd for Miri is now available in ...".
-
-Note: `./x.py --stage 2 compiler/rustc` currently errors with `thread 'main'
-panicked at 'fs::read(stamp) failed with No such file or directory (os error 2)`,
-you can simply ignore that error; Miri will build anyway.
+`--bless` will work, too.
-For more information about building and configuring a local compiler,
-see <https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html>.
+You can also directly run Miri on a Rust source file:
-With this, you should now have a working development setup! See
-[above](#building-and-testing-miri) for how to proceed working on Miri.
+```
+./x.py run miri --stage 0 --args src/tools/miri/tests/pass/hello.rs
+```
## Advanced topic: Syncing with the rustc repo
[[package]]
name = "ui_test"
-version = "0.4.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf4559da3fe6b481f8674a29379677cb9606cd6f75fc254a2c9834c55638503d"
+checksum = "54ddb6f31025943e2f9d59237f433711c461a43d9415974c3eb3a4902edc1c1f"
dependencies = [
"bstr",
"cargo_metadata",
[dev-dependencies]
colored = "2"
-ui_test = "0.4"
+ui_test = "0.5"
rustc_version = "0.4"
# Features chosen to match those required by env_logger, to avoid rebuilds
regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] }
let target = target.as_ref().unwrap_or(host);
// We always setup.
- setup(&subcommand, target, &rustc_version);
+ setup(&subcommand, target, &rustc_version, verbose);
// Invoke actual cargo for the job, but with different flags.
// We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but
continue;
} else if verbose > 0 {
eprintln!(
- "[cargo-miri runner] Overwriting run-time env var {:?}={:?} with build-time value {:?}",
- name, old_val, val
+ "[cargo-miri runner] Overwriting run-time env var {name:?}={old_val:?} with build-time value {val:?}"
);
}
}
/// Performs the setup required to make `cargo miri` work: Getting a custom-built libstd. Then sets
/// `MIRI_SYSROOT`. Skipped if `MIRI_SYSROOT` is already set, in which case we expect the user has
/// done all this already.
-pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta) {
+pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta, verbose: usize) {
let only_setup = matches!(subcommand, MiriCommand::Setup);
let ask_user = !only_setup;
let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
// `config.toml`.
command.env("RUSTC_WRAPPER", "");
- if only_setup {
- if print_sysroot {
- // Be extra sure there is no noise on stdout.
- command.stdout(process::Stdio::null());
+ if only_setup && !print_sysroot {
+ // Forward output. Even make it verbose, if requested.
+ for _ in 0..verbose {
+ command.arg("-v");
}
} else {
+ // Supress output.
command.stdout(process::Stdio::null());
command.stderr(process::Stdio::null());
}
std::env::set_var("MIRI_SYSROOT", &sysroot_dir);
// Do the build.
- if only_setup {
+ if print_sysroot {
+ // Be silent.
+ } else if only_setup {
// We want to be explicit.
eprintln!("Preparing a sysroot for Miri (target: {target})...");
} else {
)
}
});
- if only_setup {
+ if print_sysroot {
+ // Be silent.
+ } else if only_setup {
eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display());
} else {
eprintln!("done");
./miri test
if [ -z "${MIRI_TEST_TARGET+exists}" ]; then
# Only for host architecture: tests with optimizations (`-O` is what cargo passes, but crank MIR
- # optimizations up all the way).
- # Optimizations change diagnostics (mostly backtraces), so we don't check them
- #FIXME(#2155): we want to only run the pass and panic tests here, not the fail tests.
+ # optimizations up all the way, too).
+ # Optimizations change diagnostics (mostly backtraces), so we don't check
+ # them. Also error locations change so we don't run the failing tests.
MIRIFLAGS="${MIRIFLAGS:-} -O -Zmir-opt-level=4" MIRI_SKIP_UI_CHECKS=1 ./miri test -- tests/{pass,panic}
+
+ # Also run some many-seeds tests. 64 seeds means this takes around a minute per test.
+ for FILE in tests/many-seeds/*.rs; do
+ MIRI_SEEDS=64 CARGO_EXTRA_FLAGS="$CARGO_EXTRA_FLAGS -q" ./miri many-seeds ./miri run "$FILE"
+ done
fi
## test-cargo-miri
./miri many-seeds <command>:
Runs <command> over and over again with different seeds for Miri. The MIRIFLAGS
variable is set to its original value appended with ` -Zmiri-seed=$SEED` for
-many different seeds.
+many different seeds. The MIRI_SEEDS variable controls how many seeds are being
+tried; MIRI_SEED_START controls the first seed to try.
./miri bench <benches>:
Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
fi
;;
many-seeds)
- for SEED in $(seq 0 255); do
+ MIRI_SEED_START=${MIRI_SEED_START:-0} # default to 0
+ MIRI_SEEDS=${MIRI_SEEDS:-256} # default to 256
+ for SEED in $(seq $MIRI_SEED_START $(( $MIRI_SEED_START + $MIRI_SEEDS - 1 )) ); do
echo "Trying seed: $SEED"
MIRIFLAGS="$MIRIFLAGS -Zlayout-seed=$SEED -Zmiri-seed=$SEED" $@ || { echo "Failing seed: $SEED"; break; }
done
# Build a sysroot and set MIRI_SYSROOT to use it. Arguments are passed to `cargo miri setup`.
build_sysroot() {
if ! MIRI_SYSROOT="$($CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -- miri setup --print-sysroot "$@")"; then
+ # Run it again so the user can see the error.
+ $CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -- miri setup "$@"
echo "'cargo miri setup' failed"
exit 1
fi
-454784afba5bf35b5ff14ada0e31265ad1d75e73
+203c8765ea33c65d888febe0e8219c4bb11b0d89
if log::Level::from_str(&var).is_ok() {
env::set_var(
"RUSTC_LOG",
- format!(
- "rustc_middle::mir::interpret={0},rustc_const_eval::interpret={0}",
- var
- ),
+ format!("rustc_middle::mir::interpret={var},rustc_const_eval::interpret={var}"),
);
} else {
env::set_var("RUSTC_LOG", &var);
} else if arg == "-Zmiri-disable-validation" {
miri_config.validate = false;
} else if arg == "-Zmiri-disable-stacked-borrows" {
- miri_config.stacked_borrows = false;
+ miri_config.borrow_tracker = None;
} else if arg == "-Zmiri-disable-data-race-detector" {
miri_config.data_race_detector = false;
miri_config.weak_memory_emulation = false;
err
),
};
- for id in ids.into_iter().map(miri::SbTag::new) {
+ for id in ids.into_iter().map(miri::BorTag::new) {
if let Some(id) = id {
miri_config.tracked_pointer_tags.insert(id);
} else {
--- /dev/null
+use std::cell::RefCell;
+use std::fmt;
+use std::num::NonZeroU64;
+
+use log::trace;
+use smallvec::SmallVec;
+
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_middle::mir::RetagKind;
+use rustc_target::abi::Size;
+
+use crate::*;
+pub mod stacked_borrows;
+
+pub type CallId = NonZeroU64;
+
+/// Tracking pointer provenance
+#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct BorTag(NonZeroU64);
+
+impl BorTag {
+ pub fn new(i: u64) -> Option<Self> {
+ NonZeroU64::new(i).map(BorTag)
+ }
+
+ pub fn get(&self) -> u64 {
+ self.0.get()
+ }
+
+ pub fn inner(&self) -> NonZeroU64 {
+ self.0
+ }
+
+ pub fn succ(self) -> Option<Self> {
+ self.0.checked_add(1).map(Self)
+ }
+
+ /// The minimum representable tag
+ pub fn one() -> Self {
+ Self::new(1).unwrap()
+ }
+}
+
+impl std::default::Default for BorTag {
+ /// The default to be used when borrow tracking is disabled
+ fn default() -> Self {
+ Self::one()
+ }
+}
+
+impl fmt::Debug for BorTag {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "<{}>", self.0)
+ }
+}
+
+/// Per-call-stack-frame data for borrow tracking
+#[derive(Debug)]
+pub struct FrameState {
+ /// The ID of the call this frame corresponds to.
+ pub call_id: CallId,
+
+ /// If this frame is protecting any tags, they are listed here. We use this list to do
+ /// incremental updates of the global list of protected tags stored in the
+ /// `stacked_borrows::GlobalState` upon function return, and if we attempt to pop a protected
+ /// tag, to identify which call is responsible for protecting the tag.
+ /// See `Stack::item_popped` for more explanation.
+ ///
+ /// This will contain one tag per reference passed to the function, so
+ /// a size of 2 is enough for the vast majority of functions.
+ pub protected_tags: SmallVec<[BorTag; 2]>,
+}
+
+impl VisitTags for FrameState {
+ fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
+ // `protected_tags` are fine to GC.
+ }
+}
+
+/// Extra global state, available to the memory access hooks.
+#[derive(Debug)]
+pub struct GlobalStateInner {
+ /// Borrow tracker method currently in use.
+ pub borrow_tracker_method: BorrowTrackerMethod,
+ /// Next unused pointer ID (tag).
+ pub next_ptr_tag: BorTag,
+ /// Table storing the "base" tag for each allocation.
+ /// The base tag is the one used for the initial pointer.
+ /// We need this in a separate table to handle cyclic statics.
+ pub base_ptr_tags: FxHashMap<AllocId, BorTag>,
+ /// Next unused call ID (for protectors).
+ pub next_call_id: CallId,
+ /// All currently protected tags.
+ /// An item is protected if its tag is in this set, *and* it has the "protected" bit set.
+ /// We add tags to this when they are created with a protector in `reborrow`, and
+ /// we remove tags from this when the call which is protecting them returns, in
+ /// `GlobalStateInner::end_call`. See `Stack::item_popped` for more details.
+ pub protected_tags: FxHashMap<BorTag, ProtectorKind>,
+ /// The pointer ids to trace
+ pub tracked_pointer_tags: FxHashSet<BorTag>,
+ /// The call ids to trace
+ pub tracked_call_ids: FxHashSet<CallId>,
+ /// Whether to recurse into datatypes when searching for pointers to retag.
+ pub retag_fields: RetagFields,
+}
+
+impl VisitTags for GlobalStateInner {
+ fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
+ // The only candidate is base_ptr_tags, and that does not need visiting since we don't ever
+ // GC the bottommost tag.
+ }
+}
+
+/// We need interior mutable access to the global state.
+pub type GlobalState = RefCell<GlobalStateInner>;
+
+/// Indicates which kind of access is being performed.
+#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
+pub enum AccessKind {
+ Read,
+ Write,
+}
+
+impl fmt::Display for AccessKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ AccessKind::Read => write!(f, "read access"),
+ AccessKind::Write => write!(f, "write access"),
+ }
+ }
+}
+
+/// Policy on whether to recurse into fields to retag
+#[derive(Copy, Clone, Debug)]
+pub enum RetagFields {
+ /// Don't retag any fields.
+ No,
+ /// Retag all fields.
+ Yes,
+ /// Only retag fields of types with Scalar and ScalarPair layout,
+ /// to match the LLVM `noalias` we generate.
+ OnlyScalar,
+}
+
+/// The flavor of the protector.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum ProtectorKind {
+ /// Protected against aliasing violations from other pointers.
+ ///
+ /// Items protected like this cause UB when they are invalidated, *but* the pointer itself may
+ /// still be used to issue a deallocation.
+ ///
+ /// This is required for LLVM IR pointers that are `noalias` but *not* `dereferenceable`.
+ WeakProtector,
+
+ /// Protected against any kind of invalidation.
+ ///
+ /// Items protected like this cause UB when they are invalidated or the memory is deallocated.
+ /// This is strictly stronger protection than `WeakProtector`.
+ ///
+ /// This is required for LLVM IR pointers that are `dereferenceable` (and also allows `noalias`).
+ StrongProtector,
+}
+
+/// Utilities for initialization and ID generation
+impl GlobalStateInner {
+ pub fn new(
+ borrow_tracker_method: BorrowTrackerMethod,
+ tracked_pointer_tags: FxHashSet<BorTag>,
+ tracked_call_ids: FxHashSet<CallId>,
+ retag_fields: RetagFields,
+ ) -> Self {
+ GlobalStateInner {
+ borrow_tracker_method,
+ next_ptr_tag: BorTag::one(),
+ base_ptr_tags: FxHashMap::default(),
+ next_call_id: NonZeroU64::new(1).unwrap(),
+ protected_tags: FxHashMap::default(),
+ tracked_pointer_tags,
+ tracked_call_ids,
+ retag_fields,
+ }
+ }
+
+ /// Generates a new pointer tag. Remember to also check track_pointer_tags and log its creation!
+ pub fn new_ptr(&mut self) -> BorTag {
+ let id = self.next_ptr_tag;
+ self.next_ptr_tag = id.succ().unwrap();
+ id
+ }
+
+ pub fn new_frame(&mut self, machine: &MiriMachine<'_, '_>) -> FrameState {
+ let call_id = self.next_call_id;
+ trace!("new_frame: Assigning call ID {}", call_id);
+ if self.tracked_call_ids.contains(&call_id) {
+ machine.emit_diagnostic(NonHaltingDiagnostic::CreatedCallId(call_id));
+ }
+ self.next_call_id = NonZeroU64::new(call_id.get() + 1).unwrap();
+ FrameState { call_id, protected_tags: SmallVec::new() }
+ }
+
+ pub fn end_call(&mut self, frame: &machine::FrameExtra<'_>) {
+ for tag in &frame
+ .borrow_tracker
+ .as_ref()
+ .expect("we should have borrow tracking data")
+ .protected_tags
+ {
+ self.protected_tags.remove(tag);
+ }
+ }
+
+ pub fn base_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_, '_>) -> BorTag {
+ self.base_ptr_tags.get(&id).copied().unwrap_or_else(|| {
+ let tag = self.new_ptr();
+ if self.tracked_pointer_tags.contains(&tag) {
+ machine.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
+ tag.inner(),
+ None,
+ None,
+ ));
+ }
+ trace!("New allocation {:?} has base tag {:?}", id, tag);
+ self.base_ptr_tags.try_insert(id, tag).unwrap();
+ tag
+ })
+ }
+}
+
+/// Which borrow tracking method to use
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum BorrowTrackerMethod {
+ /// Stacked Borrows, as implemented in borrow_tracker/stacked
+ StackedBorrows,
+}
+
+impl BorrowTrackerMethod {
+ pub fn instanciate_global_state(self, config: &MiriConfig) -> GlobalState {
+ RefCell::new(GlobalStateInner::new(
+ self,
+ config.tracked_pointer_tags.clone(),
+ config.tracked_call_ids.clone(),
+ config.retag_fields,
+ ))
+ }
+}
+
+impl GlobalStateInner {
+ pub fn new_allocation(
+ &mut self,
+ id: AllocId,
+ alloc_size: Size,
+ kind: MemoryKind<machine::MiriMemoryKind>,
+ machine: &MiriMachine<'_, '_>,
+ ) -> AllocState {
+ match self.borrow_tracker_method {
+ BorrowTrackerMethod::StackedBorrows =>
+ AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation(
+ id, alloc_size, self, kind, machine,
+ )))),
+ }
+ }
+}
+
+impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
+pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
+ fn retag_ptr_value(&mut self, kind: RetagKind, val: &ImmTy<'tcx, Provenance>) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
+ let this = self.eval_context_mut();
+ let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
+ match method {
+ BorrowTrackerMethod::StackedBorrows => this.sb_retag_ptr_value(kind, val),
+ }
+ }
+
+ fn retag_place_contents(&mut self, kind: RetagKind, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
+ let this = self.eval_context_mut();
+ let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
+ match method {
+ BorrowTrackerMethod::StackedBorrows => this.sb_retag_place_contents(kind, place),
+ }
+ }
+
+ fn retag_return_place(&mut self) -> InterpResult<'tcx> {
+ let this = self.eval_context_mut();
+ let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
+ match method {
+ BorrowTrackerMethod::StackedBorrows => this.sb_retag_return_place(),
+ }
+ }
+
+ fn expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
+ let this = self.eval_context_mut();
+ let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
+ match method {
+ BorrowTrackerMethod::StackedBorrows => this.sb_expose_tag(alloc_id, tag),
+ }
+ }
+}
+
+/// Extra per-allocation data for borrow tracking
+#[derive(Debug, Clone)]
+pub enum AllocState {
+ /// Data corresponding to Stacked Borrows
+ StackedBorrows(Box<RefCell<stacked_borrows::AllocState>>),
+}
+
+impl machine::AllocExtra {
+ #[track_caller]
+ pub fn borrow_tracker_sb(&self) -> &RefCell<stacked_borrows::AllocState> {
+ match self.borrow_tracker {
+ Some(AllocState::StackedBorrows(ref sb)) => sb,
+ _ => panic!("expected Stacked Borrows borrow tracking, got something else"),
+ }
+ }
+
+ #[track_caller]
+ pub fn borrow_tracker_sb_mut(&mut self) -> &mut RefCell<stacked_borrows::AllocState> {
+ match self.borrow_tracker {
+ Some(AllocState::StackedBorrows(ref mut sb)) => sb,
+ _ => panic!("expected Stacked Borrows borrow tracking, got something else"),
+ }
+ }
+}
+
+impl AllocState {
+ pub fn before_memory_read<'tcx>(
+ &self,
+ alloc_id: AllocId,
+ prov_extra: ProvenanceExtra,
+ range: AllocRange,
+ machine: &MiriMachine<'_, 'tcx>,
+ ) -> InterpResult<'tcx> {
+ match self {
+ AllocState::StackedBorrows(sb) =>
+ sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine),
+ }
+ }
+
+ pub fn before_memory_write<'tcx>(
+ &mut self,
+ alloc_id: AllocId,
+ prov_extra: ProvenanceExtra,
+ range: AllocRange,
+ machine: &mut MiriMachine<'_, 'tcx>,
+ ) -> InterpResult<'tcx> {
+ match self {
+ AllocState::StackedBorrows(sb) =>
+ sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine),
+ }
+ }
+
+ pub fn before_memory_deallocation<'tcx>(
+ &mut self,
+ alloc_id: AllocId,
+ prov_extra: ProvenanceExtra,
+ range: AllocRange,
+ machine: &mut MiriMachine<'_, 'tcx>,
+ ) -> InterpResult<'tcx> {
+ match self {
+ AllocState::StackedBorrows(sb) =>
+ sb.get_mut().before_memory_deallocation(alloc_id, prov_extra, range, machine),
+ }
+ }
+
+ pub fn remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>) {
+ match self {
+ AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags),
+ }
+ }
+}
+
+impl VisitTags for AllocState {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
+ match self {
+ AllocState::StackedBorrows(sb) => sb.visit_tags(visit),
+ }
+ }
+}
--- /dev/null
+use smallvec::SmallVec;
+use std::fmt;
+
+use rustc_middle::mir::interpret::{alloc_range, AllocId, AllocRange, InterpError};
+use rustc_span::{Span, SpanData};
+use rustc_target::abi::Size;
+
+use crate::borrow_tracker::{
+ stacked_borrows::{err_sb_ub, Permission},
+ AccessKind, GlobalStateInner, ProtectorKind,
+};
+use crate::*;
+
+#[derive(Clone, Debug)]
+pub struct AllocHistory {
+ id: AllocId,
+ base: (Item, Span),
+ creations: smallvec::SmallVec<[Creation; 1]>,
+ invalidations: smallvec::SmallVec<[Invalidation; 1]>,
+ protectors: smallvec::SmallVec<[Protection; 1]>,
+}
+
+#[derive(Clone, Debug)]
+struct Creation {
+ retag: RetagOp,
+ span: Span,
+}
+
+impl Creation {
+ fn generate_diagnostic(&self) -> (String, SpanData) {
+ let tag = self.retag.new_tag;
+ if let Some(perm) = self.retag.permission {
+ (
+ format!(
+ "{tag:?} was created by a {:?} retag at offsets {:?}",
+ perm, self.retag.range,
+ ),
+ self.span.data(),
+ )
+ } else {
+ assert!(self.retag.range.size == Size::ZERO);
+ (
+ format!(
+ "{tag:?} would have been created here, but this is a zero-size retag ({:?}) so the tag in question does not exist anywhere",
+ self.retag.range,
+ ),
+ self.span.data(),
+ )
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+struct Invalidation {
+ tag: BorTag,
+ range: AllocRange,
+ span: Span,
+ cause: InvalidationCause,
+}
+
+#[derive(Clone, Debug)]
+enum InvalidationCause {
+ Access(AccessKind),
+ Retag(Permission, RetagCause),
+}
+
+impl Invalidation {
+ fn generate_diagnostic(&self) -> (String, SpanData) {
+ let message = if let InvalidationCause::Retag(_, RetagCause::FnEntry) = self.cause {
+ // For a FnEntry retag, our Span points at the caller.
+ // See `DiagnosticCx::log_invalidation`.
+ format!(
+ "{:?} was later invalidated at offsets {:?} by a {} inside this call",
+ self.tag, self.range, self.cause
+ )
+ } else {
+ format!(
+ "{:?} was later invalidated at offsets {:?} by a {}",
+ self.tag, self.range, self.cause
+ )
+ };
+ (message, self.span.data())
+ }
+}
+
+impl fmt::Display for InvalidationCause {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ InvalidationCause::Access(kind) => write!(f, "{kind}"),
+ InvalidationCause::Retag(perm, kind) =>
+ if *kind == RetagCause::FnEntry {
+ write!(f, "{perm:?} FnEntry retag")
+ } else {
+ write!(f, "{perm:?} retag")
+ },
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+struct Protection {
+ tag: BorTag,
+ span: Span,
+}
+
+#[derive(Clone)]
+pub struct TagHistory {
+ pub created: (String, SpanData),
+ pub invalidated: Option<(String, SpanData)>,
+ pub protected: Option<(String, SpanData)>,
+}
+
+pub struct DiagnosticCxBuilder<'ecx, 'mir, 'tcx> {
+ operation: Operation,
+ machine: &'ecx MiriMachine<'mir, 'tcx>,
+}
+
+pub struct DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
+ operation: Operation,
+ machine: &'ecx MiriMachine<'mir, 'tcx>,
+ history: &'history mut AllocHistory,
+ offset: Size,
+}
+
+impl<'ecx, 'mir, 'tcx> DiagnosticCxBuilder<'ecx, 'mir, 'tcx> {
+ pub fn build<'history>(
+ self,
+ history: &'history mut AllocHistory,
+ offset: Size,
+ ) -> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
+ DiagnosticCx { operation: self.operation, machine: self.machine, history, offset }
+ }
+
+ pub fn retag(
+ machine: &'ecx MiriMachine<'mir, 'tcx>,
+ cause: RetagCause,
+ new_tag: BorTag,
+ orig_tag: ProvenanceExtra,
+ range: AllocRange,
+ ) -> Self {
+ let operation =
+ Operation::Retag(RetagOp { cause, new_tag, orig_tag, range, permission: None });
+
+ DiagnosticCxBuilder { machine, operation }
+ }
+
+ pub fn read(
+ machine: &'ecx MiriMachine<'mir, 'tcx>,
+ tag: ProvenanceExtra,
+ range: AllocRange,
+ ) -> Self {
+ let operation = Operation::Access(AccessOp { kind: AccessKind::Read, tag, range });
+ DiagnosticCxBuilder { machine, operation }
+ }
+
+ pub fn write(
+ machine: &'ecx MiriMachine<'mir, 'tcx>,
+ tag: ProvenanceExtra,
+ range: AllocRange,
+ ) -> Self {
+ let operation = Operation::Access(AccessOp { kind: AccessKind::Write, tag, range });
+ DiagnosticCxBuilder { machine, operation }
+ }
+
+ pub fn dealloc(machine: &'ecx MiriMachine<'mir, 'tcx>, tag: ProvenanceExtra) -> Self {
+ let operation = Operation::Dealloc(DeallocOp { tag });
+ DiagnosticCxBuilder { machine, operation }
+ }
+}
+
+impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
+ pub fn unbuild(self) -> DiagnosticCxBuilder<'ecx, 'mir, 'tcx> {
+ DiagnosticCxBuilder { machine: self.machine, operation: self.operation }
+ }
+}
+
+#[derive(Debug, Clone)]
+enum Operation {
+ Retag(RetagOp),
+ Access(AccessOp),
+ Dealloc(DeallocOp),
+}
+
+#[derive(Debug, Clone)]
+struct RetagOp {
+ cause: RetagCause,
+ new_tag: BorTag,
+ orig_tag: ProvenanceExtra,
+ range: AllocRange,
+ permission: Option<Permission>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum RetagCause {
+ Normal,
+ FnReturn,
+ FnEntry,
+ TwoPhase,
+}
+
+#[derive(Debug, Clone)]
+struct AccessOp {
+ kind: AccessKind,
+ tag: ProvenanceExtra,
+ range: AllocRange,
+}
+
+#[derive(Debug, Clone)]
+struct DeallocOp {
+ tag: ProvenanceExtra,
+}
+
+impl AllocHistory {
+ pub fn new(id: AllocId, item: Item, machine: &MiriMachine<'_, '_>) -> Self {
+ Self {
+ id,
+ base: (item, machine.current_span()),
+ creations: SmallVec::new(),
+ invalidations: SmallVec::new(),
+ protectors: SmallVec::new(),
+ }
+ }
+}
+
+impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
+ pub fn start_grant(&mut self, perm: Permission) {
+ let Operation::Retag(op) = &mut self.operation else {
+ unreachable!("start_grant must only be called during a retag, this is: {:?}", self.operation)
+ };
+ op.permission = Some(perm);
+
+ let last_creation = &mut self.history.creations.last_mut().unwrap();
+ match last_creation.retag.permission {
+ None => {
+ last_creation.retag.permission = Some(perm);
+ }
+ Some(previous) =>
+ if previous != perm {
+ // 'Split up' the creation event.
+ let previous_range = last_creation.retag.range;
+ last_creation.retag.range = alloc_range(previous_range.start, self.offset);
+ let mut new_event = last_creation.clone();
+ new_event.retag.range = alloc_range(self.offset, previous_range.end());
+ new_event.retag.permission = Some(perm);
+ self.history.creations.push(new_event);
+ },
+ }
+ }
+
+ pub fn log_creation(&mut self) {
+ let Operation::Retag(op) = &self.operation else {
+ unreachable!("log_creation must only be called during a retag")
+ };
+ self.history
+ .creations
+ .push(Creation { retag: op.clone(), span: self.machine.current_span() });
+ }
+
+ pub fn log_invalidation(&mut self, tag: BorTag) {
+ let mut span = self.machine.current_span();
+ let (range, cause) = match &self.operation {
+ Operation::Retag(RetagOp { cause, range, permission, .. }) => {
+ if *cause == RetagCause::FnEntry {
+ span = self.machine.caller_span();
+ }
+ (*range, InvalidationCause::Retag(permission.unwrap(), *cause))
+ }
+ Operation::Access(AccessOp { kind, range, .. }) =>
+ (*range, InvalidationCause::Access(*kind)),
+ Operation::Dealloc(_) => {
+ // This can be reached, but never be relevant later since the entire allocation is
+ // gone now.
+ return;
+ }
+ };
+ self.history.invalidations.push(Invalidation { tag, range, span, cause });
+ }
+
+ pub fn log_protector(&mut self) {
+ let Operation::Retag(op) = &self.operation else {
+ unreachable!("Protectors can only be created during a retag")
+ };
+ self.history
+ .protectors
+ .push(Protection { tag: op.new_tag, span: self.machine.current_span() });
+ }
+
+ pub fn get_logs_relevant_to(
+ &self,
+ tag: BorTag,
+ protector_tag: Option<BorTag>,
+ ) -> Option<TagHistory> {
+ let Some(created) = self.history
+ .creations
+ .iter()
+ .rev()
+ .find_map(|event| {
+ // First, look for a Creation event where the tag and the offset matches. This
+ // ensrues that we pick the right Creation event when a retag isn't uniform due to
+ // Freeze.
+ let range = event.retag.range;
+ if event.retag.new_tag == tag
+ && self.offset >= range.start
+ && self.offset < (range.start + range.size)
+ {
+ Some(event.generate_diagnostic())
+ } else {
+ None
+ }
+ })
+ .or_else(|| {
+ // If we didn't find anything with a matching offset, just return the event where
+ // the tag was created. This branch is hit when we use a tag at an offset that
+ // doesn't have the tag.
+ self.history.creations.iter().rev().find_map(|event| {
+ if event.retag.new_tag == tag {
+ Some(event.generate_diagnostic())
+ } else {
+ None
+ }
+ })
+ }).or_else(|| {
+ // If we didn't find a retag that created this tag, it might be the base tag of
+ // this allocation.
+ if self.history.base.0.tag() == tag {
+ Some((
+ format!("{tag:?} was created here, as the base tag for {:?}", self.history.id),
+ self.history.base.1.data()
+ ))
+ } else {
+ None
+ }
+ }) else {
+ // But if we don't have a creation event, this is related to a wildcard, and there
+ // is really nothing we can do to help.
+ return None;
+ };
+
+ let invalidated = self.history.invalidations.iter().rev().find_map(|event| {
+ if event.tag == tag { Some(event.generate_diagnostic()) } else { None }
+ });
+
+ let protected = protector_tag
+ .and_then(|protector| {
+ self.history.protectors.iter().find(|protection| protection.tag == protector)
+ })
+ .map(|protection| {
+ let protected_tag = protection.tag;
+ (format!("{protected_tag:?} is this argument"), protection.span.data())
+ });
+
+ Some(TagHistory { created, invalidated, protected })
+ }
+
+ /// Report a descriptive error when `new` could not be granted from `derived_from`.
+ #[inline(never)] // This is only called on fatal code paths
+ pub(super) fn grant_error(&self, stack: &Stack) -> InterpError<'tcx> {
+ let Operation::Retag(op) = &self.operation else {
+ unreachable!("grant_error should only be called during a retag")
+ };
+ let perm =
+ op.permission.expect("`start_grant` must be called before calling `grant_error`");
+ let action = format!(
+ "trying to retag from {:?} for {:?} permission at {:?}[{:#x}]",
+ op.orig_tag,
+ perm,
+ self.history.id,
+ self.offset.bytes(),
+ );
+ err_sb_ub(
+ format!("{action}{}", error_cause(stack, op.orig_tag)),
+ Some(operation_summary(&op.cause.summary(), self.history.id, op.range)),
+ op.orig_tag.and_then(|orig_tag| self.get_logs_relevant_to(orig_tag, None)),
+ )
+ }
+
+ /// Report a descriptive error when `access` is not permitted based on `tag`.
+ #[inline(never)] // This is only called on fatal code paths
+ pub(super) fn access_error(&self, stack: &Stack) -> InterpError<'tcx> {
+ // Deallocation and retagging also do an access as part of their thing, so handle that here, too.
+ let op = match &self.operation {
+ Operation::Access(op) => op,
+ Operation::Retag(_) => return self.grant_error(stack),
+ Operation::Dealloc(_) => return self.dealloc_error(stack),
+ };
+ let action = format!(
+ "attempting a {access} using {tag:?} at {alloc_id:?}[{offset:#x}]",
+ access = op.kind,
+ tag = op.tag,
+ alloc_id = self.history.id,
+ offset = self.offset.bytes(),
+ );
+ err_sb_ub(
+ format!("{action}{}", error_cause(stack, op.tag)),
+ Some(operation_summary("an access", self.history.id, op.range)),
+ op.tag.and_then(|tag| self.get_logs_relevant_to(tag, None)),
+ )
+ }
+
+ #[inline(never)] // This is only called on fatal code paths
+ pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpError<'tcx> {
+ let protected = match kind {
+ ProtectorKind::WeakProtector => "weakly protected",
+ ProtectorKind::StrongProtector => "strongly protected",
+ };
+ let call_id = self
+ .machine
+ .threads
+ .all_stacks()
+ .flatten()
+ .map(|frame| {
+ frame.extra.borrow_tracker.as_ref().expect("we should have borrow tracking data")
+ })
+ .find(|frame| frame.protected_tags.contains(&item.tag()))
+ .map(|frame| frame.call_id)
+ .unwrap(); // FIXME: Surely we should find something, but a panic seems wrong here?
+ match self.operation {
+ Operation::Dealloc(_) =>
+ err_sb_ub(
+ format!("deallocating while item {item:?} is {protected} by call {call_id:?}",),
+ None,
+ None,
+ ),
+ Operation::Retag(RetagOp { orig_tag: tag, .. })
+ | Operation::Access(AccessOp { tag, .. }) =>
+ err_sb_ub(
+ format!(
+ "not granting access to tag {tag:?} because that would remove {item:?} which is {protected} because it is an argument of call {call_id:?}",
+ ),
+ None,
+ tag.and_then(|tag| self.get_logs_relevant_to(tag, Some(item.tag()))),
+ ),
+ }
+ }
+
+ #[inline(never)] // This is only called on fatal code paths
+ pub fn dealloc_error(&self, stack: &Stack) -> InterpError<'tcx> {
+ let Operation::Dealloc(op) = &self.operation else {
+ unreachable!("dealloc_error should only be called during a deallocation")
+ };
+ err_sb_ub(
+ format!(
+ "attempting deallocation using {tag:?} at {alloc_id:?}{cause}",
+ tag = op.tag,
+ alloc_id = self.history.id,
+ cause = error_cause(stack, op.tag),
+ ),
+ None,
+ op.tag.and_then(|tag| self.get_logs_relevant_to(tag, None)),
+ )
+ }
+
+ #[inline(never)]
+ pub fn check_tracked_tag_popped(&self, item: &Item, global: &GlobalStateInner) {
+ if !global.tracked_pointer_tags.contains(&item.tag()) {
+ return;
+ }
+ let cause = match self.operation {
+ Operation::Dealloc(_) => format!(" due to deallocation"),
+ Operation::Access(AccessOp { kind, tag, .. }) =>
+ format!(" due to {kind:?} access for {tag:?}"),
+ Operation::Retag(RetagOp { orig_tag, permission, new_tag, .. }) => {
+ let permission = permission
+ .expect("start_grant should set the current permission before popping a tag");
+ format!(" due to {permission:?} retag from {orig_tag:?} (that retag created {new_tag:?})")
+ }
+ };
+
+ self.machine.emit_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(*item, cause));
+ }
+}
+
+fn operation_summary(operation: &str, alloc_id: AllocId, alloc_range: AllocRange) -> String {
+ format!("this error occurs as part of {operation} at {alloc_id:?}{alloc_range:?}")
+}
+
+fn error_cause(stack: &Stack, prov_extra: ProvenanceExtra) -> &'static str {
+ if let ProvenanceExtra::Concrete(tag) = prov_extra {
+ if (0..stack.len())
+ .map(|i| stack.get(i).unwrap())
+ .any(|item| item.tag() == tag && item.perm() != Permission::Disabled)
+ {
+ ", but that tag only grants SharedReadOnly permission for this location"
+ } else {
+ ", but that tag does not exist in the borrow stack for this location"
+ }
+ } else {
+ ", but no exposed tags have suitable permission in the borrow stack for this location"
+ }
+}
+
+impl RetagCause {
+ fn summary(&self) -> String {
+ match self {
+ RetagCause::Normal => "retag",
+ RetagCause::FnEntry => "FnEntry retag",
+ RetagCause::FnReturn => "FnReturn retag",
+ RetagCause::TwoPhase => "two-phase retag",
+ }
+ .to_string()
+ }
+}
--- /dev/null
+use std::fmt;
+
+use crate::borrow_tracker::BorTag;
+
+/// An item in the per-location borrow stack.
+#[derive(Copy, Clone, Hash, PartialEq, Eq)]
+pub struct Item(u64);
+
+// An Item contains 3 bitfields:
+// * Bits 0-61 store a BorTag
+// * Bits 61-63 store a Permission
+// * Bit 64 stores a flag which indicates if we have a protector
+const TAG_MASK: u64 = u64::MAX >> 3;
+const PERM_MASK: u64 = 0x3 << 61;
+const PROTECTED_MASK: u64 = 0x1 << 63;
+
+const PERM_SHIFT: u64 = 61;
+const PROTECTED_SHIFT: u64 = 63;
+
+impl Item {
+ pub fn new(tag: BorTag, perm: Permission, protected: bool) -> Self {
+ assert!(tag.get() <= TAG_MASK);
+ let packed_tag = tag.get();
+ let packed_perm = perm.to_bits() << PERM_SHIFT;
+ let packed_protected = u64::from(protected) << PROTECTED_SHIFT;
+
+ let new = Self(packed_tag | packed_perm | packed_protected);
+
+ debug_assert!(new.tag() == tag);
+ debug_assert!(new.perm() == perm);
+ debug_assert!(new.protected() == protected);
+
+ new
+ }
+
+ /// The pointers the permission is granted to.
+ pub fn tag(self) -> BorTag {
+ BorTag::new(self.0 & TAG_MASK).unwrap()
+ }
+
+ /// The permission this item grants.
+ pub fn perm(self) -> Permission {
+ Permission::from_bits((self.0 & PERM_MASK) >> PERM_SHIFT)
+ }
+
+ /// Whether or not there is a protector for this tag
+ pub fn protected(self) -> bool {
+ self.0 & PROTECTED_MASK > 0
+ }
+
+ /// Set the Permission stored in this Item
+ pub fn set_permission(&mut self, perm: Permission) {
+ // Clear the current set permission
+ self.0 &= !PERM_MASK;
+ // Write Permission::Disabled to the Permission bits
+ self.0 |= perm.to_bits() << PERM_SHIFT;
+ }
+}
+
+impl fmt::Debug for Item {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "[{:?} for {:?}]", self.perm(), self.tag())
+ }
+}
+
+/// Indicates which permission is granted (by this item to some pointers)
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub enum Permission {
+ /// Grants unique mutable access.
+ Unique,
+ /// Grants shared mutable access.
+ SharedReadWrite,
+ /// Grants shared read-only access.
+ SharedReadOnly,
+ /// Grants no access, but separates two groups of SharedReadWrite so they are not
+ /// all considered mutually compatible.
+ Disabled,
+}
+
+impl Permission {
+ const UNIQUE: u64 = 0;
+ const SHARED_READ_WRITE: u64 = 1;
+ const SHARED_READ_ONLY: u64 = 2;
+ const DISABLED: u64 = 3;
+
+ fn to_bits(self) -> u64 {
+ match self {
+ Permission::Unique => Self::UNIQUE,
+ Permission::SharedReadWrite => Self::SHARED_READ_WRITE,
+ Permission::SharedReadOnly => Self::SHARED_READ_ONLY,
+ Permission::Disabled => Self::DISABLED,
+ }
+ }
+
+ fn from_bits(perm: u64) -> Self {
+ match perm {
+ Self::UNIQUE => Permission::Unique,
+ Self::SHARED_READ_WRITE => Permission::SharedReadWrite,
+ Self::SHARED_READ_ONLY => Permission::SharedReadOnly,
+ Self::DISABLED => Permission::Disabled,
+ _ => unreachable!(),
+ }
+ }
+}
--- /dev/null
+//! Implements "Stacked Borrows". See <https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md>
+//! for further information.
+
+pub mod diagnostics;
+mod item;
+mod stack;
+
+use log::trace;
+use std::cmp;
+use std::fmt::Write;
+
+use rustc_data_structures::fx::FxHashSet;
+use rustc_middle::mir::{Mutability, RetagKind};
+use rustc_middle::ty::{
+ self,
+ layout::{HasParamEnv, LayoutOf},
+};
+use rustc_target::abi::{Abi, Size};
+
+use crate::borrow_tracker::{
+ stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder, TagHistory},
+ AccessKind, GlobalStateInner, ProtectorKind, RetagFields,
+};
+use crate::*;
+
+use diagnostics::RetagCause;
+pub use item::{Item, Permission};
+pub use stack::Stack;
+
+pub type AllocState = Stacks;
+
+/// Extra per-allocation state.
+#[derive(Clone, Debug)]
+pub struct Stacks {
+ // Even reading memory can have effects on the stack, so we need a `RefCell` here.
+ stacks: RangeMap<Stack>,
+ /// Stores past operations on this allocation
+ history: AllocHistory,
+ /// The set of tags that have been exposed inside this allocation.
+ exposed_tags: FxHashSet<BorTag>,
+ /// Whether this memory has been modified since the last time the tag GC ran
+ modified_since_last_gc: bool,
+}
+
+/// Indicates which permissions to grant to the retagged pointer.
+#[derive(Clone, Debug)]
+enum NewPermission {
+ Uniform {
+ perm: Permission,
+ access: Option<AccessKind>,
+ protector: Option<ProtectorKind>,
+ },
+ FreezeSensitive {
+ freeze_perm: Permission,
+ freeze_access: Option<AccessKind>,
+ freeze_protector: Option<ProtectorKind>,
+ nonfreeze_perm: Permission,
+ nonfreeze_access: Option<AccessKind>,
+ // nonfreeze_protector must always be None
+ },
+}
+
+impl NewPermission {
+ /// A key function: determine the permissions to grant at a retag for the given kind of
+ /// reference/pointer.
+ fn from_ref_ty<'tcx>(
+ ty: ty::Ty<'tcx>,
+ kind: RetagKind,
+ cx: &crate::MiriInterpCx<'_, 'tcx>,
+ ) -> Self {
+ let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector);
+ match ty.kind() {
+ ty::Ref(_, pointee, Mutability::Mut) => {
+ if kind == RetagKind::TwoPhase {
+ // We mostly just give up on 2phase-borrows, and treat these exactly like raw pointers.
+ assert!(protector.is_none()); // RetagKind can't be both FnEntry and TwoPhase.
+ NewPermission::Uniform {
+ perm: Permission::SharedReadWrite,
+ access: None,
+ protector: None,
+ }
+ } else if pointee.is_unpin(*cx.tcx, cx.param_env()) {
+ // A regular full mutable reference.
+ NewPermission::Uniform {
+ perm: Permission::Unique,
+ access: Some(AccessKind::Write),
+ protector,
+ }
+ } else {
+ NewPermission::Uniform {
+ perm: Permission::SharedReadWrite,
+ // FIXME: We emit `dereferenceable` for `!Unpin` mutable references, so we
+ // should do fake accesses here. But then we run into
+ // <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>, so for now
+ // we don't do that.
+ access: None,
+ protector,
+ }
+ }
+ }
+ ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Mut, .. }) => {
+ assert!(protector.is_none()); // RetagKind can't be both FnEntry and Raw.
+ // Mutable raw pointer. No access, not protected.
+ NewPermission::Uniform {
+ perm: Permission::SharedReadWrite,
+ access: None,
+ protector: None,
+ }
+ }
+ ty::Ref(_, _pointee, Mutability::Not) => {
+ NewPermission::FreezeSensitive {
+ freeze_perm: Permission::SharedReadOnly,
+ freeze_access: Some(AccessKind::Read),
+ freeze_protector: protector,
+ nonfreeze_perm: Permission::SharedReadWrite,
+ // Inside UnsafeCell, this does *not* count as an access, as there
+ // might actually be mutable references further up the stack that
+ // we have to keep alive.
+ nonfreeze_access: None,
+ // We do not protect inside UnsafeCell.
+ // This fixes https://github.com/rust-lang/rust/issues/55005.
+ }
+ }
+ ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Not, .. }) => {
+ assert!(protector.is_none()); // RetagKind can't be both FnEntry and Raw.
+ // `*const T`, when freshly created, are read-only in the frozen part.
+ NewPermission::FreezeSensitive {
+ freeze_perm: Permission::SharedReadOnly,
+ freeze_access: Some(AccessKind::Read),
+ freeze_protector: None,
+ nonfreeze_perm: Permission::SharedReadWrite,
+ nonfreeze_access: None,
+ }
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ fn protector(&self) -> Option<ProtectorKind> {
+ match self {
+ NewPermission::Uniform { protector, .. } => *protector,
+ NewPermission::FreezeSensitive { freeze_protector, .. } => *freeze_protector,
+ }
+ }
+}
+
+/// Error reporting
+pub fn err_sb_ub<'tcx>(
+ msg: String,
+ help: Option<String>,
+ history: Option<TagHistory>,
+) -> InterpError<'tcx> {
+ err_machine_stop!(TerminationInfo::StackedBorrowsUb { msg, help, history })
+}
+
+// # Stacked Borrows Core Begin
+
+/// We need to make at least the following things true:
+///
+/// U1: After creating a `Uniq`, it is at the top.
+/// U2: If the top is `Uniq`, accesses must be through that `Uniq` or remove it.
+/// U3: If an access happens with a `Uniq`, it requires the `Uniq` to be in the stack.
+///
+/// F1: After creating a `&`, the parts outside `UnsafeCell` have our `SharedReadOnly` on top.
+/// F2: If a write access happens, it pops the `SharedReadOnly`. This has three pieces:
+/// F2a: If a write happens granted by an item below our `SharedReadOnly`, the `SharedReadOnly`
+/// gets popped.
+/// F2b: No `SharedReadWrite` or `Unique` will ever be added on top of our `SharedReadOnly`.
+/// F3: If an access happens with an `&` outside `UnsafeCell`,
+/// it requires the `SharedReadOnly` to still be in the stack.
+
+/// Core relation on `Permission` to define which accesses are allowed
+impl Permission {
+ /// This defines for a given permission, whether it permits the given kind of access.
+ fn grants(self, access: AccessKind) -> bool {
+ // Disabled grants nothing. Otherwise, all items grant read access, and except for SharedReadOnly they grant write access.
+ self != Permission::Disabled
+ && (access == AccessKind::Read || self != Permission::SharedReadOnly)
+ }
+}
+
+/// Determines whether an item was invalidated by a conflicting access, or by deallocation.
+#[derive(Copy, Clone, Debug)]
+enum ItemInvalidationCause {
+ Conflict,
+ Dealloc,
+}
+
+/// Core per-location operations: access, dealloc, reborrow.
+impl<'tcx> Stack {
+ /// Find the first write-incompatible item above the given one --
+ /// i.e, find the height to which the stack will be truncated when writing to `granting`.
+ fn find_first_write_incompatible(&self, granting: usize) -> usize {
+ let perm = self.get(granting).unwrap().perm();
+ match perm {
+ Permission::SharedReadOnly => bug!("Cannot use SharedReadOnly for writing"),
+ Permission::Disabled => bug!("Cannot use Disabled for anything"),
+ Permission::Unique => {
+ // On a write, everything above us is incompatible.
+ granting + 1
+ }
+ Permission::SharedReadWrite => {
+ // The SharedReadWrite *just* above us are compatible, to skip those.
+ let mut idx = granting + 1;
+ while let Some(item) = self.get(idx) {
+ if item.perm() == Permission::SharedReadWrite {
+ // Go on.
+ idx += 1;
+ } else {
+ // Found first incompatible!
+ break;
+ }
+ }
+ idx
+ }
+ }
+ }
+
+ /// The given item was invalidated -- check its protectors for whether that will cause UB.
+ fn item_invalidated(
+ item: &Item,
+ global: &GlobalStateInner,
+ dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
+ cause: ItemInvalidationCause,
+ ) -> InterpResult<'tcx> {
+ if !global.tracked_pointer_tags.is_empty() {
+ dcx.check_tracked_tag_popped(item, global);
+ }
+
+ if !item.protected() {
+ return Ok(());
+ }
+
+ // We store tags twice, once in global.protected_tags and once in each call frame.
+ // We do this because consulting a single global set in this function is faster
+ // than attempting to search all call frames in the program for the `FrameExtra`
+ // (if any) which is protecting the popped tag.
+ //
+ // This duplication trades off making `end_call` slower to make this function faster. This
+ // trade-off is profitable in practice for a combination of two reasons.
+ // 1. A single protected tag can (and does in some programs) protect thousands of `Item`s.
+ // Therefore, adding overhead in function call/return is profitable even if it only
+ // saves a little work in this function.
+ // 2. Most frames protect only one or two tags. So this duplicative global turns a search
+ // which ends up about linear in the number of protected tags in the program into a
+ // constant time check (and a slow linear, because the tags in the frames aren't contiguous).
+ if let Some(&protector_kind) = global.protected_tags.get(&item.tag()) {
+ // The only way this is okay is if the protector is weak and we are deallocating with
+ // the right pointer.
+ let allowed = matches!(cause, ItemInvalidationCause::Dealloc)
+ && matches!(protector_kind, ProtectorKind::WeakProtector);
+ if !allowed {
+ return Err(dcx.protector_error(item, protector_kind).into());
+ }
+ }
+ Ok(())
+ }
+
+ /// Test if a memory `access` using pointer tagged `tag` is granted.
+ /// If yes, return the index of the item that granted it.
+ /// `range` refers the entire operation, and `offset` refers to the specific offset into the
+ /// allocation that we are currently checking.
+ fn access(
+ &mut self,
+ access: AccessKind,
+ tag: ProvenanceExtra,
+ global: &GlobalStateInner,
+ dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
+ exposed_tags: &FxHashSet<BorTag>,
+ ) -> InterpResult<'tcx> {
+ // Two main steps: Find granting item, remove incompatible items above.
+
+ // Step 1: Find granting item.
+ let granting_idx =
+ self.find_granting(access, tag, exposed_tags).map_err(|()| dcx.access_error(self))?;
+
+ // Step 2: Remove incompatible items above them. Make sure we do not remove protected
+ // items. Behavior differs for reads and writes.
+ // In case of wildcards/unknown matches, we remove everything that is *definitely* gone.
+ if access == AccessKind::Write {
+ // Remove everything above the write-compatible items, like a proper stack. This makes sure read-only and unique
+ // pointers become invalid on write accesses (ensures F2a, and ensures U2 for write accesses).
+ let first_incompatible_idx = if let Some(granting_idx) = granting_idx {
+ // The granting_idx *might* be approximate, but any lower idx would remove more
+ // things. Even if this is a Unique and the lower idx is an SRW (which removes
+ // less), there is an SRW group boundary here so strictly more would get removed.
+ self.find_first_write_incompatible(granting_idx)
+ } else {
+ // We are writing to something in the unknown part.
+ // There is a SRW group boundary between the unknown and the known, so everything is incompatible.
+ 0
+ };
+ self.pop_items_after(first_incompatible_idx, |item| {
+ Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
+ dcx.log_invalidation(item.tag());
+ Ok(())
+ })?;
+ } else {
+ // On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses.
+ // The reason this is not following the stack discipline (by removing the first Unique and
+ // everything on top of it) is that in `let raw = &mut *x as *mut _; let _val = *x;`, the second statement
+ // would pop the `Unique` from the reborrow of the first statement, and subsequently also pop the
+ // `SharedReadWrite` for `raw`.
+ // This pattern occurs a lot in the standard library: create a raw pointer, then also create a shared
+ // reference and use that.
+ // We *disable* instead of removing `Unique` to avoid "connecting" two neighbouring blocks of SRWs.
+ let first_incompatible_idx = if let Some(granting_idx) = granting_idx {
+ // The granting_idx *might* be approximate, but any lower idx would disable more things.
+ granting_idx + 1
+ } else {
+ // We are reading from something in the unknown part. That means *all* `Unique` we know about are dead now.
+ 0
+ };
+ self.disable_uniques_starting_at(first_incompatible_idx, |item| {
+ Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
+ dcx.log_invalidation(item.tag());
+ Ok(())
+ })?;
+ }
+
+ // If this was an approximate action, we now collapse everything into an unknown.
+ if granting_idx.is_none() || matches!(tag, ProvenanceExtra::Wildcard) {
+ // Compute the upper bound of the items that remain.
+ // (This is why we did all the work above: to reduce the items we have to consider here.)
+ let mut max = BorTag::one();
+ for i in 0..self.len() {
+ let item = self.get(i).unwrap();
+ // Skip disabled items, they cannot be matched anyway.
+ if !matches!(item.perm(), Permission::Disabled) {
+ // We are looking for a strict upper bound, so add 1 to this tag.
+ max = cmp::max(item.tag().succ().unwrap(), max);
+ }
+ }
+ if let Some(unk) = self.unknown_bottom() {
+ max = cmp::max(unk, max);
+ }
+ // Use `max` as new strict upper bound for everything.
+ trace!(
+ "access: forgetting stack to upper bound {max} due to wildcard or unknown access",
+ max = max.get(),
+ );
+ self.set_unknown_bottom(max);
+ }
+
+ // Done.
+ Ok(())
+ }
+
+ /// Deallocate a location: Like a write access, but also there must be no
+ /// active protectors at all because we will remove all items.
+ fn dealloc(
+ &mut self,
+ tag: ProvenanceExtra,
+ global: &GlobalStateInner,
+ dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
+ exposed_tags: &FxHashSet<BorTag>,
+ ) -> InterpResult<'tcx> {
+ // Step 1: Make a write access.
+ // As part of this we do regular protector checking, i.e. even weakly protected items cause UB when popped.
+ self.access(AccessKind::Write, tag, global, dcx, exposed_tags)?;
+
+ // Step 2: Pretend we remove the remaining items, checking if any are strongly protected.
+ for idx in (0..self.len()).rev() {
+ let item = self.get(idx).unwrap();
+ Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Dealloc)?;
+ }
+
+ Ok(())
+ }
+
+ /// Derive a new pointer from one with the given tag.
+ ///
+ /// `access` indicates which kind of memory access this retag itself should correspond to.
+ fn grant(
+ &mut self,
+ derived_from: ProvenanceExtra,
+ new: Item,
+ access: Option<AccessKind>,
+ global: &GlobalStateInner,
+ dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
+ exposed_tags: &FxHashSet<BorTag>,
+ ) -> InterpResult<'tcx> {
+ dcx.start_grant(new.perm());
+
+ // Compute where to put the new item.
+ // Either way, we ensure that we insert the new item in a way such that between
+ // `derived_from` and the new one, there are only items *compatible with* `derived_from`.
+ let new_idx = if let Some(access) = access {
+ // Simple case: We are just a regular memory access, and then push our thing on top,
+ // like a regular stack.
+ // This ensures F2b for `Unique`, by removing offending `SharedReadOnly`.
+ self.access(access, derived_from, global, dcx, exposed_tags)?;
+
+ // We insert "as far up as possible": We know only compatible items are remaining
+ // on top of `derived_from`, and we want the new item at the top so that we
+ // get the strongest possible guarantees.
+ // This ensures U1 and F1.
+ self.len()
+ } else {
+ // The tricky case: creating a new SRW permission without actually being an access.
+ assert!(new.perm() == Permission::SharedReadWrite);
+
+ // First we figure out which item grants our parent (`derived_from`) this kind of access.
+ // We use that to determine where to put the new item.
+ let granting_idx = self
+ .find_granting(AccessKind::Write, derived_from, exposed_tags)
+ .map_err(|()| dcx.grant_error(self))?;
+
+ let (Some(granting_idx), ProvenanceExtra::Concrete(_)) = (granting_idx, derived_from) else {
+ // The parent is a wildcard pointer or matched the unknown bottom.
+ // This is approximate. Nobody knows what happened, so forget everything.
+ // The new thing is SRW anyway, so we cannot push it "on top of the unkown part"
+ // (for all we know, it might join an SRW group inside the unknown).
+ trace!("reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown");
+ self.set_unknown_bottom(global.next_ptr_tag);
+ return Ok(());
+ };
+
+ // SharedReadWrite can coexist with "existing loans", meaning they don't act like a write
+ // access. Instead of popping the stack, we insert the item at the place the stack would
+ // be popped to (i.e., we insert it above all the write-compatible items).
+ // This ensures F2b by adding the new item below any potentially existing `SharedReadOnly`.
+ self.find_first_write_incompatible(granting_idx)
+ };
+
+ // Put the new item there.
+ trace!("reborrow: adding item {:?}", new);
+ self.insert(new_idx, new);
+ Ok(())
+ }
+}
+// # Stacked Borrows Core End
+
+/// Integration with the BorTag garbage collector
+impl Stacks {
+ pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<BorTag>) {
+ if self.modified_since_last_gc {
+ for stack in self.stacks.iter_mut_all() {
+ if stack.len() > 64 {
+ stack.retain(live_tags);
+ }
+ }
+ self.modified_since_last_gc = false;
+ }
+ }
+}
+
+impl VisitTags for Stacks {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
+ for tag in self.exposed_tags.iter().copied() {
+ visit(tag);
+ }
+ }
+}
+
+/// Map per-stack operations to higher-level per-location-range operations.
+impl<'tcx> Stacks {
+ /// Creates a new stack with an initial tag. For diagnostic purposes, we also need to know
+ /// the [`AllocId`] of the allocation this is associated with.
+ fn new(
+ size: Size,
+ perm: Permission,
+ tag: BorTag,
+ id: AllocId,
+ machine: &MiriMachine<'_, '_>,
+ ) -> Self {
+ let item = Item::new(tag, perm, false);
+ let stack = Stack::new(item);
+
+ Stacks {
+ stacks: RangeMap::new(size, stack),
+ history: AllocHistory::new(id, item, machine),
+ exposed_tags: FxHashSet::default(),
+ modified_since_last_gc: false,
+ }
+ }
+
+ /// Call `f` on every stack in the range.
+ fn for_each(
+ &mut self,
+ range: AllocRange,
+ mut dcx_builder: DiagnosticCxBuilder<'_, '_, 'tcx>,
+ mut f: impl FnMut(
+ &mut Stack,
+ &mut DiagnosticCx<'_, '_, '_, 'tcx>,
+ &mut FxHashSet<BorTag>,
+ ) -> InterpResult<'tcx>,
+ ) -> InterpResult<'tcx> {
+ self.modified_since_last_gc = true;
+ for (offset, stack) in self.stacks.iter_mut(range.start, range.size) {
+ let mut dcx = dcx_builder.build(&mut self.history, offset);
+ f(stack, &mut dcx, &mut self.exposed_tags)?;
+ dcx_builder = dcx.unbuild();
+ }
+ Ok(())
+ }
+}
+
+/// Glue code to connect with Miri Machine Hooks
+impl Stacks {
+ pub fn new_allocation(
+ id: AllocId,
+ size: Size,
+ state: &mut GlobalStateInner,
+ kind: MemoryKind<MiriMemoryKind>,
+ machine: &MiriMachine<'_, '_>,
+ ) -> Self {
+ let (base_tag, perm) = match kind {
+ // New unique borrow. This tag is not accessible by the program,
+ // so it will only ever be used when using the local directly (i.e.,
+ // not through a pointer). That is, whenever we directly write to a local, this will pop
+ // everything else off the stack, invalidating all previous pointers,
+ // and in particular, *all* raw pointers.
+ MemoryKind::Stack => (state.base_ptr_tag(id, machine), Permission::Unique),
+ // Everything else is shared by default.
+ _ => (state.base_ptr_tag(id, machine), Permission::SharedReadWrite),
+ };
+ Stacks::new(size, perm, base_tag, id, machine)
+ }
+
+ #[inline(always)]
+ pub fn before_memory_read<'tcx, 'mir, 'ecx>(
+ &mut self,
+ alloc_id: AllocId,
+ tag: ProvenanceExtra,
+ range: AllocRange,
+ machine: &'ecx MiriMachine<'mir, 'tcx>,
+ ) -> InterpResult<'tcx>
+ where
+ 'tcx: 'ecx,
+ {
+ trace!(
+ "read access with tag {:?}: {:?}, size {}",
+ tag,
+ Pointer::new(alloc_id, range.start),
+ range.size.bytes()
+ );
+ let dcx = DiagnosticCxBuilder::read(machine, tag, range);
+ let state = machine.borrow_tracker.as_ref().unwrap().borrow();
+ self.for_each(range, dcx, |stack, dcx, exposed_tags| {
+ stack.access(AccessKind::Read, tag, &state, dcx, exposed_tags)
+ })
+ }
+
+ #[inline(always)]
+ pub fn before_memory_write<'tcx>(
+ &mut self,
+ alloc_id: AllocId,
+ tag: ProvenanceExtra,
+ range: AllocRange,
+ machine: &mut MiriMachine<'_, 'tcx>,
+ ) -> InterpResult<'tcx> {
+ trace!(
+ "write access with tag {:?}: {:?}, size {}",
+ tag,
+ Pointer::new(alloc_id, range.start),
+ range.size.bytes()
+ );
+ let dcx = DiagnosticCxBuilder::write(machine, tag, range);
+ let state = machine.borrow_tracker.as_ref().unwrap().borrow();
+ self.for_each(range, dcx, |stack, dcx, exposed_tags| {
+ stack.access(AccessKind::Write, tag, &state, dcx, exposed_tags)
+ })
+ }
+
+ #[inline(always)]
+ pub fn before_memory_deallocation<'tcx>(
+ &mut self,
+ alloc_id: AllocId,
+ tag: ProvenanceExtra,
+ range: AllocRange,
+ machine: &mut MiriMachine<'_, 'tcx>,
+ ) -> InterpResult<'tcx> {
+ trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes());
+ let dcx = DiagnosticCxBuilder::dealloc(machine, tag);
+ let state = machine.borrow_tracker.as_ref().unwrap().borrow();
+ self.for_each(range, dcx, |stack, dcx, exposed_tags| {
+ stack.dealloc(tag, &state, dcx, exposed_tags)
+ })?;
+ Ok(())
+ }
+}
+
+/// Retagging/reborrowing. There is some policy in here, such as which permissions
+/// to grant for which references, and when to add protectors.
+impl<'mir: 'ecx, 'tcx: 'mir, 'ecx> EvalContextPrivExt<'mir, 'tcx, 'ecx>
+ for crate::MiriInterpCx<'mir, 'tcx>
+{
+}
+trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> {
+ /// Returns the `AllocId` the reborrow was done in, if some actual borrow stack manipulation
+ /// happened.
+ fn sb_reborrow(
+ &mut self,
+ place: &MPlaceTy<'tcx, Provenance>,
+ size: Size,
+ new_perm: NewPermission,
+ new_tag: BorTag,
+ retag_cause: RetagCause, // What caused this retag, for diagnostics only
+ ) -> InterpResult<'tcx, Option<AllocId>> {
+ let this = self.eval_context_mut();
+
+ // It is crucial that this gets called on all code paths, to ensure we track tag creation.
+ let log_creation = |this: &MiriInterpCx<'mir, 'tcx>,
+ loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag
+ -> InterpResult<'tcx> {
+ let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
+ let ty = place.layout.ty;
+ if global.tracked_pointer_tags.contains(&new_tag) {
+ let mut kind_str = String::new();
+ match new_perm {
+ NewPermission::Uniform { perm, .. } =>
+ write!(kind_str, "{perm:?} permission").unwrap(),
+ NewPermission::FreezeSensitive { freeze_perm, .. } if ty.is_freeze(*this.tcx, this.param_env()) =>
+ write!(kind_str, "{freeze_perm:?} permission").unwrap(),
+ NewPermission::FreezeSensitive { freeze_perm, nonfreeze_perm, .. } =>
+ write!(kind_str, "{freeze_perm:?}/{nonfreeze_perm:?} permission for frozen/non-frozen parts").unwrap(),
+ }
+ write!(kind_str, " (pointee type {ty})").unwrap();
+ this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
+ new_tag.inner(),
+ Some(kind_str),
+ loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, size), orig_tag)),
+ ));
+ }
+ drop(global); // don't hold that reference any longer than we have to
+
+ let Some((alloc_id, base_offset, orig_tag)) = loc else {
+ return Ok(())
+ };
+
+ let (_size, _align, alloc_kind) = this.get_alloc_info(alloc_id);
+ match alloc_kind {
+ AllocKind::LiveData => {
+ // This should have alloc_extra data, but `get_alloc_extra` can still fail
+ // if converting this alloc_id from a global to a local one
+ // uncovers a non-supported `extern static`.
+ let extra = this.get_alloc_extra(alloc_id)?;
+ let mut stacked_borrows = extra
+ .borrow_tracker_sb()
+ .borrow_mut();
+ // Note that we create a *second* `DiagnosticCxBuilder` below for the actual retag.
+ // FIXME: can this be done cleaner?
+ let dcx = DiagnosticCxBuilder::retag(
+ &this.machine,
+ retag_cause,
+ new_tag,
+ orig_tag,
+ alloc_range(base_offset, size),
+ );
+ let mut dcx = dcx.build(&mut stacked_borrows.history, base_offset);
+ dcx.log_creation();
+ if new_perm.protector().is_some() {
+ dcx.log_protector();
+ }
+ },
+ AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
+ // No stacked borrows on these allocations.
+ }
+ }
+ Ok(())
+ };
+
+ if size == Size::ZERO {
+ trace!(
+ "reborrow of size 0: reference {:?} derived from {:?} (pointee {})",
+ new_tag,
+ place.ptr,
+ place.layout.ty,
+ );
+ // Don't update any stacks for a zero-sized access; borrow stacks are per-byte and this
+ // touches no bytes so there is no stack to put this tag in.
+ // However, if the pointer for this operation points at a real allocation we still
+ // record where it was created so that we can issue a helpful diagnostic if there is an
+ // attempt to use it for a non-zero-sized access.
+ // Dangling slices are a common case here; it's valid to get their length but with raw
+ // pointer tagging for example all calls to get_unchecked on them are invalid.
+ if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr) {
+ log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
+ return Ok(Some(alloc_id));
+ }
+ // This pointer doesn't come with an AllocId. :shrug:
+ log_creation(this, None)?;
+ return Ok(None);
+ }
+
+ let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr)?;
+ log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
+
+ // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
+ let (alloc_size, _) = this.get_live_alloc_size_and_align(alloc_id)?;
+ if base_offset + size > alloc_size {
+ throw_ub!(PointerOutOfBounds {
+ alloc_id,
+ alloc_size,
+ ptr_offset: this.machine_usize_to_isize(base_offset.bytes()),
+ ptr_size: size,
+ msg: CheckInAllocMsg::InboundsTest
+ });
+ }
+
+ trace!(
+ "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
+ new_tag,
+ orig_tag,
+ place.layout.ty,
+ Pointer::new(alloc_id, base_offset),
+ size.bytes()
+ );
+
+ if let Some(protect) = new_perm.protector() {
+ // See comment in `Stack::item_invalidated` for why we store the tag twice.
+ this.frame_mut().extra.borrow_tracker.as_mut().unwrap().protected_tags.push(new_tag);
+ this.machine
+ .borrow_tracker
+ .as_mut()
+ .unwrap()
+ .get_mut()
+ .protected_tags
+ .insert(new_tag, protect);
+ }
+
+ // Update the stacks, according to the new permission information we are given.
+ match new_perm {
+ NewPermission::Uniform { perm, access, protector } => {
+ assert!(perm != Permission::SharedReadOnly);
+ // Here we can avoid `borrow()` calls because we have mutable references.
+ // Note that this asserts that the allocation is mutable -- but since we are creating a
+ // mutable pointer, that seems reasonable.
+ let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc_id)?;
+ let stacked_borrows = alloc_extra.borrow_tracker_sb_mut().get_mut();
+ let item = Item::new(new_tag, perm, protector.is_some());
+ let range = alloc_range(base_offset, size);
+ let global = machine.borrow_tracker.as_ref().unwrap().borrow();
+ let dcx = DiagnosticCxBuilder::retag(
+ machine,
+ retag_cause,
+ new_tag,
+ orig_tag,
+ alloc_range(base_offset, size),
+ );
+ stacked_borrows.for_each(range, dcx, |stack, dcx, exposed_tags| {
+ stack.grant(orig_tag, item, access, &global, dcx, exposed_tags)
+ })?;
+ drop(global);
+ if let Some(access) = access {
+ assert_eq!(access, AccessKind::Write);
+ // Make sure the data race model also knows about this.
+ if let Some(data_race) = alloc_extra.data_race.as_mut() {
+ data_race.write(alloc_id, range, machine)?;
+ }
+ }
+ }
+ NewPermission::FreezeSensitive {
+ freeze_perm,
+ freeze_access,
+ freeze_protector,
+ nonfreeze_perm,
+ nonfreeze_access,
+ } => {
+ // The permission is not uniform across the entire range!
+ // We need a frozen-sensitive reborrow.
+ // We have to use shared references to alloc/memory_extra here since
+ // `visit_freeze_sensitive` needs to access the global state.
+ let alloc_extra = this.get_alloc_extra(alloc_id)?;
+ let mut stacked_borrows = alloc_extra.borrow_tracker_sb().borrow_mut();
+ this.visit_freeze_sensitive(place, size, |mut range, frozen| {
+ // Adjust range.
+ range.start += base_offset;
+ // We are only ever `SharedReadOnly` inside the frozen bits.
+ let (perm, access, protector) = if frozen {
+ (freeze_perm, freeze_access, freeze_protector)
+ } else {
+ (nonfreeze_perm, nonfreeze_access, None)
+ };
+ let item = Item::new(new_tag, perm, protector.is_some());
+ let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
+ let dcx = DiagnosticCxBuilder::retag(
+ &this.machine,
+ retag_cause,
+ new_tag,
+ orig_tag,
+ alloc_range(base_offset, size),
+ );
+ stacked_borrows.for_each(range, dcx, |stack, dcx, exposed_tags| {
+ stack.grant(orig_tag, item, access, &global, dcx, exposed_tags)
+ })?;
+ drop(global);
+ if let Some(access) = access {
+ assert_eq!(access, AccessKind::Read);
+ // Make sure the data race model also knows about this.
+ if let Some(data_race) = alloc_extra.data_race.as_ref() {
+ data_race.read(alloc_id, range, &this.machine)?;
+ }
+ }
+ Ok(())
+ })?;
+ }
+ }
+
+ Ok(Some(alloc_id))
+ }
+
+ /// Retags an indidual pointer, returning the retagged version.
+ /// `kind` indicates what kind of reference is being created.
+ fn sb_retag_reference(
+ &mut self,
+ val: &ImmTy<'tcx, Provenance>,
+ new_perm: NewPermission,
+ cause: RetagCause, // What caused this retag, for diagnostics only
+ ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
+ let this = self.eval_context_mut();
+ // We want a place for where the ptr *points to*, so we get one.
+ let place = this.ref_to_mplace(val)?;
+ let size = this.size_and_align_of_mplace(&place)?.map(|(size, _)| size);
+ // FIXME: If we cannot determine the size (because the unsized tail is an `extern type`),
+ // bail out -- we cannot reasonably figure out which memory range to reborrow.
+ // See https://github.com/rust-lang/unsafe-code-guidelines/issues/276.
+ let size = match size {
+ Some(size) => size,
+ None => return Ok(val.clone()),
+ };
+
+ // Compute new borrow.
+ let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
+
+ // Reborrow.
+ let alloc_id = this.sb_reborrow(&place, size, new_perm, new_tag, cause)?;
+
+ // Adjust pointer.
+ let new_place = place.map_provenance(|p| {
+ p.map(|prov| {
+ match alloc_id {
+ Some(alloc_id) => {
+ // If `reborrow` could figure out the AllocId of this ptr, hard-code it into the new one.
+ // Even if we started out with a wildcard, this newly retagged pointer is tied to that allocation.
+ Provenance::Concrete { alloc_id, tag: new_tag }
+ }
+ None => {
+ // Looks like this has to stay a wildcard pointer.
+ assert!(matches!(prov, Provenance::Wildcard));
+ Provenance::Wildcard
+ }
+ }
+ })
+ });
+
+ // Return new pointer.
+ Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
+ }
+}
+
+impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
+pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
+ fn sb_retag_ptr_value(
+ &mut self,
+ kind: RetagKind,
+ val: &ImmTy<'tcx, Provenance>,
+ ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
+ let this = self.eval_context_mut();
+ let new_perm = NewPermission::from_ref_ty(val.layout.ty, kind, this);
+ let retag_cause = match kind {
+ RetagKind::TwoPhase { .. } => RetagCause::TwoPhase,
+ RetagKind::FnEntry => unreachable!(),
+ RetagKind::Raw | RetagKind::Default => RetagCause::Normal,
+ };
+ this.sb_retag_reference(&val, new_perm, retag_cause)
+ }
+
+ fn sb_retag_place_contents(
+ &mut self,
+ kind: RetagKind,
+ place: &PlaceTy<'tcx, Provenance>,
+ ) -> InterpResult<'tcx> {
+ let this = self.eval_context_mut();
+ let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields;
+ let retag_cause = match kind {
+ RetagKind::Raw | RetagKind::TwoPhase { .. } => unreachable!(), // these can only happen in `retag_ptr_value`
+ RetagKind::FnEntry => RetagCause::FnEntry,
+ RetagKind::Default => RetagCause::Normal,
+ };
+ let mut visitor = RetagVisitor { ecx: this, kind, retag_cause, retag_fields };
+ return visitor.visit_value(place);
+
+ // The actual visitor.
+ struct RetagVisitor<'ecx, 'mir, 'tcx> {
+ ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>,
+ kind: RetagKind,
+ retag_cause: RetagCause,
+ retag_fields: RetagFields,
+ }
+ impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> {
+ #[inline(always)] // yes this helps in our benchmarks
+ fn retag_ptr_inplace(
+ &mut self,
+ place: &PlaceTy<'tcx, Provenance>,
+ new_perm: NewPermission,
+ retag_cause: RetagCause,
+ ) -> InterpResult<'tcx> {
+ let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
+ let val = self.ecx.sb_retag_reference(&val, new_perm, retag_cause)?;
+ self.ecx.write_immediate(*val, place)?;
+ Ok(())
+ }
+ }
+ impl<'ecx, 'mir, 'tcx> MutValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>>
+ for RetagVisitor<'ecx, 'mir, 'tcx>
+ {
+ type V = PlaceTy<'tcx, Provenance>;
+
+ #[inline(always)]
+ fn ecx(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> {
+ self.ecx
+ }
+
+ fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
+ // Boxes get a weak protectors, since they may be deallocated.
+ let new_perm = NewPermission::Uniform {
+ perm: Permission::Unique,
+ access: Some(AccessKind::Write),
+ protector: (self.kind == RetagKind::FnEntry)
+ .then_some(ProtectorKind::WeakProtector),
+ };
+ self.retag_ptr_inplace(place, new_perm, self.retag_cause)
+ }
+
+ fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
+ // If this place is smaller than a pointer, we know that it can't contain any
+ // pointers we need to retag, so we can stop recursion early.
+ // This optimization is crucial for ZSTs, because they can contain way more fields
+ // than we can ever visit.
+ if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
+ return Ok(());
+ }
+
+ // Check the type of this value to see what to do with it (retag, or recurse).
+ match place.layout.ty.kind() {
+ ty::Ref(..) => {
+ let new_perm =
+ NewPermission::from_ref_ty(place.layout.ty, self.kind, self.ecx);
+ self.retag_ptr_inplace(place, new_perm, self.retag_cause)?;
+ }
+ ty::RawPtr(..) => {
+ // We do *not* want to recurse into raw pointers -- wide raw pointers have
+ // fields, and for dyn Trait pointees those can have reference type!
+ }
+ ty::Adt(adt, _) if adt.is_box() => {
+ // Recurse for boxes, they require some tricky handling and will end up in `visit_box` above.
+ // (Yes this means we technically also recursively retag the allocator itself
+ // even if field retagging is not enabled. *shrug*)
+ self.walk_value(place)?;
+ }
+ _ => {
+ // Not a reference/pointer/box. Only recurse if configured appropriately.
+ let recurse = match self.retag_fields {
+ RetagFields::No => false,
+ RetagFields::Yes => true,
+ RetagFields::OnlyScalar => {
+ // Matching `ArgAbi::new` at the time of writing, only fields of
+ // `Scalar` and `ScalarPair` ABI are considered.
+ matches!(place.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..))
+ }
+ };
+ if recurse {
+ self.walk_value(place)?;
+ }
+ }
+ }
+
+ Ok(())
+ }
+ }
+ }
+
+ /// After a stack frame got pushed, retag the return place so that we are sure
+ /// it does not alias with anything.
+ ///
+ /// This is a HACK because there is nothing in MIR that would make the retag
+ /// explicit. Also see <https://github.com/rust-lang/rust/issues/71117>.
+ fn sb_retag_return_place(&mut self) -> InterpResult<'tcx> {
+ let this = self.eval_context_mut();
+ let return_place = &this.frame().return_place;
+ if return_place.layout.is_zst() {
+ // There may not be any memory here, nothing to do.
+ return Ok(());
+ }
+ // We need this to be in-memory to use tagged pointers.
+ let return_place = this.force_allocation(&return_place.clone())?;
+
+ // We have to turn the place into a pointer to use the existing code.
+ // (The pointer type does not matter, so we use a raw pointer.)
+ let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?;
+ let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
+ // Reborrow it. With protection! That is part of the point.
+ let new_perm = NewPermission::Uniform {
+ perm: Permission::Unique,
+ access: Some(AccessKind::Write),
+ protector: Some(ProtectorKind::StrongProtector),
+ };
+ let val = this.sb_retag_reference(&val, new_perm, RetagCause::FnReturn)?;
+ // And use reborrowed pointer for return place.
+ let return_place = this.ref_to_mplace(&val)?;
+ this.frame_mut().return_place = return_place.into();
+
+ Ok(())
+ }
+
+ /// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
+ fn sb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
+ let this = self.eval_context_mut();
+
+ // Function pointers and dead objects don't have an alloc_extra so we ignore them.
+ // This is okay because accessing them is UB anyway, no need for any Stacked Borrows checks.
+ // NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
+ let (_size, _align, kind) = this.get_alloc_info(alloc_id);
+ match kind {
+ AllocKind::LiveData => {
+ // This should have alloc_extra data, but `get_alloc_extra` can still fail
+ // if converting this alloc_id from a global to a local one
+ // uncovers a non-supported `extern static`.
+ let alloc_extra = this.get_alloc_extra(alloc_id)?;
+ trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
+ alloc_extra.borrow_tracker_sb().borrow_mut().exposed_tags.insert(tag);
+ }
+ AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
+ // No stacked borrows on these allocations.
+ }
+ }
+ Ok(())
+ }
+
+ fn print_stacks(&mut self, alloc_id: AllocId) -> InterpResult<'tcx> {
+ let this = self.eval_context_mut();
+ let alloc_extra = this.get_alloc_extra(alloc_id)?;
+ let stacks = alloc_extra.borrow_tracker_sb().borrow();
+ for (range, stack) in stacks.stacks.iter_all() {
+ print!("{range:?}: [");
+ if let Some(bottom) = stack.unknown_bottom() {
+ print!(" unknown-bottom(..{bottom:?})");
+ }
+ for i in 0..stack.len() {
+ let item = stack.get(i).unwrap();
+ print!(" {:?}{:?}", item.perm(), item.tag());
+ }
+ println!(" ]");
+ }
+ Ok(())
+ }
+}
--- /dev/null
+#[cfg(feature = "stack-cache")]
+use std::ops::Range;
+
+use rustc_data_structures::fx::FxHashSet;
+
+use crate::borrow_tracker::{
+ stacked_borrows::{Item, Permission},
+ AccessKind, BorTag,
+};
+use crate::ProvenanceExtra;
+
+/// Exactly what cache size we should use is a difficult tradeoff. There will always be some
+/// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up
+/// falling back to linear searches of the borrow stack very often.
+/// The cost of making this value too large is that the loop in `Stack::insert` which ensures the
+/// entries in the cache stay correct after an insert becomes expensive.
+#[cfg(feature = "stack-cache")]
+const CACHE_LEN: usize = 32;
+
+/// Extra per-location state.
+#[derive(Clone, Debug)]
+pub struct Stack {
+ /// Used *mostly* as a stack; never empty.
+ /// Invariants:
+ /// * Above a `SharedReadOnly` there can only be more `SharedReadOnly`.
+ /// * Except for `Untagged`, no tag occurs in the stack more than once.
+ borrows: Vec<Item>,
+ /// If this is `Some(id)`, then the actual current stack is unknown. This can happen when
+ /// wildcard pointers are used to access this location. What we do know is that `borrows` are at
+ /// the top of the stack, and below it are arbitrarily many items whose `tag` is strictly less
+ /// than `id`.
+ /// When the bottom is unknown, `borrows` always has a `SharedReadOnly` or `Unique` at the bottom;
+ /// we never have the unknown-to-known boundary in an SRW group.
+ unknown_bottom: Option<BorTag>,
+
+ /// A small LRU cache of searches of the borrow stack.
+ #[cfg(feature = "stack-cache")]
+ cache: StackCache,
+ /// On a read, we need to disable all `Unique` above the granting item. We can avoid most of
+ /// this scan by keeping track of the region of the borrow stack that may contain `Unique`s.
+ #[cfg(feature = "stack-cache")]
+ unique_range: Range<usize>,
+}
+
+impl Stack {
+ pub fn retain(&mut self, tags: &FxHashSet<BorTag>) {
+ let mut first_removed = None;
+
+ // We never consider removing the bottom-most tag. For stacks without an unknown
+ // bottom this preserves the base tag.
+ // Note that the algorithm below is based on considering the tag at read_idx - 1,
+ // so precisely considering the tag at index 0 for removal when we have an unknown
+ // bottom would complicate the implementation. The simplification of not considering
+ // it does not have a significant impact on the degree to which the GC mititages
+ // memory growth.
+ let mut read_idx = 1;
+ let mut write_idx = read_idx;
+ while read_idx < self.borrows.len() {
+ let left = self.borrows[read_idx - 1];
+ let this = self.borrows[read_idx];
+ let should_keep = match this.perm() {
+ // SharedReadWrite is the simplest case, if it's unreachable we can just remove it.
+ Permission::SharedReadWrite => tags.contains(&this.tag()),
+ // Only retain a Disabled tag if it is terminating a SharedReadWrite block.
+ Permission::Disabled => left.perm() == Permission::SharedReadWrite,
+ // Unique and SharedReadOnly can terminate a SharedReadWrite block, so only remove
+ // them if they are both unreachable and not directly after a SharedReadWrite.
+ Permission::Unique | Permission::SharedReadOnly =>
+ left.perm() == Permission::SharedReadWrite || tags.contains(&this.tag()),
+ };
+
+ if should_keep {
+ if read_idx != write_idx {
+ self.borrows[write_idx] = self.borrows[read_idx];
+ }
+ write_idx += 1;
+ } else if first_removed.is_none() {
+ first_removed = Some(read_idx);
+ }
+
+ read_idx += 1;
+ }
+ self.borrows.truncate(write_idx);
+
+ #[cfg(not(feature = "stack-cache"))]
+ drop(first_removed); // This is only needed for the stack-cache
+
+ #[cfg(feature = "stack-cache")]
+ if let Some(first_removed) = first_removed {
+ // Either end of unique_range may have shifted, all we really know is that we can't
+ // have introduced a new Unique.
+ if !self.unique_range.is_empty() {
+ self.unique_range = 0..self.len();
+ }
+
+ // Replace any Items which have been collected with the base item, a known-good value.
+ for i in 0..CACHE_LEN {
+ if self.cache.idx[i] >= first_removed {
+ self.cache.items[i] = self.borrows[0];
+ self.cache.idx[i] = 0;
+ }
+ }
+ }
+ }
+}
+
+/// A very small cache of searches of a borrow stack, mapping `Item`s to their position in said stack.
+///
+/// It may seem like maintaining this cache is a waste for small stacks, but
+/// (a) iterating over small fixed-size arrays is super fast, and (b) empirically this helps *a lot*,
+/// probably because runtime is dominated by large stacks.
+#[cfg(feature = "stack-cache")]
+#[derive(Clone, Debug)]
+struct StackCache {
+ items: [Item; CACHE_LEN], // Hot in find_granting
+ idx: [usize; CACHE_LEN], // Hot in grant
+}
+
+#[cfg(feature = "stack-cache")]
+impl StackCache {
+ /// When a tag is used, we call this function to add or refresh it in the cache.
+ ///
+ /// We use the position in the cache to represent how recently a tag was used; the first position
+ /// is the most recently used tag. So an add shifts every element towards the end, and inserts
+ /// the new element at the start. We lose the last element.
+ /// This strategy is effective at keeping the most-accessed items in the cache, but it costs a
+ /// linear shift across the entire cache when we add a new tag.
+ fn add(&mut self, idx: usize, item: Item) {
+ self.items.copy_within(0..CACHE_LEN - 1, 1);
+ self.items[0] = item;
+ self.idx.copy_within(0..CACHE_LEN - 1, 1);
+ self.idx[0] = idx;
+ }
+}
+
+impl PartialEq for Stack {
+ fn eq(&self, other: &Self) -> bool {
+ // All the semantics of Stack are in self.borrows, everything else is caching
+ self.borrows == other.borrows
+ }
+}
+
+impl Eq for Stack {}
+
+impl<'tcx> Stack {
+ /// Panics if any of the caching mechanisms have broken,
+ /// - The StackCache indices don't refer to the parallel items,
+ /// - There are no Unique items outside of first_unique..last_unique
+ #[cfg(all(feature = "stack-cache", debug_assertions))]
+ fn verify_cache_consistency(&self) {
+ // Only a full cache needs to be valid. Also see the comments in find_granting_cache
+ // and set_unknown_bottom.
+ if self.borrows.len() >= CACHE_LEN {
+ for (tag, stack_idx) in self.cache.items.iter().zip(self.cache.idx.iter()) {
+ assert_eq!(self.borrows[*stack_idx], *tag);
+ }
+ }
+
+ // Check that all Unique items fall within unique_range.
+ for (idx, item) in self.borrows.iter().enumerate() {
+ if item.perm() == Permission::Unique {
+ assert!(
+ self.unique_range.contains(&idx),
+ "{:?} {:?}",
+ self.unique_range,
+ self.borrows
+ );
+ }
+ }
+
+ // Check that the unique_range is a valid index into the borrow stack.
+ // This asserts that the unique_range's start <= end.
+ let _uniques = &self.borrows[self.unique_range.clone()];
+
+ // We cannot assert that the unique range is precise.
+ // Both ends may shift around when `Stack::retain` is called. Additionally,
+ // when we pop items within the unique range, setting the end of the range precisely
+ // requires doing a linear search of the borrow stack, which is exactly the kind of
+ // operation that all this caching exists to avoid.
+ }
+
+ /// Find the item granting the given kind of access to the given tag, and return where
+ /// it is on the stack. For wildcard tags, the given index is approximate, but if *no*
+ /// index is given it means the match was *not* in the known part of the stack.
+ /// `Ok(None)` indicates it matched the "unknown" part of the stack.
+ /// `Err` indicates it was not found.
+ pub(super) fn find_granting(
+ &mut self,
+ access: AccessKind,
+ tag: ProvenanceExtra,
+ exposed_tags: &FxHashSet<BorTag>,
+ ) -> Result<Option<usize>, ()> {
+ #[cfg(all(feature = "stack-cache", debug_assertions))]
+ self.verify_cache_consistency();
+
+ let ProvenanceExtra::Concrete(tag) = tag else {
+ // Handle the wildcard case.
+ // Go search the stack for an exposed tag.
+ if let Some(idx) =
+ self.borrows
+ .iter()
+ .enumerate() // we also need to know *where* in the stack
+ .rev() // search top-to-bottom
+ .find_map(|(idx, item)| {
+ // If the item fits and *might* be this wildcard, use it.
+ if item.perm().grants(access) && exposed_tags.contains(&item.tag()) {
+ Some(idx)
+ } else {
+ None
+ }
+ })
+ {
+ return Ok(Some(idx));
+ }
+ // If we couldn't find it in the stack, check the unknown bottom.
+ return if self.unknown_bottom.is_some() { Ok(None) } else { Err(()) };
+ };
+
+ if let Some(idx) = self.find_granting_tagged(access, tag) {
+ return Ok(Some(idx));
+ }
+
+ // Couldn't find it in the stack; but if there is an unknown bottom it might be there.
+ let found = self.unknown_bottom.is_some_and(|unknown_limit| {
+ tag < unknown_limit // unknown_limit is an upper bound for what can be in the unknown bottom.
+ });
+ if found { Ok(None) } else { Err(()) }
+ }
+
+ fn find_granting_tagged(&mut self, access: AccessKind, tag: BorTag) -> Option<usize> {
+ #[cfg(feature = "stack-cache")]
+ if let Some(idx) = self.find_granting_cache(access, tag) {
+ return Some(idx);
+ }
+
+ // If we didn't find the tag in the cache, fall back to a linear search of the
+ // whole stack, and add the tag to the cache.
+ for (stack_idx, item) in self.borrows.iter().enumerate().rev() {
+ if tag == item.tag() && item.perm().grants(access) {
+ #[cfg(feature = "stack-cache")]
+ self.cache.add(stack_idx, *item);
+ return Some(stack_idx);
+ }
+ }
+ None
+ }
+
+ #[cfg(feature = "stack-cache")]
+ fn find_granting_cache(&mut self, access: AccessKind, tag: BorTag) -> Option<usize> {
+ // This looks like a common-sense optimization; we're going to do a linear search of the
+ // cache or the borrow stack to scan the shorter of the two. This optimization is miniscule
+ // and this check actually ensures we do not access an invalid cache.
+ // When a stack is created and when items are removed from the top of the borrow stack, we
+ // need some valid value to populate the cache. In both cases, we try to use the bottom
+ // item. But when the stack is cleared in `set_unknown_bottom` there is nothing we could
+ // place in the cache that is correct. But due to the way we populate the cache in
+ // `StackCache::add`, we know that when the borrow stack has grown larger than the cache,
+ // every slot in the cache is valid.
+ if self.borrows.len() <= CACHE_LEN {
+ return None;
+ }
+ // Search the cache for the tag we're looking up
+ let cache_idx = self.cache.items.iter().position(|t| t.tag() == tag)?;
+ let stack_idx = self.cache.idx[cache_idx];
+ // If we found the tag, look up its position in the stack to see if it grants
+ // the required permission
+ if self.cache.items[cache_idx].perm().grants(access) {
+ // If it does, and it's not already in the most-recently-used position, re-insert it at
+ // the most-recently-used position. This technically reduces the efficiency of the
+ // cache by duplicating elements, but current benchmarks do not seem to benefit from
+ // avoiding this duplication.
+ // But if the tag is in position 1, avoiding the duplicating add is trivial.
+ // If it does, and it's not already in the most-recently-used position, move it there.
+ // Except if the tag is in position 1, this is equivalent to just a swap, so do that.
+ if cache_idx == 1 {
+ self.cache.items.swap(0, 1);
+ self.cache.idx.swap(0, 1);
+ } else if cache_idx > 1 {
+ self.cache.add(stack_idx, self.cache.items[cache_idx]);
+ }
+ Some(stack_idx)
+ } else {
+ // Tag is in the cache, but it doesn't grant the required permission
+ None
+ }
+ }
+
+ pub fn insert(&mut self, new_idx: usize, new: Item) {
+ self.borrows.insert(new_idx, new);
+
+ #[cfg(feature = "stack-cache")]
+ self.insert_cache(new_idx, new);
+ }
+
+ #[cfg(feature = "stack-cache")]
+ fn insert_cache(&mut self, new_idx: usize, new: Item) {
+ // Adjust the possibly-unique range if an insert occurs before or within it
+ if self.unique_range.start >= new_idx {
+ self.unique_range.start += 1;
+ }
+ if self.unique_range.end >= new_idx {
+ self.unique_range.end += 1;
+ }
+ if new.perm() == Permission::Unique {
+ // If this is the only Unique, set the range to contain just the new item.
+ if self.unique_range.is_empty() {
+ self.unique_range = new_idx..new_idx + 1;
+ } else {
+ // We already have other Unique items, expand the range to include the new item
+ self.unique_range.start = self.unique_range.start.min(new_idx);
+ self.unique_range.end = self.unique_range.end.max(new_idx + 1);
+ }
+ }
+
+ // The above insert changes the meaning of every index in the cache >= new_idx, so now
+ // we need to find every one of those indexes and increment it.
+ // But if the insert is at the end (equivalent to a push), we can skip this step because
+ // it didn't change the position of any other items.
+ if new_idx != self.borrows.len() - 1 {
+ for idx in &mut self.cache.idx {
+ if *idx >= new_idx {
+ *idx += 1;
+ }
+ }
+ }
+
+ // This primes the cache for the next access, which is almost always the just-added tag.
+ self.cache.add(new_idx, new);
+
+ #[cfg(debug_assertions)]
+ self.verify_cache_consistency();
+ }
+
+ /// Construct a new `Stack` using the passed `Item` as the base tag.
+ pub fn new(item: Item) -> Self {
+ Stack {
+ borrows: vec![item],
+ unknown_bottom: None,
+ #[cfg(feature = "stack-cache")]
+ cache: StackCache { idx: [0; CACHE_LEN], items: [item; CACHE_LEN] },
+ #[cfg(feature = "stack-cache")]
+ unique_range: if item.perm() == Permission::Unique { 0..1 } else { 0..0 },
+ }
+ }
+
+ pub fn get(&self, idx: usize) -> Option<Item> {
+ self.borrows.get(idx).cloned()
+ }
+
+ #[allow(clippy::len_without_is_empty)] // Stacks are never empty
+ pub fn len(&self) -> usize {
+ self.borrows.len()
+ }
+
+ pub fn unknown_bottom(&self) -> Option<BorTag> {
+ self.unknown_bottom
+ }
+
+ pub fn set_unknown_bottom(&mut self, tag: BorTag) {
+ // We clear the borrow stack but the lookup cache doesn't support clearing per se. Instead,
+ // there is a check explained in `find_granting_cache` which protects against accessing the
+ // cache when it has been cleared and not yet refilled.
+ self.borrows.clear();
+ self.unknown_bottom = Some(tag);
+ #[cfg(feature = "stack-cache")]
+ {
+ self.unique_range = 0..0;
+ }
+ }
+
+ /// Find all `Unique` elements in this borrow stack above `granting_idx`, pass a copy of them
+ /// to the `visitor`, then set their `Permission` to `Disabled`.
+ pub fn disable_uniques_starting_at(
+ &mut self,
+ disable_start: usize,
+ mut visitor: impl FnMut(Item) -> crate::InterpResult<'tcx>,
+ ) -> crate::InterpResult<'tcx> {
+ #[cfg(feature = "stack-cache")]
+ let unique_range = self.unique_range.clone();
+ #[cfg(not(feature = "stack-cache"))]
+ let unique_range = 0..self.len();
+
+ if disable_start <= unique_range.end {
+ let lower = unique_range.start.max(disable_start);
+ let upper = unique_range.end;
+ for item in &mut self.borrows[lower..upper] {
+ if item.perm() == Permission::Unique {
+ log::trace!("access: disabling item {:?}", item);
+ visitor(*item)?;
+ item.set_permission(Permission::Disabled);
+ // Also update all copies of this item in the cache.
+ #[cfg(feature = "stack-cache")]
+ for it in &mut self.cache.items {
+ if it.tag() == item.tag() {
+ it.set_permission(Permission::Disabled);
+ }
+ }
+ }
+ }
+ }
+
+ #[cfg(feature = "stack-cache")]
+ if disable_start <= self.unique_range.start {
+ // We disabled all Unique items
+ self.unique_range.start = 0;
+ self.unique_range.end = 0;
+ } else {
+ // Truncate the range to only include items up to the index that we started disabling
+ // at.
+ self.unique_range.end = self.unique_range.end.min(disable_start);
+ }
+
+ #[cfg(all(feature = "stack-cache", debug_assertions))]
+ self.verify_cache_consistency();
+
+ Ok(())
+ }
+
+ /// Produces an iterator which iterates over `range` in reverse, and when dropped removes that
+ /// range of `Item`s from this `Stack`.
+ pub fn pop_items_after<V: FnMut(Item) -> crate::InterpResult<'tcx>>(
+ &mut self,
+ start: usize,
+ mut visitor: V,
+ ) -> crate::InterpResult<'tcx> {
+ while self.borrows.len() > start {
+ let item = self.borrows.pop().unwrap();
+ visitor(item)?;
+ }
+
+ #[cfg(feature = "stack-cache")]
+ if !self.borrows.is_empty() {
+ // After we remove from the borrow stack, every aspect of our caching may be invalid, but it is
+ // also possible that the whole cache is still valid. So we call this method to repair what
+ // aspects of the cache are now invalid, instead of resetting the whole thing to a trivially
+ // valid default state.
+ let base_tag = self.borrows[0];
+ let mut removed = 0;
+ let mut cursor = 0;
+ // Remove invalid entries from the cache by rotating them to the end of the cache, then
+ // keep track of how many invalid elements there are and overwrite them with the base tag.
+ // The base tag here serves as a harmless default value.
+ for _ in 0..CACHE_LEN - 1 {
+ if self.cache.idx[cursor] >= start {
+ self.cache.idx[cursor..CACHE_LEN - removed].rotate_left(1);
+ self.cache.items[cursor..CACHE_LEN - removed].rotate_left(1);
+ removed += 1;
+ } else {
+ cursor += 1;
+ }
+ }
+ for i in CACHE_LEN - removed - 1..CACHE_LEN {
+ self.cache.idx[i] = 0;
+ self.cache.items[i] = base_tag;
+ }
+
+ if start <= self.unique_range.start {
+ // We removed all the Unique items
+ self.unique_range = 0..0;
+ } else {
+ // Ensure the range doesn't extend past the new top of the stack
+ self.unique_range.end = self.unique_range.end.min(start);
+ }
+ } else {
+ self.unique_range = 0..0;
+ }
+
+ #[cfg(all(feature = "stack-cache", debug_assertions))]
+ self.verify_cache_consistency();
+ Ok(())
+ }
+}
weak_memory::EvalContextExt as _,
};
-pub type AllocExtra = VClockAlloc;
+pub type AllocState = VClockAlloc;
/// Valid atomic read-write orderings, alias of atomic::Ordering (not non-exhaustive).
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
}
impl VisitTags for VClockAlloc {
- fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
// No tags here.
}
}
}
impl VisitTags for GlobalState {
- fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
// We don't have any tags.
}
}
}
impl<'mir, 'tcx> VisitTags for InitOnce<'mir, 'tcx> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
for waiter in self.waiters.iter() {
waiter.callback.visit_tags(visit);
}
}
impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
for init_once in self.init_onces.iter() {
init_once.visit_tags(visit);
}
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::num::TryFromIntError;
+use std::task::Poll;
use std::time::{Duration, SystemTime};
use log::trace;
use crate::concurrency::data_race;
use crate::concurrency::sync::SynchronizationState;
+use crate::shims::tls;
use crate::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum SchedulingAction {
+enum SchedulingAction {
/// Execute step on the active thread.
ExecuteStep,
/// Execute a timeout callback.
ExecuteTimeoutCallback,
- /// Execute destructors of the active thread.
- ExecuteDtors,
- /// Stop the program.
- Stop,
+ /// Wait for a bit, until there is a timeout to be called.
+ Sleep(Duration),
}
/// Trait for callbacks that can be executed when some event happens, such as after a timeout.
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct ThreadId(u32);
-/// The main thread. When it terminates, the whole application terminates.
-const MAIN_THREAD: ThreadId = ThreadId(0);
-
impl ThreadId {
pub fn to_u32(self) -> u32 {
self.0
thread_name: Option<Vec<u8>>,
/// The virtual call stack.
- stack: Vec<Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>>,
+ stack: Vec<Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>>,
+
+ /// The function to call when the stack ran empty, to figure out what to do next.
+ /// Conceptually, this is the interpreter implementation of the things that happen 'after' the
+ /// Rust language entry point for this thread returns (usually implemented by the C or OS runtime).
+ /// (`None` is an error, it means the callback has not been set up yet or is actively running.)
+ pub(crate) on_stack_empty: Option<StackEmptyCallback<'mir, 'tcx>>,
/// The index of the topmost user-relevant frame in `stack`. This field must contain
/// the value produced by `get_top_user_relevant_frame`.
pub(crate) last_error: Option<MPlaceTy<'tcx, Provenance>>,
}
-impl<'mir, 'tcx> Thread<'mir, 'tcx> {
- /// Check if the thread is done executing (no more stack frames). If yes,
- /// change the state to terminated and return `true`.
- fn check_terminated(&mut self) -> bool {
- if self.state == ThreadState::Enabled {
- if self.stack.is_empty() {
- self.state = ThreadState::Terminated;
- return true;
- }
- }
- false
- }
+pub type StackEmptyCallback<'mir, 'tcx> =
+ Box<dyn FnMut(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, Poll<()>>>;
+impl<'mir, 'tcx> Thread<'mir, 'tcx> {
/// Get the name of the current thread, or `<unnamed>` if it was not set.
fn thread_name(&self) -> &[u8] {
if let Some(ref thread_name) = self.thread_name { thread_name } else { b"<unnamed>" }
}
}
-impl<'mir, 'tcx> Default for Thread<'mir, 'tcx> {
- fn default() -> Self {
+impl<'mir, 'tcx> Thread<'mir, 'tcx> {
+ fn new(name: Option<&str>, on_stack_empty: Option<StackEmptyCallback<'mir, 'tcx>>) -> Self {
Self {
state: ThreadState::Enabled,
- thread_name: None,
+ thread_name: name.map(|name| Vec::from(name.as_bytes())),
stack: Vec::new(),
top_user_relevant_frame: None,
join_status: ThreadJoinStatus::Joinable,
panic_payload: None,
last_error: None,
+ on_stack_empty,
}
}
}
-impl<'mir, 'tcx> Thread<'mir, 'tcx> {
- fn new(name: &str) -> Self {
- let mut thread = Thread::default();
- thread.thread_name = Some(Vec::from(name.as_bytes()));
- thread
- }
-}
-
impl VisitTags for Thread<'_, '_> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Thread {
panic_payload,
last_error,
state: _,
thread_name: _,
join_status: _,
+ on_stack_empty: _, // we assume the closure captures no GC-relevant state
} = self;
panic_payload.visit_tags(visit);
}
}
-impl VisitTags for Frame<'_, '_, Provenance, FrameData<'_>> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+impl VisitTags for Frame<'_, '_, Provenance, FrameExtra<'_>> {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Frame {
return_place,
locals,
timeout_callbacks: FxHashMap<ThreadId, TimeoutCallbackInfo<'mir, 'tcx>>,
}
-impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
- fn default() -> Self {
- let mut threads = IndexVec::new();
- // Create the main thread and add it to the list of threads.
- threads.push(Thread::new("main"));
- Self {
- active_thread: ThreadId::new(0),
- threads,
- sync: SynchronizationState::default(),
- thread_local_alloc_ids: Default::default(),
- yield_active_thread: false,
- timeout_callbacks: FxHashMap::default(),
- }
- }
-}
-
impl VisitTags for ThreadManager<'_, '_> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let ThreadManager {
threads,
thread_local_alloc_ids,
}
}
+impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
+ fn default() -> Self {
+ let mut threads = IndexVec::new();
+ // Create the main thread and add it to the list of threads.
+ threads.push(Thread::new(Some("main"), None));
+ Self {
+ active_thread: ThreadId::new(0),
+ threads,
+ sync: SynchronizationState::default(),
+ thread_local_alloc_ids: Default::default(),
+ yield_active_thread: false,
+ timeout_callbacks: FxHashMap::default(),
+ }
+ }
+}
+
impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
- pub(crate) fn init(ecx: &mut MiriInterpCx<'mir, 'tcx>) {
+ pub(crate) fn init(
+ ecx: &mut MiriInterpCx<'mir, 'tcx>,
+ on_main_stack_empty: StackEmptyCallback<'mir, 'tcx>,
+ ) {
+ ecx.machine.threads.threads[ThreadId::new(0)].on_stack_empty = Some(on_main_stack_empty);
if ecx.tcx.sess.target.os.as_ref() != "windows" {
// The main thread can *not* be joined on except on windows.
ecx.machine.threads.threads[ThreadId::new(0)].join_status = ThreadJoinStatus::Detached;
}
/// Borrow the stack of the active thread.
- pub fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>] {
+ pub fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>] {
&self.threads[self.active_thread].stack
}
/// Mutably borrow the stack of the active thread.
fn active_thread_stack_mut(
&mut self,
- ) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>> {
+ ) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> {
&mut self.threads[self.active_thread].stack
}
pub fn all_stacks(
&self,
- ) -> impl Iterator<Item = &[Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>]> {
+ ) -> impl Iterator<Item = &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>]> {
self.threads.iter().map(|t| &t.stack[..])
}
/// Create a new thread and returns its id.
- fn create_thread(&mut self) -> ThreadId {
+ fn create_thread(&mut self, on_stack_empty: StackEmptyCallback<'mir, 'tcx>) -> ThreadId {
let new_thread_id = ThreadId::new(self.threads.len());
- self.threads.push(Default::default());
+ self.threads.push(Thread::new(None, Some(on_stack_empty)));
new_thread_id
}
}
/// Get a mutable borrow of the currently active thread.
- fn active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx> {
+ pub fn active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx> {
&mut self.threads[self.active_thread]
}
/// long as we can and switch only when we have to (the active thread was
/// blocked, terminated, or has explicitly asked to be preempted).
fn schedule(&mut self, clock: &Clock) -> InterpResult<'tcx, SchedulingAction> {
- // Check whether the thread has **just** terminated (`check_terminated`
- // checks whether the thread has popped all its stack and if yes, sets
- // the thread state to terminated).
- if self.threads[self.active_thread].check_terminated() {
- return Ok(SchedulingAction::ExecuteDtors);
- }
- // If we get here again and the thread is *still* terminated, there are no more dtors to run.
- if self.threads[MAIN_THREAD].state == ThreadState::Terminated {
- // The main thread terminated; stop the program.
- // We do *not* run TLS dtors of remaining threads, which seems to match rustc behavior.
- return Ok(SchedulingAction::Stop);
- }
// This thread and the program can keep going.
if self.threads[self.active_thread].state == ThreadState::Enabled
&& !self.yield_active_thread
// The currently active thread is still enabled, just continue with it.
return Ok(SchedulingAction::ExecuteStep);
}
- // The active thread yielded. Let's see if there are any timeouts to take care of. We do
- // this *before* running any other thread, to ensure that timeouts "in the past" fire before
- // any other thread can take an action. This ensures that for `pthread_cond_timedwait`, "an
- // error is returned if [...] the absolute time specified by abstime has already been passed
- // at the time of the call".
+ // The active thread yielded or got terminated. Let's see if there are any timeouts to take
+ // care of. We do this *before* running any other thread, to ensure that timeouts "in the
+ // past" fire before any other thread can take an action. This ensures that for
+ // `pthread_cond_timedwait`, "an error is returned if [...] the absolute time specified by
+ // abstime has already been passed at the time of the call".
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html>
let potential_sleep_time =
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time(clock)).min();
if potential_sleep_time == Some(Duration::new(0, 0)) {
return Ok(SchedulingAction::ExecuteTimeoutCallback);
}
- // No callbacks scheduled, pick a regular thread to execute.
+ // No callbacks immediately scheduled, pick a regular thread to execute.
// The active thread blocked or yielded. So we go search for another enabled thread.
// Crucially, we start searching at the current active thread ID, rather than at 0, since we
// want to avoid always scheduling threads 0 and 1 without ever making progress in thread 2.
// All threads are currently blocked, but we have unexecuted
// timeout_callbacks, which may unblock some of the threads. Hence,
// sleep until the first callback.
-
- clock.sleep(sleep_time);
- Ok(SchedulingAction::ExecuteTimeoutCallback)
+ Ok(SchedulingAction::Sleep(sleep_time))
} else {
throw_machine_stop!(TerminationInfo::Deadlock);
}
}
}
+impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {}
+trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
+ /// Execute a timeout callback on the callback's thread.
+ #[inline]
+ fn run_timeout_callback(&mut self) -> InterpResult<'tcx> {
+ let this = self.eval_context_mut();
+ let (thread, callback) = if let Some((thread, callback)) =
+ this.machine.threads.get_ready_callback(&this.machine.clock)
+ {
+ (thread, callback)
+ } else {
+ // get_ready_callback can return None if the computer's clock
+ // was shifted after calling the scheduler and before the call
+ // to get_ready_callback (see issue
+ // https://github.com/rust-lang/miri/issues/1763). In this case,
+ // just do nothing, which effectively just returns to the
+ // scheduler.
+ return Ok(());
+ };
+ // This back-and-forth with `set_active_thread` is here because of two
+ // design decisions:
+ // 1. Make the caller and not the callback responsible for changing
+ // thread.
+ // 2. Make the scheduler the only place that can change the active
+ // thread.
+ let old_thread = this.set_active_thread(thread);
+ callback.call(this)?;
+ this.set_active_thread(old_thread);
+ Ok(())
+ }
+
+ #[inline]
+ fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {
+ let this = self.eval_context_mut();
+ let mut callback = this
+ .active_thread_mut()
+ .on_stack_empty
+ .take()
+ .expect("`on_stack_empty` not set up, or already running");
+ let res = callback(this)?;
+ this.active_thread_mut().on_stack_empty = Some(callback);
+ Ok(res)
+ }
+}
+
// Public interface to thread management.
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}
+ /// Start a regular (non-main) thread.
#[inline]
- fn create_thread(&mut self) -> ThreadId {
- let this = self.eval_context_mut();
- let id = this.machine.threads.create_thread();
- if let Some(data_race) = &mut this.machine.data_race {
- data_race.thread_created(&this.machine.threads, id);
- }
- id
- }
-
- #[inline]
- fn start_thread(
+ fn start_regular_thread(
&mut self,
thread: Option<MPlaceTy<'tcx, Provenance>>,
start_routine: Pointer<Option<Provenance>>,
let this = self.eval_context_mut();
// Create the new thread
- let new_thread_id = this.create_thread();
+ let new_thread_id = this.machine.threads.create_thread({
+ let mut state = tls::TlsDtorsState::default();
+ Box::new(move |m| state.on_stack_empty(m))
+ });
+ if let Some(data_race) = &mut this.machine.data_race {
+ data_race.thread_created(&this.machine.threads, new_thread_id);
+ }
// Write the current thread-id, switch to the next thread later
// to treat this write operation as occuring on the current thread.
this.machine.threads.get_total_thread_count()
}
- #[inline]
- fn has_terminated(&self, thread_id: ThreadId) -> bool {
- let this = self.eval_context_ref();
- this.machine.threads.has_terminated(thread_id)
- }
-
#[inline]
fn have_all_terminated(&self) -> bool {
let this = self.eval_context_ref();
}
#[inline]
- fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>] {
+ fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>] {
let this = self.eval_context_ref();
this.machine.threads.active_thread_stack()
}
#[inline]
fn active_thread_stack_mut(
&mut self,
- ) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>> {
+ ) -> &mut Vec<Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> {
let this = self.eval_context_mut();
this.machine.threads.active_thread_stack_mut()
}
where
'mir: 'c,
{
- let this = self.eval_context_ref();
- this.machine.threads.get_thread_name(thread)
+ self.eval_context_ref().machine.threads.get_thread_name(thread)
}
#[inline]
fn block_thread(&mut self, thread: ThreadId) {
- let this = self.eval_context_mut();
- this.machine.threads.block_thread(thread);
+ self.eval_context_mut().machine.threads.block_thread(thread);
}
#[inline]
fn unblock_thread(&mut self, thread: ThreadId) {
- let this = self.eval_context_mut();
- this.machine.threads.unblock_thread(thread);
+ self.eval_context_mut().machine.threads.unblock_thread(thread);
}
#[inline]
fn yield_active_thread(&mut self) {
- let this = self.eval_context_mut();
- this.machine.threads.yield_active_thread();
+ self.eval_context_mut().machine.threads.yield_active_thread();
}
#[inline]
this.machine.threads.unregister_timeout_callback_if_exists(thread);
}
- /// Execute a timeout callback on the callback's thread.
- #[inline]
- fn run_timeout_callback(&mut self) -> InterpResult<'tcx> {
- let this = self.eval_context_mut();
- let (thread, callback) = if let Some((thread, callback)) =
- this.machine.threads.get_ready_callback(&this.machine.clock)
- {
- (thread, callback)
- } else {
- // get_ready_callback can return None if the computer's clock
- // was shifted after calling the scheduler and before the call
- // to get_ready_callback (see issue
- // https://github.com/rust-lang/miri/issues/1763). In this case,
- // just do nothing, which effectively just returns to the
- // scheduler.
- return Ok(());
- };
- // This back-and-forth with `set_active_thread` is here because of two
- // design decisions:
- // 1. Make the caller and not the callback responsible for changing
- // thread.
- // 2. Make the scheduler the only place that can change the active
- // thread.
- let old_thread = this.set_active_thread(thread);
- callback.call(this)?;
- this.set_active_thread(old_thread);
- Ok(())
- }
-
- /// Decide which action to take next and on which thread.
- #[inline]
- fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
+ /// Run the core interpreter loop. Returns only when an interrupt occurs (an error or program
+ /// termination).
+ fn run_threads(&mut self) -> InterpResult<'tcx, !> {
let this = self.eval_context_mut();
- this.machine.threads.schedule(&this.machine.clock)
+ loop {
+ match this.machine.threads.schedule(&this.machine.clock)? {
+ SchedulingAction::ExecuteStep => {
+ if !this.step()? {
+ // See if this thread can do something else.
+ match this.run_on_stack_empty()? {
+ Poll::Pending => {} // keep going
+ Poll::Ready(()) => this.terminate_active_thread()?,
+ }
+ }
+ }
+ SchedulingAction::ExecuteTimeoutCallback => {
+ this.run_timeout_callback()?;
+ }
+ SchedulingAction::Sleep(duration) => {
+ this.machine.clock.sleep(duration);
+ }
+ }
+ }
}
/// Handles thread termination of the active thread: wakes up threads joining on this one,
/// and deallocated thread-local statics.
///
- /// This is called from `tls.rs` after handling the TLS dtors.
+ /// This is called by the eval loop when a thread's on_stack_empty returns `Ready`.
#[inline]
- fn thread_terminated(&mut self) -> InterpResult<'tcx> {
+ fn terminate_active_thread(&mut self) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
+ let thread = this.active_thread_mut();
+ assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated");
+ thread.state = ThreadState::Terminated;
+
for ptr in this.machine.threads.thread_terminated(this.machine.data_race.as_mut()) {
this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?;
}
assert_eq!(
alt_compare,
o.map(Ordering::reverse),
- "Invalid alt comparison\n l: {:?}\n r: {:?}",
- l,
- r
+ "Invalid alt comparison\n l: {l:?}\n r: {r:?}"
);
//Test operators with faster implementations
assert_eq!(
matches!(compare, Some(Ordering::Less)),
l < r,
- "Invalid (<):\n l: {:?}\n r: {:?}",
- l,
- r
+ "Invalid (<):\n l: {l:?}\n r: {r:?}"
);
assert_eq!(
matches!(compare, Some(Ordering::Less) | Some(Ordering::Equal)),
l <= r,
- "Invalid (<=):\n l: {:?}\n r: {:?}",
- l,
- r
+ "Invalid (<=):\n l: {l:?}\n r: {r:?}"
);
assert_eq!(
matches!(compare, Some(Ordering::Greater)),
l > r,
- "Invalid (>):\n l: {:?}\n r: {:?}",
- l,
- r
+ "Invalid (>):\n l: {l:?}\n r: {r:?}"
);
assert_eq!(
matches!(compare, Some(Ordering::Greater) | Some(Ordering::Equal)),
l >= r,
- "Invalid (>=):\n l: {:?}\n r: {:?}",
- l,
- r
+ "Invalid (>=):\n l: {l:?}\n r: {r:?}"
);
assert_eq!(
matches!(alt_compare, Some(Ordering::Less)),
r < l,
- "Invalid alt (<):\n l: {:?}\n r: {:?}",
- l,
- r
+ "Invalid alt (<):\n l: {l:?}\n r: {r:?}"
);
assert_eq!(
matches!(alt_compare, Some(Ordering::Less) | Some(Ordering::Equal)),
r <= l,
- "Invalid alt (<=):\n l: {:?}\n r: {:?}",
- l,
- r
+ "Invalid alt (<=):\n l: {l:?}\n r: {r:?}"
);
assert_eq!(
matches!(alt_compare, Some(Ordering::Greater)),
r > l,
- "Invalid alt (>):\n l: {:?}\n r: {:?}",
- l,
- r
+ "Invalid alt (>):\n l: {l:?}\n r: {r:?}"
);
assert_eq!(
matches!(alt_compare, Some(Ordering::Greater) | Some(Ordering::Equal)),
r >= l,
- "Invalid alt (>=):\n l: {:?}\n r: {:?}",
- l,
- r
+ "Invalid alt (>=):\n l: {l:?}\n r: {r:?}"
);
}
}
vector_clock::{VClock, VTimestamp, VectorIdx},
};
-pub type AllocExtra = StoreBufferAlloc;
+pub type AllocState = StoreBufferAlloc;
// Each store buffer must be bounded otherwise it will grow indefinitely.
// However, bounding the store buffer means restricting the amount of weak
}
impl VisitTags for StoreBufferAlloc {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Self { store_buffers } = self;
for val in store_buffers
.borrow()
use rustc_span::{source_map::DUMMY_SP, SpanData, Symbol};
use rustc_target::abi::{Align, Size};
-use crate::stacked_borrows::{diagnostics::TagHistory, AccessKind};
+use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory;
use crate::*;
/// Details of premature program termination.
pub enum TerminationInfo {
- Exit(i64),
+ Exit {
+ code: i64,
+ leak_check: bool,
+ },
Abort(String),
UnsupportedInIsolation(String),
StackedBorrowsUb {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use TerminationInfo::*;
match self {
- Exit(code) => write!(f, "the evaluated program completed with exit code {code}"),
+ Exit { code, .. } => write!(f, "the evaluated program completed with exit code {code}"),
Abort(msg) => write!(f, "{msg}"),
UnsupportedInIsolation(msg) => write!(f, "{msg}"),
Int2PtrWithStrictProvenance =>
/// Miri specific diagnostics
pub enum NonHaltingDiagnostic {
- /// (new_tag, new_kind, (alloc_id, base_offset, orig_tag))
+ /// (new_tag, new_perm, (alloc_id, base_offset, orig_tag))
///
- /// new_kind is `None` for base tags.
+ /// new_perm is `None` for base tags.
CreatedPointerTag(NonZeroU64, Option<String>, Option<(AllocId, AllocRange, ProvenanceExtra)>),
- /// This `Item` was popped from the borrow stack, either due to an access with the given tag or
- /// a deallocation when the second argument is `None`.
- PoppedPointerTag(Item, Option<(ProvenanceExtra, AccessKind)>),
+ /// This `Item` was popped from the borrow stack. The string explains the reason.
+ PoppedPointerTag(Item, String),
CreatedCallId(CallId),
CreatedAlloc(AllocId, Size, Align, MemoryKind<MiriMemoryKind>),
FreedAlloc(AllocId),
/// Emit a custom diagnostic without going through the miri-engine machinery.
///
-/// Returns `Some` if this was regular program termination with a given exit code, `None` otherwise.
+/// Returns `Some` if this was regular program termination with a given exit code and a `bool` indicating whether a leak check should happen; `None` otherwise.
pub fn report_error<'tcx, 'mir>(
ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
e: InterpErrorInfo<'tcx>,
-) -> Option<i64> {
+) -> Option<(i64, bool)> {
use InterpError::*;
let mut msg = vec![];
let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
use TerminationInfo::*;
let title = match info {
- Exit(code) => return Some(*code),
+ Exit { code, leak_check } => return Some((*code, *leak_check)),
Abort(_) => Some("abnormal termination"),
UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance =>
Some("unsupported operation"),
if is_local && idx > 0 {
err.span_note(frame_info.span, &frame_info.to_string());
} else {
- err.note(&frame_info.to_string());
+ let sm = sess.source_map();
+ let span = sm.span_to_embeddable_string(frame_info.span);
+ err.note(format!("{frame_info} at {span}"));
}
}
let msg = match &e {
CreatedPointerTag(tag, None, _) => format!("created base tag {tag:?}"),
- CreatedPointerTag(tag, Some(kind), None) => format!("created {tag:?} for {kind}"),
- CreatedPointerTag(tag, Some(kind), Some((alloc_id, range, orig_tag))) =>
+ CreatedPointerTag(tag, Some(perm), None) => format!("created {tag:?} with {perm} derived from unknown tag"),
+ CreatedPointerTag(tag, Some(perm), Some((alloc_id, range, orig_tag))) =>
format!(
- "created tag {tag:?} for {kind} at {alloc_id:?}{range:?} derived from {orig_tag:?}"
+ "created tag {tag:?} with {perm} at {alloc_id:?}{range:?} derived from {orig_tag:?}"
),
- PoppedPointerTag(item, tag) =>
- match tag {
- None => format!("popped tracked tag for item {item:?} due to deallocation",),
- Some((tag, access)) => {
- format!(
- "popped tracked tag for item {item:?} due to {access:?} access for {tag:?}",
- )
- }
- },
+ PoppedPointerTag(item, cause) => format!("popped tracked tag for item {item:?}{cause}"),
CreatedCallId(id) => format!("function call with id {id}"),
CreatedAlloc(AllocId(id), size, align, kind) =>
format!(
use std::iter;
use std::panic::{self, AssertUnwindSafe};
use std::path::PathBuf;
+use std::task::Poll;
use std::thread;
use log::info;
+use crate::borrow_tracker::RetagFields;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::DefId;
use rustc_session::config::EntryFnType;
+use crate::shims::tls;
use crate::*;
+/// When the main thread would exit, we will yield to any other thread that is ready to execute.
+/// But we must only do that a finite number of times, or a background thread running `loop {}`
+/// will hang the program.
+const MAIN_THREAD_YIELDS_AT_SHUTDOWN: u32 = 256;
+
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AlignmentCheck {
/// Do not check alignment.
/// Determine if validity checking is enabled.
pub validate: bool,
/// Determines if Stacked Borrows is enabled.
- pub stacked_borrows: bool,
+ pub borrow_tracker: Option<BorrowTrackerMethod>,
/// Controls alignment checking.
pub check_alignment: AlignmentCheck,
/// Controls function [ABI](Abi) checking.
/// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
pub seed: Option<u64>,
/// The stacked borrows pointer ids to report about
- pub tracked_pointer_tags: FxHashSet<SbTag>,
+ pub tracked_pointer_tags: FxHashSet<BorTag>,
/// The stacked borrows call IDs to report about
pub tracked_call_ids: FxHashSet<CallId>,
/// The allocation ids to report about.
/// The location of a shared object file to load when calling external functions
/// FIXME! consider allowing users to specify paths to multiple SO files, or to a directory
pub external_so_file: Option<PathBuf>,
- /// Run a garbage collector for SbTags every N basic blocks.
+ /// Run a garbage collector for BorTags every N basic blocks.
pub gc_interval: u32,
/// The number of CPUs to be reported by miri.
pub num_cpus: u32,
MiriConfig {
env: vec![],
validate: true,
- stacked_borrows: true,
+ borrow_tracker: Some(BorrowTrackerMethod::StackedBorrows),
check_alignment: AlignmentCheck::Int,
check_abi: true,
isolated_op: IsolatedOp::Reject(RejectOpWith::Abort),
}
}
-/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing
-/// the location where the return value of the `start` function will be
-/// written to.
+/// The state of the main thread. Implementation detail of `on_main_stack_empty`.
+#[derive(Default, Debug)]
+enum MainThreadState {
+ #[default]
+ Running,
+ TlsDtors(tls::TlsDtorsState),
+ Yield {
+ remaining: u32,
+ },
+ Done,
+}
+
+impl MainThreadState {
+ fn on_main_stack_empty<'tcx>(
+ &mut self,
+ this: &mut MiriInterpCx<'_, 'tcx>,
+ ) -> InterpResult<'tcx, Poll<()>> {
+ use MainThreadState::*;
+ match self {
+ Running => {
+ *self = TlsDtors(Default::default());
+ }
+ TlsDtors(state) =>
+ match state.on_stack_empty(this)? {
+ Poll::Pending => {} // just keep going
+ Poll::Ready(()) => {
+ // Give background threads a chance to finish by yielding the main thread a
+ // couple of times -- but only if we would also preempt threads randomly.
+ if this.machine.preemption_rate > 0.0 {
+ // There is a non-zero chance they will yield back to us often enough to
+ // make Miri terminate eventually.
+ *self = Yield { remaining: MAIN_THREAD_YIELDS_AT_SHUTDOWN };
+ } else {
+ // The other threads did not get preempted, so no need to yield back to
+ // them.
+ *self = Done;
+ }
+ }
+ },
+ Yield { remaining } =>
+ match remaining.checked_sub(1) {
+ None => *self = Done,
+ Some(new_remaining) => {
+ *remaining = new_remaining;
+ this.yield_active_thread();
+ }
+ },
+ Done => {
+ // Figure out exit code.
+ let ret_place = MPlaceTy::from_aligned_ptr(
+ this.machine.main_fn_ret_place.unwrap().ptr,
+ this.machine.layouts.isize,
+ );
+ let exit_code = this.read_scalar(&ret_place.into())?.to_machine_isize(this)?;
+ // Need to call this ourselves since we are not going to return to the scheduler
+ // loop, and we want the main thread TLS to not show up as memory leaks.
+ this.terminate_active_thread()?;
+ // Stop interpreter loop.
+ throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true });
+ }
+ }
+ Ok(Poll::Pending)
+ }
+}
+
+/// Returns a freshly created `InterpCx`.
/// Public because this is also used by `priroda`.
pub fn create_ecx<'mir, 'tcx: 'mir>(
tcx: TyCtxt<'tcx>,
entry_id: DefId,
entry_type: EntryFnType,
config: &MiriConfig,
-) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, MPlaceTy<'tcx, Provenance>)>
-{
+) -> InterpResult<'tcx, InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>> {
let param_env = ty::ParamEnv::reveal_all();
let layout_cx = LayoutCx { tcx, param_env };
let mut ecx = InterpCx::new(
);
// Some parts of initialization require a full `InterpCx`.
- MiriMachine::late_init(&mut ecx, config)?;
+ MiriMachine::late_init(&mut ecx, config, {
+ let mut state = MainThreadState::default();
+ // Cannot capture anything GC-relevant here.
+ Box::new(move |m| state.on_main_stack_empty(m))
+ })?;
// Make sure we have MIR. We check MIR for some stable monomorphic function in libcore.
let sentinel = ecx.try_resolve_path(&["core", "ascii", "escape_default"], Namespace::ValueNS);
// Return place (in static memory so that it does not count as leak).
let ret_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
+ ecx.machine.main_fn_ret_place = Some(*ret_place);
// Call start function.
match entry_type {
}
}
- Ok((ecx, ret_place))
+ Ok(ecx)
}
/// Evaluates the entry function specified by `entry_id`.
// Copy setting before we move `config`.
let ignore_leaks = config.ignore_leaks;
- let (mut ecx, ret_place) = match create_ecx(tcx, entry_id, entry_type, &config) {
+ let mut ecx = match create_ecx(tcx, entry_id, entry_type, &config) {
Ok(v) => v,
Err(err) => {
err.print_backtrace();
};
// Perform the main execution.
- let res: thread::Result<InterpResult<'_, i64>> = panic::catch_unwind(AssertUnwindSafe(|| {
- // Main loop.
- loop {
- match ecx.schedule()? {
- SchedulingAction::ExecuteStep => {
- assert!(ecx.step()?, "a terminated thread was scheduled for execution");
- }
- SchedulingAction::ExecuteTimeoutCallback => {
- ecx.run_timeout_callback()?;
- }
- SchedulingAction::ExecuteDtors => {
- // This will either enable the thread again (so we go back
- // to `ExecuteStep`), or determine that this thread is done
- // for good.
- ecx.schedule_next_tls_dtor_for_active_thread()?;
- }
- SchedulingAction::Stop => {
- break;
- }
- }
- }
- let return_code = ecx.read_scalar(&ret_place.into())?.to_machine_isize(&ecx)?;
- Ok(return_code)
- }));
+ let res: thread::Result<InterpResult<'_, !>> =
+ panic::catch_unwind(AssertUnwindSafe(|| ecx.run_threads()));
let res = res.unwrap_or_else(|panic_payload| {
ecx.handle_ice();
panic::resume_unwind(panic_payload)
});
+ let res = match res {
+ Err(res) => res,
+ // `Ok` can never happen
+ Ok(never) => match never {},
+ };
// Machine cleanup. Only do this if all threads have terminated; threads that are still running
// might cause Stacked Borrows errors (https://github.com/rust-lang/miri/issues/2396).
}
// Process the result.
- match res {
- Ok(return_code) => {
- if !ignore_leaks {
- // Check for thread leaks.
- if !ecx.have_all_terminated() {
- tcx.sess.err(
- "the main thread terminated without waiting for all remaining threads",
- );
- tcx.sess.note_without_error("pass `-Zmiri-ignore-leaks` to disable this check");
- return None;
- }
- // Check for memory leaks.
- info!("Additonal static roots: {:?}", ecx.machine.static_roots);
- let leaks = ecx.leak_report(&ecx.machine.static_roots);
- if leaks != 0 {
- tcx.sess.err("the evaluated program leaked memory");
- tcx.sess.note_without_error("pass `-Zmiri-ignore-leaks` to disable this check");
- // Ignore the provided return code - let the reported error
- // determine the return code.
- return None;
- }
- }
- Some(return_code)
+ let (return_code, leak_check) = report_error(&ecx, res)?;
+ if leak_check && !ignore_leaks {
+ // Check for thread leaks.
+ if !ecx.have_all_terminated() {
+ tcx.sess.err("the main thread terminated without waiting for all remaining threads");
+ tcx.sess.note_without_error("pass `-Zmiri-ignore-leaks` to disable this check");
+ return None;
+ }
+ // Check for memory leaks.
+ info!("Additonal static roots: {:?}", ecx.machine.static_roots);
+ let leaks = ecx.leak_report(&ecx.machine.static_roots);
+ if leaks != 0 {
+ tcx.sess.err("the evaluated program leaked memory");
+ tcx.sess.note_without_error("pass `-Zmiri-ignore-leaks` to disable this check");
+ // Ignore the provided return code - let the reported error
+ // determine the return code.
+ return None;
}
- Err(e) => report_error(&ecx, e),
}
+ Some(return_code)
}
/// Turns an array of arguments into a Windows command line string.
assert_eq!(
self.eval_context_ref().tcx.sess.target.os,
target_os,
- "`{}` is only available on the `{}` target OS",
- name,
- target_os,
+ "`{name}` is only available on the `{target_os}` target OS",
)
}
fn assert_target_os_is_unix(&self, name: &str) {
assert!(
target_os_is_unix(self.eval_context_ref().tcx.sess.target.os.as_ref()),
- "`{}` is only available for supported UNIX family targets",
- name,
+ "`{name}` is only available for supported UNIX family targets",
);
}
self.stack()[frame_idx].current_span()
}
- fn stack(&self) -> &[Frame<'mir, 'tcx, Provenance, machine::FrameData<'tcx>>] {
+ fn stack(&self) -> &[Frame<'mir, 'tcx, Provenance, machine::FrameExtra<'tcx>>] {
self.threads.active_thread_stack()
}
pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
- "{} not available when isolation is enabled",
- name,
+ "{name} not available when isolation is enabled",
)))
}
}
impl VisitTags for GlobalStateInner {
- fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
// Nothing to visit here.
}
}
pub fn expose_ptr(
ecx: &mut MiriInterpCx<'mir, 'tcx>,
alloc_id: AllocId,
- sb: SbTag,
+ tag: BorTag,
) -> InterpResult<'tcx> {
let global_state = ecx.machine.intptrcast.get_mut();
// In strict mode, we don't need this, so we can save some cycles by not tracking it.
if global_state.provenance_mode != ProvenanceMode::Strict {
trace!("Exposing allocation id {alloc_id:?}");
global_state.exposed.insert(alloc_id);
- if ecx.machine.stacked_borrows.is_some() {
- ecx.expose_tag(alloc_id, sb)?;
+ if ecx.machine.borrow_tracker.is_some() {
+ ecx.expose_tag(alloc_id, tag)?;
}
}
Ok(())
extern crate rustc_span;
extern crate rustc_target;
+mod borrow_tracker;
mod clock;
mod concurrency;
mod diagnostics;
mod operator;
mod range_map;
mod shims;
-mod stacked_borrows;
mod tag_gc;
// Establish a "crate-wide prelude": we often import `crate::*`.
pub use crate::shims::os_str::EvalContextExt as _;
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
pub use crate::shims::time::EvalContextExt as _;
-pub use crate::shims::tls::{EvalContextExt as _, TlsData};
+pub use crate::shims::tls::TlsData;
pub use crate::shims::EvalContextExt as _;
+pub use crate::borrow_tracker::stacked_borrows::{
+ EvalContextExt as _, Item, Permission, Stack, Stacks,
+};
+pub use crate::borrow_tracker::{
+ BorTag, BorrowTrackerMethod, CallId, EvalContextExt as _, RetagFields,
+};
pub use crate::clock::{Clock, Instant};
pub use crate::concurrency::{
data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
init_once::{EvalContextExt as _, InitOnceId},
sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SyncId},
- thread::{EvalContextExt as _, SchedulingAction, ThreadId, ThreadManager, ThreadState, Time},
+ thread::{EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, Time},
};
pub use crate::diagnostics::{
report_error, EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo,
pub use crate::helpers::EvalContextExt as _;
pub use crate::intptrcast::ProvenanceMode;
pub use crate::machine::{
- AllocExtra, FrameData, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind,
+ AllocExtra, FrameExtra, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind,
PrimitiveLayouts, Provenance, ProvenanceExtra, PAGE_SIZE, STACK_ADDR, STACK_SIZE,
};
pub use crate::mono_hash_map::MonoHashMap;
pub use crate::operator::EvalContextExt as _;
pub use crate::range_map::RangeMap;
-pub use crate::stacked_borrows::{
- CallId, EvalContextExt as _, Item, Permission, RetagFields, SbTag, Stack, Stacks,
-};
pub use crate::tag_gc::{EvalContextExt as _, VisitTags};
/// Insert rustc arguments at the beginning of the argument list that Miri wants to be
pub const STACK_SIZE: u64 = 16 * PAGE_SIZE; // whatever
/// Extra data stored with each stack frame
-pub struct FrameData<'tcx> {
+pub struct FrameExtra<'tcx> {
/// Extra data for Stacked Borrows.
- pub stacked_borrows: Option<stacked_borrows::FrameExtra>,
+ pub borrow_tracker: Option<borrow_tracker::FrameState>,
/// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn`
/// called by `try`). When this frame is popped during unwinding a panic,
pub is_user_relevant: bool,
}
-impl<'tcx> std::fmt::Debug for FrameData<'tcx> {
+impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Omitting `timing`, it does not support `Debug`.
- let FrameData { stacked_borrows, catch_unwind, timing: _, is_user_relevant: _ } = self;
+ let FrameExtra { borrow_tracker, catch_unwind, timing: _, is_user_relevant: _ } = self;
f.debug_struct("FrameData")
- .field("stacked_borrows", stacked_borrows)
+ .field("borrow_tracker", borrow_tracker)
.field("catch_unwind", catch_unwind)
.finish()
}
}
-impl VisitTags for FrameData<'_> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
- let FrameData { catch_unwind, stacked_borrows, timing: _, is_user_relevant: _ } = self;
+impl VisitTags for FrameExtra<'_> {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
+ let FrameExtra { catch_unwind, borrow_tracker, timing: _, is_user_relevant: _ } = self;
catch_unwind.visit_tags(visit);
- stacked_borrows.visit_tags(visit);
+ borrow_tracker.visit_tags(visit);
}
}
Concrete {
alloc_id: AllocId,
/// Stacked Borrows tag.
- sb: SbTag,
+ tag: BorTag,
},
Wildcard,
}
/// The "extra" information a pointer has over a regular AllocId.
#[derive(Copy, Clone, PartialEq)]
pub enum ProvenanceExtra {
- Concrete(SbTag),
+ Concrete(BorTag),
Wildcard,
}
impl fmt::Debug for Provenance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- Provenance::Concrete { alloc_id, sb } => {
+ Provenance::Concrete { alloc_id, tag } => {
// Forward `alternate` flag to `alloc_id` printing.
if f.alternate() {
write!(f, "[{alloc_id:#?}]")?;
write!(f, "[{alloc_id:?}]")?;
}
// Print Stacked Borrows tag.
- write!(f, "{sb:?}")?;
+ write!(f, "{tag:?}")?;
}
Provenance::Wildcard => {
write!(f, "[wildcard]")?;
match (left, right) {
// If both are the *same* concrete tag, that is the result.
(
- Some(Provenance::Concrete { alloc_id: left_alloc, sb: left_sb }),
- Some(Provenance::Concrete { alloc_id: right_alloc, sb: right_sb }),
- ) if left_alloc == right_alloc && left_sb == right_sb => left,
+ Some(Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }),
+ Some(Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }),
+ ) if left_alloc == right_alloc && left_tag == right_tag => left,
// If one side is a wildcard, the best possible outcome is that it is equal to the other
// one, and we use that.
(Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o,
}
impl ProvenanceExtra {
- pub fn and_then<T>(self, f: impl FnOnce(SbTag) -> Option<T>) -> Option<T> {
+ pub fn and_then<T>(self, f: impl FnOnce(BorTag) -> Option<T>) -> Option<T> {
match self {
ProvenanceExtra::Concrete(pid) => f(pid),
ProvenanceExtra::Wildcard => None,
/// Extra per-allocation data
#[derive(Debug, Clone)]
pub struct AllocExtra {
- /// Stacked Borrows state is only added if it is enabled.
- pub stacked_borrows: Option<stacked_borrows::AllocExtra>,
+ /// Global state of the borrow tracker, if enabled.
+ pub borrow_tracker: Option<borrow_tracker::AllocState>,
/// Data race detection via the use of a vector-clock,
/// this is only added if it is enabled.
- pub data_race: Option<data_race::AllocExtra>,
+ pub data_race: Option<data_race::AllocState>,
/// Weak memory emulation via the use of store buffers,
/// this is only added if it is enabled.
- pub weak_memory: Option<weak_memory::AllocExtra>,
+ pub weak_memory: Option<weak_memory::AllocState>,
}
impl VisitTags for AllocExtra {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
- let AllocExtra { stacked_borrows, data_race, weak_memory } = self;
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
+ let AllocExtra { borrow_tracker, data_race, weak_memory } = self;
- stacked_borrows.visit_tags(visit);
+ borrow_tracker.visit_tags(visit);
data_race.visit_tags(visit);
weak_memory.visit_tags(visit);
}
// We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access.
pub tcx: TyCtxt<'tcx>,
- /// Stacked Borrows global data.
- pub stacked_borrows: Option<stacked_borrows::GlobalState>,
+ /// Global data for borrow tracking.
+ pub borrow_tracker: Option<borrow_tracker::GlobalState>,
/// Data race detector global data.
pub data_race: Option<data_race::GlobalState>,
/// Miri does not expose env vars from the host to the emulated program.
pub(crate) env_vars: EnvVars<'tcx>,
+ /// Return place of the main function.
+ pub(crate) main_fn_ret_place: Option<MemPlace<Provenance>>,
+
/// Program arguments (`Option` because we can only initialize them after creating the ecx).
/// These are *pointers* to argc/argv because macOS.
/// We also need the full command line as one string because of Windows.
#[cfg(not(target_os = "linux"))]
pub external_so_lib: Option<!>,
- /// Run a garbage collector for SbTags every N basic blocks.
+ /// Run a garbage collector for BorTags every N basic blocks.
pub(crate) gc_interval: u32,
- /// The number of blocks that passed since the last SbTag GC pass.
+ /// The number of blocks that passed since the last BorTag GC pass.
pub(crate) since_gc: u32,
/// The number of CPUs to be reported by miri.
pub(crate) num_cpus: u32,
measureme::Profiler::new(out).expect("Couldn't create `measureme` profiler")
});
let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
- let stacked_borrows = config.stacked_borrows.then(|| {
- RefCell::new(stacked_borrows::GlobalStateInner::new(
- config.tracked_pointer_tags.clone(),
- config.tracked_call_ids.clone(),
- config.retag_fields,
- ))
- });
+ let borrow_tracker = config.borrow_tracker.map(|bt| bt.instanciate_global_state(config));
let data_race = config.data_race_detector.then(|| data_race::GlobalState::new(config));
MiriMachine {
tcx: layout_cx.tcx,
- stacked_borrows,
+ borrow_tracker,
data_race,
intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config)),
// `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
env_vars: EnvVars::default(),
+ main_fn_ret_place: None,
argc: None,
argv: None,
cmd_line: None,
pub(crate) fn late_init(
this: &mut MiriInterpCx<'mir, 'tcx>,
config: &MiriConfig,
+ on_main_stack_empty: StackEmptyCallback<'mir, 'tcx>,
) -> InterpResult<'tcx> {
EnvVars::init(this, config)?;
MiriMachine::init_extern_statics(this)?;
- ThreadManager::init(this);
+ ThreadManager::init(this, on_main_stack_empty);
Ok(())
}
}
impl VisitTags for MiriMachine<'_, '_> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
#[rustfmt::skip]
let MiriMachine {
threads,
tls,
env_vars,
+ main_fn_ret_place,
argc,
argv,
cmd_line,
extern_statics,
dir_handler,
- stacked_borrows,
+ borrow_tracker,
data_race,
intptrcast,
file_handler,
dir_handler.visit_tags(visit);
file_handler.visit_tags(visit);
data_race.visit_tags(visit);
- stacked_borrows.visit_tags(visit);
+ borrow_tracker.visit_tags(visit);
intptrcast.visit_tags(visit);
+ main_fn_ret_place.visit_tags(visit);
argc.visit_tags(visit);
argv.visit_tags(visit);
cmd_line.visit_tags(visit);
type MemoryKind = MiriMemoryKind;
type ExtraFnVal = Dlsym;
- type FrameExtra = FrameData<'tcx>;
+ type FrameExtra = FrameExtra<'tcx>;
type AllocExtra = AllocExtra;
type Provenance = Provenance;
}
let alloc = alloc.into_owned();
- let stacks = ecx.machine.stacked_borrows.as_ref().map(|stacked_borrows| {
- Stacks::new_allocation(id, alloc.size(), stacked_borrows, kind, &ecx.machine)
- });
+ let borrow_tracker = ecx
+ .machine
+ .borrow_tracker
+ .as_ref()
+ .map(|bt| bt.borrow_mut().new_allocation(id, alloc.size(), kind, &ecx.machine));
+
let race_alloc = ecx.machine.data_race.as_ref().map(|data_race| {
- data_race::AllocExtra::new_allocation(
+ data_race::AllocState::new_allocation(
data_race,
&ecx.machine.threads,
alloc.size(),
kind,
)
});
- let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocExtra::new_allocation);
+ let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocState::new_allocation);
let alloc: Allocation<Provenance, Self::AllocExtra> = alloc.adjust_from_tcx(
&ecx.tcx,
- AllocExtra {
- stacked_borrows: stacks.map(RefCell::new),
- data_race: race_alloc,
- weak_memory: buffer_alloc,
- },
+ AllocExtra { borrow_tracker, data_race: race_alloc, weak_memory: buffer_alloc },
|ptr| ecx.global_base_pointer(ptr),
)?;
Ok(Cow::Owned(alloc))
}
}
let absolute_addr = intptrcast::GlobalStateInner::rel_ptr_to_addr(ecx, ptr);
- let sb_tag = if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
- stacked_borrows.borrow_mut().base_ptr_tag(ptr.provenance, &ecx.machine)
+ let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
+ borrow_tracker.borrow_mut().base_ptr_tag(ptr.provenance, &ecx.machine)
} else {
// Value does not matter, SB is disabled
- SbTag::default()
+ BorTag::default()
};
Pointer::new(
- Provenance::Concrete { alloc_id: ptr.provenance, sb: sb_tag },
+ Provenance::Concrete { alloc_id: ptr.provenance, tag },
Size::from_bytes(absolute_addr),
)
}
ptr: Pointer<Self::Provenance>,
) -> InterpResult<'tcx> {
match ptr.provenance {
- Provenance::Concrete { alloc_id, sb } =>
- intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb),
+ Provenance::Concrete { alloc_id, tag } => {
+ intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, tag)
+ }
Provenance::Wildcard => {
// No need to do anything for wildcard pointers as
// their provenances have already been previously exposed.
let rel = intptrcast::GlobalStateInner::abs_ptr_to_rel(ecx, ptr);
rel.map(|(alloc_id, size)| {
- let sb = match ptr.provenance {
- Provenance::Concrete { sb, .. } => ProvenanceExtra::Concrete(sb),
+ let tag = match ptr.provenance {
+ Provenance::Concrete { tag, .. } => ProvenanceExtra::Concrete(tag),
Provenance::Wildcard => ProvenanceExtra::Wildcard,
};
- (alloc_id, size, sb)
+ (alloc_id, size, tag)
})
}
if let Some(data_race) = &alloc_extra.data_race {
data_race.read(alloc_id, range, machine)?;
}
- if let Some(stacked_borrows) = &alloc_extra.stacked_borrows {
- stacked_borrows
- .borrow_mut()
- .before_memory_read(alloc_id, prov_extra, range, machine)?;
+ if let Some(borrow_tracker) = &alloc_extra.borrow_tracker {
+ borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?;
}
if let Some(weak_memory) = &alloc_extra.weak_memory {
weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
if let Some(data_race) = &mut alloc_extra.data_race {
data_race.write(alloc_id, range, machine)?;
}
- if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
- stacked_borrows.get_mut().before_memory_write(alloc_id, prov_extra, range, machine)?;
+ if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
+ borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?;
}
if let Some(weak_memory) = &alloc_extra.weak_memory {
weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
if let Some(data_race) = &mut alloc_extra.data_race {
data_race.deallocate(alloc_id, range, machine)?;
}
- if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
- stacked_borrows.get_mut().before_memory_deallocation(
- alloc_id,
- prove_extra,
- range,
- machine,
- )
+ if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
+ borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, range, machine)?;
+ }
+ Ok(())
+ }
+
+ #[inline(always)]
+ fn retag_ptr_value(
+ ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ kind: mir::RetagKind,
+ val: &ImmTy<'tcx, Provenance>,
+ ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
+ if ecx.machine.borrow_tracker.is_some() {
+ ecx.retag_ptr_value(kind, val)
} else {
- Ok(())
+ Ok(val.clone())
}
}
#[inline(always)]
- fn retag(
+ fn retag_place_contents(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
kind: mir::RetagKind,
place: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx> {
- if ecx.machine.stacked_borrows.is_some() { ecx.retag(kind, place) } else { Ok(()) }
+ if ecx.machine.borrow_tracker.is_some() {
+ ecx.retag_place_contents(kind, place)?;
+ }
+ Ok(())
}
#[inline(always)]
fn init_frame_extra(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
frame: Frame<'mir, 'tcx, Provenance>,
- ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>> {
+ ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> {
// Start recording our event before doing anything else
let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
let fn_name = frame.instance.to_string();
None
};
- let stacked_borrows = ecx.machine.stacked_borrows.as_ref();
+ let borrow_tracker = ecx.machine.borrow_tracker.as_ref();
- let extra = FrameData {
- stacked_borrows: stacked_borrows.map(|sb| sb.borrow_mut().new_frame(&ecx.machine)),
+ let extra = FrameExtra {
+ borrow_tracker: borrow_tracker.map(|bt| bt.borrow_mut().new_frame(&ecx.machine)),
catch_unwind: None,
timing,
is_user_relevant: ecx.machine.is_user_relevant(&frame),
}
}
- // Search for SbTags to find all live pointers, then remove all other tags from borrow
+ // Search for BorTags to find all live pointers, then remove all other tags from borrow
// stacks.
// When debug assertions are enabled, run the GC as often as possible so that any cases
// where it mistakenly removes an important tag become visible.
let stack_len = ecx.active_thread_stack().len();
ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
}
-
- if ecx.machine.stacked_borrows.is_some() { ecx.retag_return_place() } else { Ok(()) }
+ if ecx.machine.borrow_tracker.is_some() {
+ ecx.retag_return_place()?;
+ }
+ Ok(())
}
#[inline(always)]
fn after_stack_pop(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
- mut frame: Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>,
+ mut frame: Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>,
unwinding: bool,
) -> InterpResult<'tcx, StackPopJump> {
if frame.extra.is_user_relevant {
ecx.active_thread_mut().recompute_top_user_relevant_frame();
}
let timing = frame.extra.timing.take();
- if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
- stacked_borrows.borrow_mut().end_call(&frame.extra);
+ if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
+ borrow_tracker.borrow_mut().end_call(&frame.extra);
}
let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
if let Some(profiler) = ecx.machine.profiler.as_ref() {
}
impl VisitTags for EnvVars<'_> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let EnvVars { map, environ } = self;
environ.visit_tags(visit);
let [code] = this.check_shim(abi, exp_abi, link_name, args)?;
// it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway
let code = this.read_scalar(code)?.to_i32()?;
- throw_machine_stop!(TerminationInfo::Exit(code.into()));
+ throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
}
"abort" => {
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
return Ok(Some(body));
}
this.handle_unsupported(format!(
- "can't call (diverging) foreign function: {}",
- link_name
+ "can't call (diverging) foreign function: {link_name}"
))?;
return Ok(None);
}
}
impl VisitTags for CatchUnwindData<'_> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let CatchUnwindData { catch_fn, data, dest, ret: _ } = self;
catch_fn.visit_tags(visit);
data.visit_tags(visit);
fn handle_stack_pop_unwind(
&mut self,
- mut extra: FrameData<'tcx>,
+ mut extra: FrameExtra<'tcx>,
unwinding: bool,
) -> InterpResult<'tcx, StackPopJump> {
let this = self.eval_context_mut();
}
impl VisitTags for UnblockCallback {
- fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {}
+ fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {}
}
impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for UnblockCallback {
//! Implement thread-local storage.
use std::collections::btree_map::Entry as BTreeEntry;
-use std::collections::hash_map::Entry as HashMapEntry;
use std::collections::BTreeMap;
+use std::task::Poll;
use log::trace;
-use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty;
use rustc_target::abi::{HasDataLayout, Size};
use rustc_target::spec::abi::Abi;
dtor: Option<ty::Instance<'tcx>>,
}
-#[derive(Clone, Debug)]
-struct RunningDtorsState {
+#[derive(Default, Debug)]
+struct RunningDtorState {
/// The last TlsKey used to retrieve a TLS destructor. `None` means that we
/// have not tried to retrieve a TLS destructor yet or that we already tried
/// all keys.
- last_dtor_key: Option<TlsKey>,
+ last_key: Option<TlsKey>,
}
#[derive(Debug)]
/// A single per thread destructor of the thread local storage (that's how
/// things work on macOS) with a data argument.
macos_thread_dtors: BTreeMap<ThreadId, (ty::Instance<'tcx>, Scalar<Provenance>)>,
-
- /// State for currently running TLS dtors. If this map contains a key for a
- /// specific thread, it means that we are in the "destruct" phase, during
- /// which some operations are UB.
- dtors_running: FxHashMap<ThreadId, RunningDtorsState>,
}
impl<'tcx> Default for TlsData<'tcx> {
next_key: 1, // start with 1 as we must not use 0 on Windows
keys: Default::default(),
macos_thread_dtors: Default::default(),
- dtors_running: Default::default(),
}
}
}
dtor: ty::Instance<'tcx>,
data: Scalar<Provenance>,
) -> InterpResult<'tcx> {
- if self.dtors_running.contains_key(&thread) {
- // UB, according to libstd docs.
- throw_ub_format!(
- "setting thread's local storage destructor while destructors are already running"
- );
- }
if self.macos_thread_dtors.insert(thread, (dtor, data)).is_some() {
throw_unsup_format!(
"setting more than one thread local storage destructor for the same thread is not supported"
None
}
- /// Set that dtors are running for `thread`. It is guaranteed not to change
- /// the existing values stored in `dtors_running` for this thread. Returns
- /// `true` if dtors for `thread` are already running.
- fn set_dtors_running_for_thread(&mut self, thread: ThreadId) -> bool {
- match self.dtors_running.entry(thread) {
- HashMapEntry::Occupied(_) => true,
- HashMapEntry::Vacant(entry) => {
- // We cannot just do `self.dtors_running.insert` because that
- // would overwrite `last_dtor_key` with `None`.
- entry.insert(RunningDtorsState { last_dtor_key: None });
- false
- }
- }
- }
-
/// Delete all TLS entries for the given thread. This function should be
/// called after all TLS destructors have already finished.
fn delete_all_thread_tls(&mut self, thread_id: ThreadId) {
}
impl VisitTags for TlsData<'_> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
- let TlsData { keys, macos_thread_dtors, next_key: _, dtors_running: _ } = self;
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
+ let TlsData { keys, macos_thread_dtors, next_key: _ } = self;
for scalar in keys.values().flat_map(|v| v.data.values()) {
scalar.visit_tags(visit);
}
}
+#[derive(Debug, Default)]
+pub struct TlsDtorsState(TlsDtorsStatePriv);
+
+#[derive(Debug, Default)]
+enum TlsDtorsStatePriv {
+ #[default]
+ Init,
+ PthreadDtors(RunningDtorState),
+ Done,
+}
+
+impl TlsDtorsState {
+ pub fn on_stack_empty<'tcx>(
+ &mut self,
+ this: &mut MiriInterpCx<'_, 'tcx>,
+ ) -> InterpResult<'tcx, Poll<()>> {
+ use TlsDtorsStatePriv::*;
+ match &mut self.0 {
+ Init => {
+ match this.tcx.sess.target.os.as_ref() {
+ "linux" | "freebsd" | "android" => {
+ // Run the pthread dtors.
+ self.0 = PthreadDtors(Default::default());
+ }
+ "macos" => {
+ // The macOS thread wide destructor runs "before any TLS slots get
+ // freed", so do that first.
+ this.schedule_macos_tls_dtor()?;
+ // When the stack is empty again, go on with the pthread dtors.
+ self.0 = PthreadDtors(Default::default());
+ }
+ "windows" => {
+ // Run the special magic hook.
+ this.schedule_windows_tls_dtors()?;
+ // And move to the final state.
+ self.0 = Done;
+ }
+ "wasi" | "none" => {
+ // No OS, no TLS dtors.
+ // FIXME: should we do something on wasi?
+ self.0 = Done;
+ }
+ os => {
+ throw_unsup_format!(
+ "the TLS machinery does not know how to handle OS `{os}`"
+ );
+ }
+ }
+ }
+ PthreadDtors(state) => {
+ match this.schedule_next_pthread_tls_dtor(state)? {
+ Poll::Pending => {} // just keep going
+ Poll::Ready(()) => self.0 = Done,
+ }
+ }
+ Done => {
+ this.machine.tls.delete_all_thread_tls(this.get_active_thread());
+ return Ok(Poll::Ready(()));
+ }
+ }
+
+ Ok(Poll::Pending)
+ }
+}
+
impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
/// Schedule TLS destructors for Windows.
/// On windows, TLS destructors are managed by std.
fn schedule_windows_tls_dtors(&mut self) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
- let active_thread = this.get_active_thread();
// Windows has a special magic linker section that is run on certain events.
// Instead of searching for that section and supporting arbitrary hooks in there
None,
StackPopCleanup::Root { cleanup: true },
)?;
-
- this.enable_thread(active_thread);
Ok(())
}
/// Schedule the MacOS thread destructor of the thread local storage to be
- /// executed. Returns `true` if scheduled.
- ///
- /// Note: It is safe to call this function also on other Unixes.
- fn schedule_macos_tls_dtor(&mut self) -> InterpResult<'tcx, bool> {
+ /// executed.
+ fn schedule_macos_tls_dtor(&mut self) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let thread_id = this.get_active_thread();
if let Some((instance, data)) = this.machine.tls.macos_thread_dtors.remove(&thread_id) {
None,
StackPopCleanup::Root { cleanup: true },
)?;
-
- // Enable the thread so that it steps through the destructor which
- // we just scheduled. Since we deleted the destructor, it is
- // guaranteed that we will schedule it again. The `dtors_running`
- // flag will prevent the code from adding the destructor again.
- this.enable_thread(thread_id);
- Ok(true)
- } else {
- Ok(false)
}
+ Ok(())
}
/// Schedule a pthread TLS destructor. Returns `true` if found
/// a destructor to schedule, and `false` otherwise.
- fn schedule_next_pthread_tls_dtor(&mut self) -> InterpResult<'tcx, bool> {
+ fn schedule_next_pthread_tls_dtor(
+ &mut self,
+ state: &mut RunningDtorState,
+ ) -> InterpResult<'tcx, Poll<()>> {
let this = self.eval_context_mut();
let active_thread = this.get_active_thread();
- assert!(this.has_terminated(active_thread), "running TLS dtors for non-terminated thread");
// Fetch next dtor after `key`.
- let last_key = this.machine.tls.dtors_running[&active_thread].last_dtor_key;
- let dtor = match this.machine.tls.fetch_tls_dtor(last_key, active_thread) {
+ let dtor = match this.machine.tls.fetch_tls_dtor(state.last_key, active_thread) {
dtor @ Some(_) => dtor,
// We ran each dtor once, start over from the beginning.
None => this.machine.tls.fetch_tls_dtor(None, active_thread),
};
if let Some((instance, ptr, key)) = dtor {
- this.machine.tls.dtors_running.get_mut(&active_thread).unwrap().last_dtor_key =
- Some(key);
+ state.last_key = Some(key);
trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread);
assert!(
!ptr.to_machine_usize(this).unwrap() != 0,
StackPopCleanup::Root { cleanup: true },
)?;
- this.enable_thread(active_thread);
- return Ok(true);
- }
- this.machine.tls.dtors_running.get_mut(&active_thread).unwrap().last_dtor_key = None;
-
- Ok(false)
- }
-}
-
-impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
-pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
- /// Schedule an active thread's TLS destructor to run on the active thread.
- /// Note that this function does not run the destructors itself, it just
- /// schedules them one by one each time it is called and reenables the
- /// thread so that it can be executed normally by the main execution loop.
- ///
- /// Note: we consistently run TLS destructors for all threads, including the
- /// main thread. However, it is not clear that we should run the TLS
- /// destructors for the main thread. See issue:
- /// <https://github.com/rust-lang/rust/issues/28129>.
- fn schedule_next_tls_dtor_for_active_thread(&mut self) -> InterpResult<'tcx> {
- let this = self.eval_context_mut();
- let active_thread = this.get_active_thread();
- trace!("schedule_next_tls_dtor_for_active_thread on thread {:?}", active_thread);
-
- if !this.machine.tls.set_dtors_running_for_thread(active_thread) {
- // This is the first time we got asked to schedule a destructor. The
- // Windows schedule destructor function must be called exactly once,
- // this is why it is in this block.
- if this.tcx.sess.target.os == "windows" {
- // On Windows, we signal that the thread quit by starting the
- // relevant function, reenabling the thread, and going back to
- // the scheduler.
- this.schedule_windows_tls_dtors()?;
- return Ok(());
- }
+ return Ok(Poll::Pending);
}
- // The remaining dtors make some progress each time around the scheduler loop,
- // until they return `false` to indicate that they are done.
-
- // The macOS thread wide destructor runs "before any TLS slots get
- // freed", so do that first.
- if this.schedule_macos_tls_dtor()? {
- // We have scheduled a MacOS dtor to run on the thread. Execute it
- // to completion and come back here. Scheduling a destructor
- // destroys it, so we will not enter this branch again.
- return Ok(());
- }
- if this.schedule_next_pthread_tls_dtor()? {
- // We have scheduled a pthread destructor and removed it from the
- // destructors list. Run it to completion and come back here.
- return Ok(());
- }
-
- // All dtors done!
- this.machine.tls.delete_all_thread_tls(active_thread);
- this.thread_terminated()?;
- Ok(())
+ Ok(Poll::Ready(()))
}
}
}
impl VisitTags for FileHandler {
- fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, _visit: &mut dyn FnMut(BorTag)) {
// All our FileDescriptor do not have any tags.
}
}
}
impl VisitTags for DirHandler {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let DirHandler { streams, next_id: _ } = self;
for dir in streams.values() {
}
impl<'tcx> VisitTags for Callback<'tcx> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Callback { thread: _, addr_usize: _, dest } = self;
dest.visit_tags(visit);
}
}
impl<'tcx> VisitTags for Callback<'tcx> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Callback { active_thread: _, mutex_id: _, id: _, dest } = self;
dest.visit_tags(visit);
}
let func_arg = this.read_immediate(arg)?;
- this.start_thread(
+ this.start_regular_thread(
Some(thread_info_place),
start_routine,
Abi::C { unwind: false },
}
impl<'tcx> VisitTags for Callback<'tcx> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Callback { init_once_id: _, pending_place } = self;
pending_place.visit_tags(visit);
}
}
impl<'tcx> VisitTags for Callback<'tcx> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Callback { thread: _, addr: _, dest } = self;
dest.visit_tags(visit);
}
}
impl<'tcx> VisitTags for Callback<'tcx> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let Callback { thread: _, condvar_id: _, lock_id: _, mode: _, dest } = self;
dest.visit_tags(visit);
}
throw_unsup_format!("non-null `lpThreadAttributes` in `CreateThread`")
}
- this.start_thread(
+ this.start_regular_thread(
thread,
start_routine,
Abi::System { unwind: false },
+++ /dev/null
-use smallvec::SmallVec;
-use std::fmt;
-
-use rustc_middle::mir::interpret::{alloc_range, AllocId, AllocRange};
-use rustc_span::{Span, SpanData};
-use rustc_target::abi::Size;
-
-use crate::stacked_borrows::{err_sb_ub, AccessKind, GlobalStateInner, Permission, ProtectorKind};
-use crate::*;
-
-use rustc_middle::mir::interpret::InterpError;
-
-#[derive(Clone, Debug)]
-pub struct AllocHistory {
- id: AllocId,
- base: (Item, Span),
- creations: smallvec::SmallVec<[Creation; 1]>,
- invalidations: smallvec::SmallVec<[Invalidation; 1]>,
- protectors: smallvec::SmallVec<[Protection; 1]>,
-}
-
-#[derive(Clone, Debug)]
-struct Creation {
- retag: RetagOp,
- span: Span,
-}
-
-impl Creation {
- fn generate_diagnostic(&self) -> (String, SpanData) {
- let tag = self.retag.new_tag;
- if let Some(perm) = self.retag.permission {
- (
- format!(
- "{tag:?} was created by a {:?} retag at offsets {:?}",
- perm, self.retag.range,
- ),
- self.span.data(),
- )
- } else {
- assert!(self.retag.range.size == Size::ZERO);
- (
- format!(
- "{tag:?} would have been created here, but this is a zero-size retag ({:?}) so the tag in question does not exist anywhere",
- self.retag.range,
- ),
- self.span.data(),
- )
- }
- }
-}
-
-#[derive(Clone, Debug)]
-struct Invalidation {
- tag: SbTag,
- range: AllocRange,
- span: Span,
- cause: InvalidationCause,
-}
-
-#[derive(Clone, Debug)]
-enum InvalidationCause {
- Access(AccessKind),
- Retag(Permission, RetagCause),
-}
-
-impl Invalidation {
- fn generate_diagnostic(&self) -> (String, SpanData) {
- let message = if let InvalidationCause::Retag(_, RetagCause::FnEntry) = self.cause {
- // For a FnEntry retag, our Span points at the caller.
- // See `DiagnosticCx::log_invalidation`.
- format!(
- "{:?} was later invalidated at offsets {:?} by a {} inside this call",
- self.tag, self.range, self.cause
- )
- } else {
- format!(
- "{:?} was later invalidated at offsets {:?} by a {}",
- self.tag, self.range, self.cause
- )
- };
- (message, self.span.data())
- }
-}
-
-impl fmt::Display for InvalidationCause {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- InvalidationCause::Access(kind) => write!(f, "{kind}"),
- InvalidationCause::Retag(perm, kind) =>
- if *kind == RetagCause::FnEntry {
- write!(f, "{perm:?} FnEntry retag")
- } else {
- write!(f, "{perm:?} retag")
- },
- }
- }
-}
-
-#[derive(Clone, Debug)]
-struct Protection {
- tag: SbTag,
- span: Span,
-}
-
-#[derive(Clone)]
-pub struct TagHistory {
- pub created: (String, SpanData),
- pub invalidated: Option<(String, SpanData)>,
- pub protected: Option<(String, SpanData)>,
-}
-
-pub struct DiagnosticCxBuilder<'ecx, 'mir, 'tcx> {
- operation: Operation,
- machine: &'ecx MiriMachine<'mir, 'tcx>,
-}
-
-pub struct DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
- operation: Operation,
- machine: &'ecx MiriMachine<'mir, 'tcx>,
- history: &'history mut AllocHistory,
- offset: Size,
-}
-
-impl<'ecx, 'mir, 'tcx> DiagnosticCxBuilder<'ecx, 'mir, 'tcx> {
- pub fn build<'history>(
- self,
- history: &'history mut AllocHistory,
- offset: Size,
- ) -> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
- DiagnosticCx { operation: self.operation, machine: self.machine, history, offset }
- }
-
- pub fn retag(
- machine: &'ecx MiriMachine<'mir, 'tcx>,
- cause: RetagCause,
- new_tag: SbTag,
- orig_tag: ProvenanceExtra,
- range: AllocRange,
- ) -> Self {
- let operation =
- Operation::Retag(RetagOp { cause, new_tag, orig_tag, range, permission: None });
-
- DiagnosticCxBuilder { machine, operation }
- }
-
- pub fn read(
- machine: &'ecx MiriMachine<'mir, 'tcx>,
- tag: ProvenanceExtra,
- range: AllocRange,
- ) -> Self {
- let operation = Operation::Access(AccessOp { kind: AccessKind::Read, tag, range });
- DiagnosticCxBuilder { machine, operation }
- }
-
- pub fn write(
- machine: &'ecx MiriMachine<'mir, 'tcx>,
- tag: ProvenanceExtra,
- range: AllocRange,
- ) -> Self {
- let operation = Operation::Access(AccessOp { kind: AccessKind::Write, tag, range });
- DiagnosticCxBuilder { machine, operation }
- }
-
- pub fn dealloc(machine: &'ecx MiriMachine<'mir, 'tcx>, tag: ProvenanceExtra) -> Self {
- let operation = Operation::Dealloc(DeallocOp { tag });
- DiagnosticCxBuilder { machine, operation }
- }
-}
-
-impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
- pub fn unbuild(self) -> DiagnosticCxBuilder<'ecx, 'mir, 'tcx> {
- DiagnosticCxBuilder { machine: self.machine, operation: self.operation }
- }
-}
-
-#[derive(Debug, Clone)]
-enum Operation {
- Retag(RetagOp),
- Access(AccessOp),
- Dealloc(DeallocOp),
-}
-
-#[derive(Debug, Clone)]
-struct RetagOp {
- cause: RetagCause,
- new_tag: SbTag,
- orig_tag: ProvenanceExtra,
- range: AllocRange,
- permission: Option<Permission>,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum RetagCause {
- Normal,
- FnReturn,
- FnEntry,
- TwoPhase,
-}
-
-#[derive(Debug, Clone)]
-struct AccessOp {
- kind: AccessKind,
- tag: ProvenanceExtra,
- range: AllocRange,
-}
-
-#[derive(Debug, Clone)]
-struct DeallocOp {
- tag: ProvenanceExtra,
-}
-
-impl AllocHistory {
- pub fn new(id: AllocId, item: Item, machine: &MiriMachine<'_, '_>) -> Self {
- Self {
- id,
- base: (item, machine.current_span()),
- creations: SmallVec::new(),
- invalidations: SmallVec::new(),
- protectors: SmallVec::new(),
- }
- }
-}
-
-impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
- pub fn start_grant(&mut self, perm: Permission) {
- let Operation::Retag(op) = &mut self.operation else {
- unreachable!("start_grant must only be called during a retag, this is: {:?}", self.operation)
- };
- op.permission = Some(perm);
-
- let last_creation = &mut self.history.creations.last_mut().unwrap();
- match last_creation.retag.permission {
- None => {
- last_creation.retag.permission = Some(perm);
- }
- Some(previous) =>
- if previous != perm {
- // 'Split up' the creation event.
- let previous_range = last_creation.retag.range;
- last_creation.retag.range = alloc_range(previous_range.start, self.offset);
- let mut new_event = last_creation.clone();
- new_event.retag.range = alloc_range(self.offset, previous_range.end());
- new_event.retag.permission = Some(perm);
- self.history.creations.push(new_event);
- },
- }
- }
-
- pub fn log_creation(&mut self) {
- let Operation::Retag(op) = &self.operation else {
- unreachable!("log_creation must only be called during a retag")
- };
- self.history
- .creations
- .push(Creation { retag: op.clone(), span: self.machine.current_span() });
- }
-
- pub fn log_invalidation(&mut self, tag: SbTag) {
- let mut span = self.machine.current_span();
- let (range, cause) = match &self.operation {
- Operation::Retag(RetagOp { cause, range, permission, .. }) => {
- if *cause == RetagCause::FnEntry {
- span = self.machine.caller_span();
- }
- (*range, InvalidationCause::Retag(permission.unwrap(), *cause))
- }
- Operation::Access(AccessOp { kind, range, .. }) =>
- (*range, InvalidationCause::Access(*kind)),
- Operation::Dealloc(_) => {
- // This can be reached, but never be relevant later since the entire allocation is
- // gone now.
- return;
- }
- };
- self.history.invalidations.push(Invalidation { tag, range, span, cause });
- }
-
- pub fn log_protector(&mut self) {
- let Operation::Retag(op) = &self.operation else {
- unreachable!("Protectors can only be created during a retag")
- };
- self.history
- .protectors
- .push(Protection { tag: op.new_tag, span: self.machine.current_span() });
- }
-
- pub fn get_logs_relevant_to(
- &self,
- tag: SbTag,
- protector_tag: Option<SbTag>,
- ) -> Option<TagHistory> {
- let Some(created) = self.history
- .creations
- .iter()
- .rev()
- .find_map(|event| {
- // First, look for a Creation event where the tag and the offset matches. This
- // ensrues that we pick the right Creation event when a retag isn't uniform due to
- // Freeze.
- let range = event.retag.range;
- if event.retag.new_tag == tag
- && self.offset >= range.start
- && self.offset < (range.start + range.size)
- {
- Some(event.generate_diagnostic())
- } else {
- None
- }
- })
- .or_else(|| {
- // If we didn't find anything with a matching offset, just return the event where
- // the tag was created. This branch is hit when we use a tag at an offset that
- // doesn't have the tag.
- self.history.creations.iter().rev().find_map(|event| {
- if event.retag.new_tag == tag {
- Some(event.generate_diagnostic())
- } else {
- None
- }
- })
- }).or_else(|| {
- // If we didn't find a retag that created this tag, it might be the base tag of
- // this allocation.
- if self.history.base.0.tag() == tag {
- Some((
- format!("{tag:?} was created here, as the base tag for {:?}", self.history.id),
- self.history.base.1.data()
- ))
- } else {
- None
- }
- }) else {
- // But if we don't have a creation event, this is related to a wildcard, and there
- // is really nothing we can do to help.
- return None;
- };
-
- let invalidated = self.history.invalidations.iter().rev().find_map(|event| {
- if event.tag == tag { Some(event.generate_diagnostic()) } else { None }
- });
-
- let protected = protector_tag
- .and_then(|protector| {
- self.history.protectors.iter().find(|protection| protection.tag == protector)
- })
- .map(|protection| {
- let protected_tag = protection.tag;
- (format!("{protected_tag:?} is this argument"), protection.span.data())
- });
-
- Some(TagHistory { created, invalidated, protected })
- }
-
- /// Report a descriptive error when `new` could not be granted from `derived_from`.
- #[inline(never)] // This is only called on fatal code paths
- pub(super) fn grant_error(&self, stack: &Stack) -> InterpError<'tcx> {
- let Operation::Retag(op) = &self.operation else {
- unreachable!("grant_error should only be called during a retag")
- };
- let perm =
- op.permission.expect("`start_grant` must be called before calling `grant_error`");
- let action = format!(
- "trying to retag from {:?} for {:?} permission at {:?}[{:#x}]",
- op.orig_tag,
- perm,
- self.history.id,
- self.offset.bytes(),
- );
- err_sb_ub(
- format!("{action}{}", error_cause(stack, op.orig_tag)),
- Some(operation_summary(&op.cause.summary(), self.history.id, op.range)),
- op.orig_tag.and_then(|orig_tag| self.get_logs_relevant_to(orig_tag, None)),
- )
- }
-
- /// Report a descriptive error when `access` is not permitted based on `tag`.
- #[inline(never)] // This is only called on fatal code paths
- pub(super) fn access_error(&self, stack: &Stack) -> InterpError<'tcx> {
- // Deallocation and retagging also do an access as part of their thing, so handle that here, too.
- let op = match &self.operation {
- Operation::Access(op) => op,
- Operation::Retag(_) => return self.grant_error(stack),
- Operation::Dealloc(_) => return self.dealloc_error(stack),
- };
- let action = format!(
- "attempting a {access} using {tag:?} at {alloc_id:?}[{offset:#x}]",
- access = op.kind,
- tag = op.tag,
- alloc_id = self.history.id,
- offset = self.offset.bytes(),
- );
- err_sb_ub(
- format!("{action}{}", error_cause(stack, op.tag)),
- Some(operation_summary("an access", self.history.id, op.range)),
- op.tag.and_then(|tag| self.get_logs_relevant_to(tag, None)),
- )
- }
-
- #[inline(never)] // This is only called on fatal code paths
- pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpError<'tcx> {
- let protected = match kind {
- ProtectorKind::WeakProtector => "weakly protected",
- ProtectorKind::StrongProtector => "strongly protected",
- };
- let call_id = self
- .machine
- .threads
- .all_stacks()
- .flatten()
- .map(|frame| {
- frame.extra.stacked_borrows.as_ref().expect("we should have Stacked Borrows data")
- })
- .find(|frame| frame.protected_tags.contains(&item.tag()))
- .map(|frame| frame.call_id)
- .unwrap(); // FIXME: Surely we should find something, but a panic seems wrong here?
- match self.operation {
- Operation::Dealloc(_) =>
- err_sb_ub(
- format!("deallocating while item {item:?} is {protected} by call {call_id:?}",),
- None,
- None,
- ),
- Operation::Retag(RetagOp { orig_tag: tag, .. })
- | Operation::Access(AccessOp { tag, .. }) =>
- err_sb_ub(
- format!(
- "not granting access to tag {tag:?} because that would remove {item:?} which is {protected} because it is an argument of call {call_id:?}",
- ),
- None,
- tag.and_then(|tag| self.get_logs_relevant_to(tag, Some(item.tag()))),
- ),
- }
- }
-
- #[inline(never)] // This is only called on fatal code paths
- pub fn dealloc_error(&self, stack: &Stack) -> InterpError<'tcx> {
- let Operation::Dealloc(op) = &self.operation else {
- unreachable!("dealloc_error should only be called during a deallocation")
- };
- err_sb_ub(
- format!(
- "attempting deallocation using {tag:?} at {alloc_id:?}{cause}",
- tag = op.tag,
- alloc_id = self.history.id,
- cause = error_cause(stack, op.tag),
- ),
- None,
- op.tag.and_then(|tag| self.get_logs_relevant_to(tag, None)),
- )
- }
-
- #[inline(never)]
- pub fn check_tracked_tag_popped(&self, item: &Item, global: &GlobalStateInner) {
- if !global.tracked_pointer_tags.contains(&item.tag()) {
- return;
- }
- let summary = match self.operation {
- Operation::Dealloc(_) => None,
- Operation::Access(AccessOp { kind, tag, .. }) => Some((tag, kind)),
- Operation::Retag(RetagOp { orig_tag, permission, .. }) => {
- let kind = match permission
- .expect("start_grant should set the current permission before popping a tag")
- {
- Permission::SharedReadOnly => AccessKind::Read,
- Permission::Unique => AccessKind::Write,
- Permission::SharedReadWrite | Permission::Disabled => {
- panic!("Only SharedReadOnly and Unique retags can pop tags");
- }
- };
- Some((orig_tag, kind))
- }
- };
- self.machine.emit_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(*item, summary));
- }
-}
-
-fn operation_summary(operation: &str, alloc_id: AllocId, alloc_range: AllocRange) -> String {
- format!("this error occurs as part of {operation} at {alloc_id:?}{alloc_range:?}")
-}
-
-fn error_cause(stack: &Stack, prov_extra: ProvenanceExtra) -> &'static str {
- if let ProvenanceExtra::Concrete(tag) = prov_extra {
- if (0..stack.len())
- .map(|i| stack.get(i).unwrap())
- .any(|item| item.tag() == tag && item.perm() != Permission::Disabled)
- {
- ", but that tag only grants SharedReadOnly permission for this location"
- } else {
- ", but that tag does not exist in the borrow stack for this location"
- }
- } else {
- ", but no exposed tags have suitable permission in the borrow stack for this location"
- }
-}
-
-impl RetagCause {
- fn summary(&self) -> String {
- match self {
- RetagCause::Normal => "retag",
- RetagCause::FnEntry => "FnEntry retag",
- RetagCause::FnReturn => "FnReturn retag",
- RetagCause::TwoPhase => "two-phase retag",
- }
- .to_string()
- }
-}
+++ /dev/null
-use crate::stacked_borrows::SbTag;
-use std::fmt;
-use std::num::NonZeroU64;
-
-/// An item in the per-location borrow stack.
-#[derive(Copy, Clone, Hash, PartialEq, Eq)]
-pub struct Item(u64);
-
-// An Item contains 3 bitfields:
-// * Bits 0-61 store an SbTag
-// * Bits 61-63 store a Permission
-// * Bit 64 stores a flag which indicates if we have a protector
-const TAG_MASK: u64 = u64::MAX >> 3;
-const PERM_MASK: u64 = 0x3 << 61;
-const PROTECTED_MASK: u64 = 0x1 << 63;
-
-const PERM_SHIFT: u64 = 61;
-const PROTECTED_SHIFT: u64 = 63;
-
-impl Item {
- pub fn new(tag: SbTag, perm: Permission, protected: bool) -> Self {
- assert!(tag.0.get() <= TAG_MASK);
- let packed_tag = tag.0.get();
- let packed_perm = perm.to_bits() << PERM_SHIFT;
- let packed_protected = u64::from(protected) << PROTECTED_SHIFT;
-
- let new = Self(packed_tag | packed_perm | packed_protected);
-
- debug_assert!(new.tag() == tag);
- debug_assert!(new.perm() == perm);
- debug_assert!(new.protected() == protected);
-
- new
- }
-
- /// The pointers the permission is granted to.
- pub fn tag(self) -> SbTag {
- SbTag(NonZeroU64::new(self.0 & TAG_MASK).unwrap())
- }
-
- /// The permission this item grants.
- pub fn perm(self) -> Permission {
- Permission::from_bits((self.0 & PERM_MASK) >> PERM_SHIFT)
- }
-
- /// Whether or not there is a protector for this tag
- pub fn protected(self) -> bool {
- self.0 & PROTECTED_MASK > 0
- }
-
- /// Set the Permission stored in this Item
- pub fn set_permission(&mut self, perm: Permission) {
- // Clear the current set permission
- self.0 &= !PERM_MASK;
- // Write Permission::Disabled to the Permission bits
- self.0 |= perm.to_bits() << PERM_SHIFT;
- }
-}
-
-impl fmt::Debug for Item {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "[{:?} for {:?}]", self.perm(), self.tag())
- }
-}
-
-/// Indicates which permission is granted (by this item to some pointers)
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-pub enum Permission {
- /// Grants unique mutable access.
- Unique,
- /// Grants shared mutable access.
- SharedReadWrite,
- /// Grants shared read-only access.
- SharedReadOnly,
- /// Grants no access, but separates two groups of SharedReadWrite so they are not
- /// all considered mutually compatible.
- Disabled,
-}
-
-impl Permission {
- const UNIQUE: u64 = 0;
- const SHARED_READ_WRITE: u64 = 1;
- const SHARED_READ_ONLY: u64 = 2;
- const DISABLED: u64 = 3;
-
- fn to_bits(self) -> u64 {
- match self {
- Permission::Unique => Self::UNIQUE,
- Permission::SharedReadWrite => Self::SHARED_READ_WRITE,
- Permission::SharedReadOnly => Self::SHARED_READ_ONLY,
- Permission::Disabled => Self::DISABLED,
- }
- }
-
- fn from_bits(perm: u64) -> Self {
- match perm {
- Self::UNIQUE => Permission::Unique,
- Self::SHARED_READ_WRITE => Permission::SharedReadWrite,
- Self::SHARED_READ_ONLY => Permission::SharedReadOnly,
- Self::DISABLED => Permission::Disabled,
- _ => unreachable!(),
- }
- }
-}
+++ /dev/null
-//! Implements "Stacked Borrows". See <https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md>
-//! for further information.
-
-use log::trace;
-use std::cell::RefCell;
-use std::cmp;
-use std::fmt;
-use std::fmt::Write;
-use std::num::NonZeroU64;
-
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::Mutability;
-use rustc_middle::mir::RetagKind;
-use rustc_middle::ty::{
- self,
- layout::{HasParamEnv, LayoutOf},
-};
-use rustc_target::abi::Abi;
-use rustc_target::abi::Size;
-use smallvec::SmallVec;
-
-use crate::*;
-
-pub mod diagnostics;
-use diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder, RetagCause, TagHistory};
-
-mod item;
-pub use item::{Item, Permission};
-mod stack;
-pub use stack::Stack;
-
-pub type CallId = NonZeroU64;
-
-// Even reading memory can have effects on the stack, so we need a `RefCell` here.
-pub type AllocExtra = RefCell<Stacks>;
-
-/// Tracking pointer provenance
-#[derive(Copy, Clone, Hash, PartialEq, Eq)]
-pub struct SbTag(NonZeroU64);
-
-impl SbTag {
- pub fn new(i: u64) -> Option<Self> {
- NonZeroU64::new(i).map(SbTag)
- }
-
- // The default to be used when SB is disabled
- #[allow(clippy::should_implement_trait)]
- pub fn default() -> Self {
- Self::new(1).unwrap()
- }
-}
-
-impl fmt::Debug for SbTag {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "<{}>", self.0)
- }
-}
-
-#[derive(Debug)]
-pub struct FrameExtra {
- /// The ID of the call this frame corresponds to.
- call_id: CallId,
-
- /// If this frame is protecting any tags, they are listed here. We use this list to do
- /// incremental updates of the global list of protected tags stored in the
- /// `stacked_borrows::GlobalState` upon function return, and if we attempt to pop a protected
- /// tag, to identify which call is responsible for protecting the tag.
- /// See `Stack::item_invalidated` for more explanation.
- ///
- /// This will contain one tag per reference passed to the function, so
- /// a size of 2 is enough for the vast majority of functions.
- protected_tags: SmallVec<[SbTag; 2]>,
-}
-
-impl VisitTags for FrameExtra {
- fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
- // `protected_tags` are fine to GC.
- }
-}
-
-/// Extra per-allocation state.
-#[derive(Clone, Debug)]
-pub struct Stacks {
- // Even reading memory can have effects on the stack, so we need a `RefCell` here.
- stacks: RangeMap<Stack>,
- /// Stores past operations on this allocation
- history: AllocHistory,
- /// The set of tags that have been exposed inside this allocation.
- exposed_tags: FxHashSet<SbTag>,
- /// Whether this memory has been modified since the last time the tag GC ran
- modified_since_last_gc: bool,
-}
-
-/// The flavor of the protector.
-#[derive(Copy, Clone, Debug)]
-enum ProtectorKind {
- /// Protected against aliasing violations from other pointers.
- ///
- /// Items protected like this cause UB when they are invalidated, *but* the pointer itself may
- /// still be used to issue a deallocation.
- ///
- /// This is required for LLVM IR pointers that are `noalias` but *not* `dereferenceable`.
- WeakProtector,
-
- /// Protected against any kind of invalidation.
- ///
- /// Items protected like this cause UB when they are invalidated or the memory is deallocated.
- /// This is strictly stronger protection than `WeakProtector`.
- ///
- /// This is required for LLVM IR pointers that are `dereferenceable` (and also allows `noalias`).
- StrongProtector,
-}
-
-/// Extra global state, available to the memory access hooks.
-#[derive(Debug)]
-pub struct GlobalStateInner {
- /// Next unused pointer ID (tag).
- next_ptr_tag: SbTag,
- /// Table storing the "base" tag for each allocation.
- /// The base tag is the one used for the initial pointer.
- /// We need this in a separate table to handle cyclic statics.
- base_ptr_tags: FxHashMap<AllocId, SbTag>,
- /// Next unused call ID (for protectors).
- next_call_id: CallId,
- /// All currently protected tags, and the status of their protection.
- /// An item is protected if its tag is in this set, *and* it has the "protected" bit set.
- /// We add tags to this when they are created with a protector in `reborrow`, and
- /// we remove tags from this when the call which is protecting them returns, in
- /// `GlobalStateInner::end_call`. See `Stack::item_invalidated` for more details.
- protected_tags: FxHashMap<SbTag, ProtectorKind>,
- /// The pointer ids to trace
- tracked_pointer_tags: FxHashSet<SbTag>,
- /// The call ids to trace
- tracked_call_ids: FxHashSet<CallId>,
- /// Whether to recurse into datatypes when searching for pointers to retag.
- retag_fields: RetagFields,
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum RetagFields {
- /// Don't retag any fields.
- No,
- /// Retag all fields.
- Yes,
- /// Only retag fields of types with Scalar and ScalarPair layout,
- /// to match the LLVM `noalias` we generate.
- OnlyScalar,
-}
-
-impl VisitTags for GlobalStateInner {
- fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
- // The only candidate is base_ptr_tags, and that does not need visiting since we don't ever
- // GC the bottommost tag.
- }
-}
-
-/// We need interior mutable access to the global state.
-pub type GlobalState = RefCell<GlobalStateInner>;
-
-/// Indicates which kind of access is being performed.
-#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
-pub enum AccessKind {
- Read,
- Write,
-}
-
-impl fmt::Display for AccessKind {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- AccessKind::Read => write!(f, "read access"),
- AccessKind::Write => write!(f, "write access"),
- }
- }
-}
-
-/// Indicates which kind of reference is being created.
-/// Used by high-level `reborrow` to compute which permissions to grant to the
-/// new pointer.
-#[derive(Copy, Clone, Hash, PartialEq, Eq)]
-pub enum RefKind {
- /// `&mut` and `Box`.
- Unique { two_phase: bool },
- /// `&` with or without interior mutability.
- Shared,
- /// `*mut`/`*const` (raw pointers).
- Raw { mutable: bool },
-}
-
-impl fmt::Display for RefKind {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- RefKind::Unique { two_phase: false } => write!(f, "unique reference"),
- RefKind::Unique { two_phase: true } => write!(f, "unique reference (two-phase)"),
- RefKind::Shared => write!(f, "shared reference"),
- RefKind::Raw { mutable: true } => write!(f, "raw (mutable) pointer"),
- RefKind::Raw { mutable: false } => write!(f, "raw (constant) pointer"),
- }
- }
-}
-
-/// Utilities for initialization and ID generation
-impl GlobalStateInner {
- pub fn new(
- tracked_pointer_tags: FxHashSet<SbTag>,
- tracked_call_ids: FxHashSet<CallId>,
- retag_fields: RetagFields,
- ) -> Self {
- GlobalStateInner {
- next_ptr_tag: SbTag(NonZeroU64::new(1).unwrap()),
- base_ptr_tags: FxHashMap::default(),
- next_call_id: NonZeroU64::new(1).unwrap(),
- protected_tags: FxHashMap::default(),
- tracked_pointer_tags,
- tracked_call_ids,
- retag_fields,
- }
- }
-
- /// Generates a new pointer tag. Remember to also check track_pointer_tags and log its creation!
- fn new_ptr(&mut self) -> SbTag {
- let id = self.next_ptr_tag;
- self.next_ptr_tag = SbTag(NonZeroU64::new(id.0.get() + 1).unwrap());
- id
- }
-
- pub fn new_frame(&mut self, machine: &MiriMachine<'_, '_>) -> FrameExtra {
- let call_id = self.next_call_id;
- trace!("new_frame: Assigning call ID {}", call_id);
- if self.tracked_call_ids.contains(&call_id) {
- machine.emit_diagnostic(NonHaltingDiagnostic::CreatedCallId(call_id));
- }
- self.next_call_id = NonZeroU64::new(call_id.get() + 1).unwrap();
- FrameExtra { call_id, protected_tags: SmallVec::new() }
- }
-
- pub fn end_call(&mut self, frame: &machine::FrameData<'_>) {
- for tag in &frame
- .stacked_borrows
- .as_ref()
- .expect("we should have Stacked Borrows data")
- .protected_tags
- {
- self.protected_tags.remove(tag);
- }
- }
-
- pub fn base_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_, '_>) -> SbTag {
- self.base_ptr_tags.get(&id).copied().unwrap_or_else(|| {
- let tag = self.new_ptr();
- if self.tracked_pointer_tags.contains(&tag) {
- machine.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(tag.0, None, None));
- }
- trace!("New allocation {:?} has base tag {:?}", id, tag);
- self.base_ptr_tags.try_insert(id, tag).unwrap();
- tag
- })
- }
-}
-
-/// Error reporting
-pub fn err_sb_ub<'tcx>(
- msg: String,
- help: Option<String>,
- history: Option<TagHistory>,
-) -> InterpError<'tcx> {
- err_machine_stop!(TerminationInfo::StackedBorrowsUb { msg, help, history })
-}
-
-// # Stacked Borrows Core Begin
-
-/// We need to make at least the following things true:
-///
-/// U1: After creating a `Uniq`, it is at the top.
-/// U2: If the top is `Uniq`, accesses must be through that `Uniq` or remove it.
-/// U3: If an access happens with a `Uniq`, it requires the `Uniq` to be in the stack.
-///
-/// F1: After creating a `&`, the parts outside `UnsafeCell` have our `SharedReadOnly` on top.
-/// F2: If a write access happens, it pops the `SharedReadOnly`. This has three pieces:
-/// F2a: If a write happens granted by an item below our `SharedReadOnly`, the `SharedReadOnly`
-/// gets popped.
-/// F2b: No `SharedReadWrite` or `Unique` will ever be added on top of our `SharedReadOnly`.
-/// F3: If an access happens with an `&` outside `UnsafeCell`,
-/// it requires the `SharedReadOnly` to still be in the stack.
-
-/// Core relation on `Permission` to define which accesses are allowed
-impl Permission {
- /// This defines for a given permission, whether it permits the given kind of access.
- fn grants(self, access: AccessKind) -> bool {
- // Disabled grants nothing. Otherwise, all items grant read access, and except for SharedReadOnly they grant write access.
- self != Permission::Disabled
- && (access == AccessKind::Read || self != Permission::SharedReadOnly)
- }
-}
-
-/// Determines whether an item was invalidated by a conflicting access, or by deallocation.
-#[derive(Copy, Clone, Debug)]
-enum ItemInvalidationCause {
- Conflict,
- Dealloc,
-}
-
-/// Core per-location operations: access, dealloc, reborrow.
-impl<'tcx> Stack {
- /// Find the first write-incompatible item above the given one --
- /// i.e, find the height to which the stack will be truncated when writing to `granting`.
- fn find_first_write_incompatible(&self, granting: usize) -> usize {
- let perm = self.get(granting).unwrap().perm();
- match perm {
- Permission::SharedReadOnly => bug!("Cannot use SharedReadOnly for writing"),
- Permission::Disabled => bug!("Cannot use Disabled for anything"),
- Permission::Unique => {
- // On a write, everything above us is incompatible.
- granting + 1
- }
- Permission::SharedReadWrite => {
- // The SharedReadWrite *just* above us are compatible, to skip those.
- let mut idx = granting + 1;
- while let Some(item) = self.get(idx) {
- if item.perm() == Permission::SharedReadWrite {
- // Go on.
- idx += 1;
- } else {
- // Found first incompatible!
- break;
- }
- }
- idx
- }
- }
- }
-
- /// Check if the given item is protected.
- ///
- /// The `provoking_access` argument is only used to produce diagnostics.
- /// It is `Some` when we are granting the contained access for said tag, and it is
- /// `None` during a deallocation.
- /// Within `provoking_access, the `AllocRange` refers the entire operation, and
- /// the `Size` refers to the specific location in the `AllocRange` that we are
- /// currently checking.
- fn item_invalidated(
- item: &Item,
- global: &GlobalStateInner,
- dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
- cause: ItemInvalidationCause,
- ) -> InterpResult<'tcx> {
- if !global.tracked_pointer_tags.is_empty() {
- dcx.check_tracked_tag_popped(item, global);
- }
-
- if !item.protected() {
- return Ok(());
- }
-
- // We store tags twice, once in global.protected_tags and once in each call frame.
- // We do this because consulting a single global set in this function is faster
- // than attempting to search all call frames in the program for the `FrameExtra`
- // (if any) which is protecting the popped tag.
- //
- // This duplication trades off making `end_call` slower to make this function faster. This
- // trade-off is profitable in practice for a combination of two reasons.
- // 1. A single protected tag can (and does in some programs) protect thousands of `Item`s.
- // Therefore, adding overhead in function call/return is profitable even if it only
- // saves a little work in this function.
- // 2. Most frames protect only one or two tags. So this duplicative global turns a search
- // which ends up about linear in the number of protected tags in the program into a
- // constant time check (and a slow linear, because the tags in the frames aren't contiguous).
- if let Some(&protector_kind) = global.protected_tags.get(&item.tag()) {
- // The only way this is okay is if the protector is weak and we are deallocating with
- // the right pointer.
- let allowed = matches!(cause, ItemInvalidationCause::Dealloc)
- && matches!(protector_kind, ProtectorKind::WeakProtector);
- if !allowed {
- return Err(dcx.protector_error(item, protector_kind).into());
- }
- }
- Ok(())
- }
-
- /// Test if a memory `access` using pointer tagged `tag` is granted.
- /// If yes, return the index of the item that granted it.
- /// `range` refers the entire operation, and `offset` refers to the specific offset into the
- /// allocation that we are currently checking.
- fn access(
- &mut self,
- access: AccessKind,
- tag: ProvenanceExtra,
- global: &GlobalStateInner,
- dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
- exposed_tags: &FxHashSet<SbTag>,
- ) -> InterpResult<'tcx> {
- // Two main steps: Find granting item, remove incompatible items above.
-
- // Step 1: Find granting item.
- let granting_idx =
- self.find_granting(access, tag, exposed_tags).map_err(|()| dcx.access_error(self))?;
-
- // Step 2: Remove incompatible items above them. Make sure we do not remove protected
- // items. Behavior differs for reads and writes.
- // In case of wildcards/unknown matches, we remove everything that is *definitely* gone.
- if access == AccessKind::Write {
- // Remove everything above the write-compatible items, like a proper stack. This makes sure read-only and unique
- // pointers become invalid on write accesses (ensures F2a, and ensures U2 for write accesses).
- let first_incompatible_idx = if let Some(granting_idx) = granting_idx {
- // The granting_idx *might* be approximate, but any lower idx would remove more
- // things. Even if this is a Unique and the lower idx is an SRW (which removes
- // less), there is an SRW group boundary here so strictly more would get removed.
- self.find_first_write_incompatible(granting_idx)
- } else {
- // We are writing to something in the unknown part.
- // There is a SRW group boundary between the unknown and the known, so everything is incompatible.
- 0
- };
- self.pop_items_after(first_incompatible_idx, |item| {
- Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
- dcx.log_invalidation(item.tag());
- Ok(())
- })?;
- } else {
- // On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses.
- // The reason this is not following the stack discipline (by removing the first Unique and
- // everything on top of it) is that in `let raw = &mut *x as *mut _; let _val = *x;`, the second statement
- // would pop the `Unique` from the reborrow of the first statement, and subsequently also pop the
- // `SharedReadWrite` for `raw`.
- // This pattern occurs a lot in the standard library: create a raw pointer, then also create a shared
- // reference and use that.
- // We *disable* instead of removing `Unique` to avoid "connecting" two neighbouring blocks of SRWs.
- let first_incompatible_idx = if let Some(granting_idx) = granting_idx {
- // The granting_idx *might* be approximate, but any lower idx would disable more things.
- granting_idx + 1
- } else {
- // We are reading from something in the unknown part. That means *all* `Unique` we know about are dead now.
- 0
- };
- self.disable_uniques_starting_at(first_incompatible_idx, |item| {
- Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
- dcx.log_invalidation(item.tag());
- Ok(())
- })?;
- }
-
- // If this was an approximate action, we now collapse everything into an unknown.
- if granting_idx.is_none() || matches!(tag, ProvenanceExtra::Wildcard) {
- // Compute the upper bound of the items that remain.
- // (This is why we did all the work above: to reduce the items we have to consider here.)
- let mut max = NonZeroU64::new(1).unwrap();
- for i in 0..self.len() {
- let item = self.get(i).unwrap();
- // Skip disabled items, they cannot be matched anyway.
- if !matches!(item.perm(), Permission::Disabled) {
- // We are looking for a strict upper bound, so add 1 to this tag.
- max = cmp::max(item.tag().0.checked_add(1).unwrap(), max);
- }
- }
- if let Some(unk) = self.unknown_bottom() {
- max = cmp::max(unk.0, max);
- }
- // Use `max` as new strict upper bound for everything.
- trace!(
- "access: forgetting stack to upper bound {max} due to wildcard or unknown access"
- );
- self.set_unknown_bottom(SbTag(max));
- }
-
- // Done.
- Ok(())
- }
-
- /// Deallocate a location: Like a write access, but also there must be no
- /// active protectors at all because we will remove all items.
- fn dealloc(
- &mut self,
- tag: ProvenanceExtra,
- global: &GlobalStateInner,
- dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
- exposed_tags: &FxHashSet<SbTag>,
- ) -> InterpResult<'tcx> {
- // Step 1: Make a write access.
- // As part of this we do regular protector checking, i.e. even weakly protected items cause UB when popped.
- self.access(AccessKind::Write, tag, global, dcx, exposed_tags)?;
-
- // Step 2: Pretend we remove the remaining items, checking if any are strongly protected.
- for idx in (0..self.len()).rev() {
- let item = self.get(idx).unwrap();
- Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Dealloc)?;
- }
-
- Ok(())
- }
-
- /// Derive a new pointer from one with the given tag.
- ///
- /// `access` indicates which kind of memory access this retag itself should correspond to.
- fn grant(
- &mut self,
- derived_from: ProvenanceExtra,
- new: Item,
- access: Option<AccessKind>,
- global: &GlobalStateInner,
- dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>,
- exposed_tags: &FxHashSet<SbTag>,
- ) -> InterpResult<'tcx> {
- dcx.start_grant(new.perm());
-
- // Compute where to put the new item.
- // Either way, we ensure that we insert the new item in a way such that between
- // `derived_from` and the new one, there are only items *compatible with* `derived_from`.
- let new_idx = if let Some(access) = access {
- // Simple case: We are just a regular memory access, and then push our thing on top,
- // like a regular stack.
- // This ensures F2b for `Unique`, by removing offending `SharedReadOnly`.
- self.access(access, derived_from, global, dcx, exposed_tags)?;
-
- // We insert "as far up as possible": We know only compatible items are remaining
- // on top of `derived_from`, and we want the new item at the top so that we
- // get the strongest possible guarantees.
- // This ensures U1 and F1.
- self.len()
- } else {
- // The tricky case: creating a new SRW permission without actually being an access.
- assert!(new.perm() == Permission::SharedReadWrite);
-
- // First we figure out which item grants our parent (`derived_from`) this kind of access.
- // We use that to determine where to put the new item.
- let granting_idx = self
- .find_granting(AccessKind::Write, derived_from, exposed_tags)
- .map_err(|()| dcx.grant_error(self))?;
-
- let (Some(granting_idx), ProvenanceExtra::Concrete(_)) = (granting_idx, derived_from) else {
- // The parent is a wildcard pointer or matched the unknown bottom.
- // This is approximate. Nobody knows what happened, so forget everything.
- // The new thing is SRW anyway, so we cannot push it "on top of the unkown part"
- // (for all we know, it might join an SRW group inside the unknown).
- trace!("reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown");
- self.set_unknown_bottom(global.next_ptr_tag);
- return Ok(());
- };
-
- // SharedReadWrite can coexist with "existing loans", meaning they don't act like a write
- // access. Instead of popping the stack, we insert the item at the place the stack would
- // be popped to (i.e., we insert it above all the write-compatible items).
- // This ensures F2b by adding the new item below any potentially existing `SharedReadOnly`.
- self.find_first_write_incompatible(granting_idx)
- };
-
- // Put the new item there.
- trace!("reborrow: adding item {:?}", new);
- self.insert(new_idx, new);
- Ok(())
- }
-}
-// # Stacked Borrows Core End
-
-/// Integration with the SbTag garbage collector
-impl Stacks {
- pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<SbTag>) {
- if self.modified_since_last_gc {
- for stack in self.stacks.iter_mut_all() {
- if stack.len() > 64 {
- stack.retain(live_tags);
- }
- }
- self.modified_since_last_gc = false;
- }
- }
-}
-
-impl VisitTags for Stacks {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
- for tag in self.exposed_tags.iter().copied() {
- visit(tag);
- }
- }
-}
-
-/// Map per-stack operations to higher-level per-location-range operations.
-impl<'tcx> Stacks {
- /// Creates a new stack with an initial tag. For diagnostic purposes, we also need to know
- /// the [`AllocId`] of the allocation this is associated with.
- fn new(
- size: Size,
- perm: Permission,
- tag: SbTag,
- id: AllocId,
- machine: &MiriMachine<'_, '_>,
- ) -> Self {
- let item = Item::new(tag, perm, false);
- let stack = Stack::new(item);
-
- Stacks {
- stacks: RangeMap::new(size, stack),
- history: AllocHistory::new(id, item, machine),
- exposed_tags: FxHashSet::default(),
- modified_since_last_gc: false,
- }
- }
-
- /// Call `f` on every stack in the range.
- fn for_each(
- &mut self,
- range: AllocRange,
- mut dcx_builder: DiagnosticCxBuilder<'_, '_, 'tcx>,
- mut f: impl FnMut(
- &mut Stack,
- &mut DiagnosticCx<'_, '_, '_, 'tcx>,
- &mut FxHashSet<SbTag>,
- ) -> InterpResult<'tcx>,
- ) -> InterpResult<'tcx> {
- self.modified_since_last_gc = true;
- for (offset, stack) in self.stacks.iter_mut(range.start, range.size) {
- let mut dcx = dcx_builder.build(&mut self.history, offset);
- f(stack, &mut dcx, &mut self.exposed_tags)?;
- dcx_builder = dcx.unbuild();
- }
- Ok(())
- }
-}
-
-/// Glue code to connect with Miri Machine Hooks
-impl Stacks {
- pub fn new_allocation(
- id: AllocId,
- size: Size,
- state: &GlobalState,
- kind: MemoryKind<MiriMemoryKind>,
- machine: &MiriMachine<'_, '_>,
- ) -> Self {
- let mut extra = state.borrow_mut();
- let (base_tag, perm) = match kind {
- // New unique borrow. This tag is not accessible by the program,
- // so it will only ever be used when using the local directly (i.e.,
- // not through a pointer). That is, whenever we directly write to a local, this will pop
- // everything else off the stack, invalidating all previous pointers,
- // and in particular, *all* raw pointers.
- MemoryKind::Stack => (extra.base_ptr_tag(id, machine), Permission::Unique),
- // Everything else is shared by default.
- _ => (extra.base_ptr_tag(id, machine), Permission::SharedReadWrite),
- };
- Stacks::new(size, perm, base_tag, id, machine)
- }
-
- #[inline(always)]
- pub fn before_memory_read<'tcx, 'mir, 'ecx>(
- &mut self,
- alloc_id: AllocId,
- tag: ProvenanceExtra,
- range: AllocRange,
- machine: &'ecx MiriMachine<'mir, 'tcx>,
- ) -> InterpResult<'tcx>
- where
- 'tcx: 'ecx,
- {
- trace!(
- "read access with tag {:?}: {:?}, size {}",
- tag,
- Pointer::new(alloc_id, range.start),
- range.size.bytes()
- );
- let dcx = DiagnosticCxBuilder::read(machine, tag, range);
- let state = machine.stacked_borrows.as_ref().unwrap().borrow();
- self.for_each(range, dcx, |stack, dcx, exposed_tags| {
- stack.access(AccessKind::Read, tag, &state, dcx, exposed_tags)
- })
- }
-
- #[inline(always)]
- pub fn before_memory_write<'tcx>(
- &mut self,
- alloc_id: AllocId,
- tag: ProvenanceExtra,
- range: AllocRange,
- machine: &mut MiriMachine<'_, 'tcx>,
- ) -> InterpResult<'tcx> {
- trace!(
- "write access with tag {:?}: {:?}, size {}",
- tag,
- Pointer::new(alloc_id, range.start),
- range.size.bytes()
- );
- let dcx = DiagnosticCxBuilder::write(machine, tag, range);
- let state = machine.stacked_borrows.as_ref().unwrap().borrow();
- self.for_each(range, dcx, |stack, dcx, exposed_tags| {
- stack.access(AccessKind::Write, tag, &state, dcx, exposed_tags)
- })
- }
-
- #[inline(always)]
- pub fn before_memory_deallocation<'tcx>(
- &mut self,
- alloc_id: AllocId,
- tag: ProvenanceExtra,
- range: AllocRange,
- machine: &mut MiriMachine<'_, 'tcx>,
- ) -> InterpResult<'tcx> {
- trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes());
- let dcx = DiagnosticCxBuilder::dealloc(machine, tag);
- let state = machine.stacked_borrows.as_ref().unwrap().borrow();
- self.for_each(range, dcx, |stack, dcx, exposed_tags| {
- stack.dealloc(tag, &state, dcx, exposed_tags)
- })?;
- Ok(())
- }
-}
-
-/// Retagging/reborrowing. There is some policy in here, such as which permissions
-/// to grant for which references, and when to add protectors.
-impl<'mir: 'ecx, 'tcx: 'mir, 'ecx> EvalContextPrivExt<'mir, 'tcx, 'ecx>
- for crate::MiriInterpCx<'mir, 'tcx>
-{
-}
-trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> {
- /// Returns the `AllocId` the reborrow was done in, if some actual borrow stack manipulation
- /// happened.
- fn reborrow(
- &mut self,
- place: &MPlaceTy<'tcx, Provenance>,
- size: Size,
- kind: RefKind,
- retag_cause: RetagCause, // What caused this retag, for diagnostics only
- new_tag: SbTag,
- protect: Option<ProtectorKind>,
- ) -> InterpResult<'tcx, Option<AllocId>> {
- let this = self.eval_context_mut();
-
- // It is crucial that this gets called on all code paths, to ensure we track tag creation.
- let log_creation = |this: &MiriInterpCx<'mir, 'tcx>,
- loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag
- -> InterpResult<'tcx> {
- let global = this.machine.stacked_borrows.as_ref().unwrap().borrow();
- let ty = place.layout.ty;
- if global.tracked_pointer_tags.contains(&new_tag) {
- let mut kind_str = format!("{kind}");
- match kind {
- RefKind::Unique { two_phase: false }
- if !ty.is_unpin(*this.tcx, this.param_env()) =>
- {
- write!(kind_str, " (!Unpin pointee type {ty})").unwrap()
- },
- RefKind::Shared
- if !ty.is_freeze(*this.tcx, this.param_env()) =>
- {
- write!(kind_str, " (!Freeze pointee type {ty})").unwrap()
- },
- _ => write!(kind_str, " (pointee type {ty})").unwrap(),
- };
- this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
- new_tag.0,
- Some(kind_str),
- loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, size), orig_tag)),
- ));
- }
- drop(global); // don't hold that reference any longer than we have to
-
- let Some((alloc_id, base_offset, orig_tag)) = loc else {
- return Ok(())
- };
-
- let (_size, _align, alloc_kind) = this.get_alloc_info(alloc_id);
- match alloc_kind {
- AllocKind::LiveData => {
- // This should have alloc_extra data, but `get_alloc_extra` can still fail
- // if converting this alloc_id from a global to a local one
- // uncovers a non-supported `extern static`.
- let extra = this.get_alloc_extra(alloc_id)?;
- let mut stacked_borrows = extra
- .stacked_borrows
- .as_ref()
- .expect("we should have Stacked Borrows data")
- .borrow_mut();
- // Note that we create a *second* `DiagnosticCxBuilder` below for the actual retag.
- // FIXME: can this be done cleaner?
- let dcx = DiagnosticCxBuilder::retag(
- &this.machine,
- retag_cause,
- new_tag,
- orig_tag,
- alloc_range(base_offset, size),
- );
- let mut dcx = dcx.build(&mut stacked_borrows.history, base_offset);
- dcx.log_creation();
- if protect.is_some() {
- dcx.log_protector();
- }
- }
- AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
- // No stacked borrows on these allocations.
- }
- }
- Ok(())
- };
-
- if size == Size::ZERO {
- trace!(
- "reborrow of size 0: {} reference {:?} derived from {:?} (pointee {})",
- kind,
- new_tag,
- place.ptr,
- place.layout.ty,
- );
- // Don't update any stacks for a zero-sized access; borrow stacks are per-byte and this
- // touches no bytes so there is no stack to put this tag in.
- // However, if the pointer for this operation points at a real allocation we still
- // record where it was created so that we can issue a helpful diagnostic if there is an
- // attempt to use it for a non-zero-sized access.
- // Dangling slices are a common case here; it's valid to get their length but with raw
- // pointer tagging for example all calls to get_unchecked on them are invalid.
- if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr) {
- log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
- return Ok(Some(alloc_id));
- }
- // This pointer doesn't come with an AllocId. :shrug:
- log_creation(this, None)?;
- return Ok(None);
- }
-
- let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr)?;
- log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
-
- // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
- let (alloc_size, _) = this.get_live_alloc_size_and_align(alloc_id)?;
- if base_offset + size > alloc_size {
- throw_ub!(PointerOutOfBounds {
- alloc_id,
- alloc_size,
- ptr_offset: this.machine_usize_to_isize(base_offset.bytes()),
- ptr_size: size,
- msg: CheckInAllocMsg::InboundsTest
- });
- }
-
- trace!(
- "reborrow: {} reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
- kind,
- new_tag,
- orig_tag,
- place.layout.ty,
- Pointer::new(alloc_id, base_offset),
- size.bytes()
- );
-
- if let Some(protect) = protect {
- // See comment in `Stack::item_invalidated` for why we store the tag twice.
- this.frame_mut().extra.stacked_borrows.as_mut().unwrap().protected_tags.push(new_tag);
- this.machine
- .stacked_borrows
- .as_mut()
- .unwrap()
- .get_mut()
- .protected_tags
- .insert(new_tag, protect);
- }
-
- // Update the stacks.
- // Make sure that raw pointers and mutable shared references are reborrowed "weak":
- // There could be existing unique pointers reborrowed from them that should remain valid!
- let (perm, access) = match kind {
- RefKind::Unique { two_phase } => {
- // Permission is Unique only if the type is `Unpin` and this is not twophase
- let perm = if !two_phase && place.layout.ty.is_unpin(*this.tcx, this.param_env()) {
- Permission::Unique
- } else {
- Permission::SharedReadWrite
- };
- // We do an access for all full borrows, even if `!Unpin`.
- let access = if !two_phase { Some(AccessKind::Write) } else { None };
- (perm, access)
- }
- RefKind::Raw { mutable: true } => {
- // Creating a raw ptr does not count as an access
- (Permission::SharedReadWrite, None)
- }
- RefKind::Shared | RefKind::Raw { mutable: false } => {
- // Shared references and *const are a whole different kind of game, the
- // permission is not uniform across the entire range!
- // We need a frozen-sensitive reborrow.
- // We have to use shared references to alloc/memory_extra here since
- // `visit_freeze_sensitive` needs to access the global state.
- let alloc_extra = this.get_alloc_extra(alloc_id)?;
- let mut stacked_borrows = alloc_extra
- .stacked_borrows
- .as_ref()
- .expect("we should have Stacked Borrows data")
- .borrow_mut();
- this.visit_freeze_sensitive(place, size, |mut range, frozen| {
- // Adjust range.
- range.start += base_offset;
- // We are only ever `SharedReadOnly` inside the frozen bits.
- let (perm, access) = if frozen {
- (Permission::SharedReadOnly, Some(AccessKind::Read))
- } else {
- // Inside UnsafeCell, this does *not* count as an access, as there
- // might actually be mutable references further up the stack that
- // we have to keep alive.
- (Permission::SharedReadWrite, None)
- };
- let protected = if frozen {
- protect.is_some()
- } else {
- // We do not protect inside UnsafeCell.
- // This fixes https://github.com/rust-lang/rust/issues/55005.
- false
- };
- let item = Item::new(new_tag, perm, protected);
- let global = this.machine.stacked_borrows.as_ref().unwrap().borrow();
- let dcx = DiagnosticCxBuilder::retag(
- &this.machine,
- retag_cause,
- new_tag,
- orig_tag,
- alloc_range(base_offset, size),
- );
- stacked_borrows.for_each(range, dcx, |stack, dcx, exposed_tags| {
- stack.grant(orig_tag, item, access, &global, dcx, exposed_tags)
- })?;
- drop(global);
- if let Some(access) = access {
- assert_eq!(access, AccessKind::Read);
- // Make sure the data race model also knows about this.
- if let Some(data_race) = alloc_extra.data_race.as_ref() {
- data_race.read(alloc_id, range, &this.machine)?;
- }
- }
- Ok(())
- })?;
- return Ok(Some(alloc_id));
- }
- };
-
- // Here we can avoid `borrow()` calls because we have mutable references.
- // Note that this asserts that the allocation is mutable -- but since we are creating a
- // mutable pointer, that seems reasonable.
- let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc_id)?;
- let stacked_borrows = alloc_extra
- .stacked_borrows
- .as_mut()
- .expect("we should have Stacked Borrows data")
- .get_mut();
- let item = Item::new(new_tag, perm, protect.is_some());
- let range = alloc_range(base_offset, size);
- let global = machine.stacked_borrows.as_ref().unwrap().borrow();
- let dcx = DiagnosticCxBuilder::retag(
- machine,
- retag_cause,
- new_tag,
- orig_tag,
- alloc_range(base_offset, size),
- );
- stacked_borrows.for_each(range, dcx, |stack, dcx, exposed_tags| {
- stack.grant(orig_tag, item, access, &global, dcx, exposed_tags)
- })?;
- drop(global);
- if let Some(access) = access {
- assert_eq!(access, AccessKind::Write);
- // Make sure the data race model also knows about this.
- if let Some(data_race) = alloc_extra.data_race.as_mut() {
- data_race.write(alloc_id, range, machine)?;
- }
- }
-
- Ok(Some(alloc_id))
- }
-
- /// Retags an indidual pointer, returning the retagged version.
- /// `mutbl` can be `None` to make this a raw pointer.
- fn retag_reference(
- &mut self,
- val: &ImmTy<'tcx, Provenance>,
- kind: RefKind,
- retag_cause: RetagCause, // What caused this retag, for diagnostics only
- protect: Option<ProtectorKind>,
- ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
- let this = self.eval_context_mut();
- // We want a place for where the ptr *points to*, so we get one.
- let place = this.ref_to_mplace(val)?;
- let size = this.size_and_align_of_mplace(&place)?.map(|(size, _)| size);
- // FIXME: If we cannot determine the size (because the unsized tail is an `extern type`),
- // bail out -- we cannot reasonably figure out which memory range to reborrow.
- // See https://github.com/rust-lang/unsafe-code-guidelines/issues/276.
- let size = match size {
- Some(size) => size,
- None => return Ok(val.clone()),
- };
-
- // Compute new borrow.
- let new_tag = this.machine.stacked_borrows.as_mut().unwrap().get_mut().new_ptr();
-
- // Reborrow.
- let alloc_id = this.reborrow(&place, size, kind, retag_cause, new_tag, protect)?;
-
- // Adjust pointer.
- let new_place = place.map_provenance(|p| {
- p.map(|prov| {
- match alloc_id {
- Some(alloc_id) => {
- // If `reborrow` could figure out the AllocId of this ptr, hard-code it into the new one.
- // Even if we started out with a wildcard, this newly retagged pointer is tied to that allocation.
- Provenance::Concrete { alloc_id, sb: new_tag }
- }
- None => {
- // Looks like this has to stay a wildcard pointer.
- assert!(matches!(prov, Provenance::Wildcard));
- Provenance::Wildcard
- }
- }
- })
- });
-
- // Return new pointer.
- Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
- }
-}
-
-impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
-pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
- fn retag(&mut self, kind: RetagKind, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
- let this = self.eval_context_mut();
- let retag_fields = this.machine.stacked_borrows.as_mut().unwrap().get_mut().retag_fields;
- let retag_cause = match kind {
- RetagKind::TwoPhase { .. } => RetagCause::TwoPhase,
- RetagKind::FnEntry => RetagCause::FnEntry,
- RetagKind::Raw | RetagKind::Default => RetagCause::Normal,
- };
- let mut visitor = RetagVisitor { ecx: this, kind, retag_cause, retag_fields };
- return visitor.visit_value(place);
-
- // The actual visitor.
- struct RetagVisitor<'ecx, 'mir, 'tcx> {
- ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>,
- kind: RetagKind,
- retag_cause: RetagCause,
- retag_fields: RetagFields,
- }
- impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> {
- #[inline(always)] // yes this helps in our benchmarks
- fn retag_place(
- &mut self,
- place: &PlaceTy<'tcx, Provenance>,
- ref_kind: RefKind,
- retag_cause: RetagCause,
- protector: Option<ProtectorKind>,
- ) -> InterpResult<'tcx> {
- let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
- let val = self.ecx.retag_reference(&val, ref_kind, retag_cause, protector)?;
- self.ecx.write_immediate(*val, place)?;
- Ok(())
- }
- }
- impl<'ecx, 'mir, 'tcx> MutValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>>
- for RetagVisitor<'ecx, 'mir, 'tcx>
- {
- type V = PlaceTy<'tcx, Provenance>;
-
- #[inline(always)]
- fn ecx(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> {
- self.ecx
- }
-
- fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
- // Boxes get a weak protectors, since they may be deallocated.
- self.retag_place(
- place,
- RefKind::Unique { two_phase: false },
- self.retag_cause,
- /*protector*/
- (self.kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector),
- )
- }
-
- fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
- // If this place is smaller than a pointer, we know that it can't contain any
- // pointers we need to retag, so we can stop recursion early.
- // This optimization is crucial for ZSTs, because they can contain way more fields
- // than we can ever visit.
- if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
- return Ok(());
- }
-
- // Check the type of this value to see what to do with it (retag, or recurse).
- match place.layout.ty.kind() {
- ty::Ref(_, _, mutbl) => {
- let ref_kind = match mutbl {
- Mutability::Mut =>
- RefKind::Unique { two_phase: self.kind == RetagKind::TwoPhase },
- Mutability::Not => RefKind::Shared,
- };
- self.retag_place(
- place,
- ref_kind,
- self.retag_cause,
- /*protector*/
- (self.kind == RetagKind::FnEntry)
- .then_some(ProtectorKind::StrongProtector),
- )?;
- }
- ty::RawPtr(tym) => {
- // We definitely do *not* want to recurse into raw pointers -- wide raw
- // pointers have fields, and for dyn Trait pointees those can have reference
- // type!
- if self.kind == RetagKind::Raw {
- // Raw pointers need to be enabled.
- self.retag_place(
- place,
- RefKind::Raw { mutable: tym.mutbl == Mutability::Mut },
- self.retag_cause,
- /*protector*/ None,
- )?;
- }
- }
- _ if place.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_box()) => {
- // Recurse for boxes, they require some tricky handling and will end up in `visit_box` above.
- // (Yes this means we technically also recursively retag the allocator itself
- // even if field retagging is not enabled. *shrug*)
- self.walk_value(place)?;
- }
- _ => {
- // Not a reference/pointer/box. Only recurse if configured appropriately.
- let recurse = match self.retag_fields {
- RetagFields::No => false,
- RetagFields::Yes => true,
- RetagFields::OnlyScalar => {
- // Matching `ArgAbi::new` at the time of writing, only fields of
- // `Scalar` and `ScalarPair` ABI are considered.
- matches!(place.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..))
- }
- };
- if recurse {
- self.walk_value(place)?;
- }
- }
- }
-
- Ok(())
- }
- }
- }
-
- /// After a stack frame got pushed, retag the return place so that we are sure
- /// it does not alias with anything.
- ///
- /// This is a HACK because there is nothing in MIR that would make the retag
- /// explicit. Also see <https://github.com/rust-lang/rust/issues/71117>.
- fn retag_return_place(&mut self) -> InterpResult<'tcx> {
- let this = self.eval_context_mut();
- let return_place = &this.frame().return_place;
- if return_place.layout.is_zst() {
- // There may not be any memory here, nothing to do.
- return Ok(());
- }
- // We need this to be in-memory to use tagged pointers.
- let return_place = this.force_allocation(&return_place.clone())?;
-
- // We have to turn the place into a pointer to use the existing code.
- // (The pointer type does not matter, so we use a raw pointer.)
- let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?;
- let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
- // Reborrow it. With protection! That is part of the point.
- let val = this.retag_reference(
- &val,
- RefKind::Unique { two_phase: false },
- RetagCause::FnReturn,
- /*protector*/ Some(ProtectorKind::StrongProtector),
- )?;
- // And use reborrowed pointer for return place.
- let return_place = this.ref_to_mplace(&val)?;
- this.frame_mut().return_place = return_place.into();
-
- Ok(())
- }
-
- /// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
- fn expose_tag(&mut self, alloc_id: AllocId, tag: SbTag) -> InterpResult<'tcx> {
- let this = self.eval_context_mut();
-
- // Function pointers and dead objects don't have an alloc_extra so we ignore them.
- // This is okay because accessing them is UB anyway, no need for any Stacked Borrows checks.
- // NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
- let (_size, _align, kind) = this.get_alloc_info(alloc_id);
- match kind {
- AllocKind::LiveData => {
- // This should have alloc_extra data, but `get_alloc_extra` can still fail
- // if converting this alloc_id from a global to a local one
- // uncovers a non-supported `extern static`.
- let alloc_extra = this.get_alloc_extra(alloc_id)?;
- trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
- alloc_extra.stacked_borrows.as_ref().unwrap().borrow_mut().exposed_tags.insert(tag);
- }
- AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
- // No stacked borrows on these allocations.
- }
- }
- Ok(())
- }
-
- fn print_stacks(&mut self, alloc_id: AllocId) -> InterpResult<'tcx> {
- let this = self.eval_context_mut();
- let alloc_extra = this.get_alloc_extra(alloc_id)?;
- let stacks = alloc_extra.stacked_borrows.as_ref().unwrap().borrow();
- for (range, stack) in stacks.stacks.iter_all() {
- print!("{range:?}: [");
- if let Some(bottom) = stack.unknown_bottom() {
- print!(" unknown-bottom(..{bottom:?})");
- }
- for i in 0..stack.len() {
- let item = stack.get(i).unwrap();
- print!(" {:?}{:?}", item.perm(), item.tag());
- }
- println!(" ]");
- }
- Ok(())
- }
-}
+++ /dev/null
-#[cfg(feature = "stack-cache")]
-use std::ops::Range;
-
-use rustc_data_structures::fx::FxHashSet;
-
-use crate::stacked_borrows::{AccessKind, Item, Permission, SbTag};
-use crate::ProvenanceExtra;
-
-/// Exactly what cache size we should use is a difficult tradeoff. There will always be some
-/// workload which has a `SbTag` working set which exceeds the size of the cache, and ends up
-/// falling back to linear searches of the borrow stack very often.
-/// The cost of making this value too large is that the loop in `Stack::insert` which ensures the
-/// entries in the cache stay correct after an insert becomes expensive.
-#[cfg(feature = "stack-cache")]
-const CACHE_LEN: usize = 32;
-
-/// Extra per-location state.
-#[derive(Clone, Debug)]
-pub struct Stack {
- /// Used *mostly* as a stack; never empty.
- /// Invariants:
- /// * Above a `SharedReadOnly` there can only be more `SharedReadOnly`.
- /// * Except for `Untagged`, no tag occurs in the stack more than once.
- borrows: Vec<Item>,
- /// If this is `Some(id)`, then the actual current stack is unknown. This can happen when
- /// wildcard pointers are used to access this location. What we do know is that `borrows` are at
- /// the top of the stack, and below it are arbitrarily many items whose `tag` is strictly less
- /// than `id`.
- /// When the bottom is unknown, `borrows` always has a `SharedReadOnly` or `Unique` at the bottom;
- /// we never have the unknown-to-known boundary in an SRW group.
- unknown_bottom: Option<SbTag>,
-
- /// A small LRU cache of searches of the borrow stack.
- #[cfg(feature = "stack-cache")]
- cache: StackCache,
- /// On a read, we need to disable all `Unique` above the granting item. We can avoid most of
- /// this scan by keeping track of the region of the borrow stack that may contain `Unique`s.
- #[cfg(feature = "stack-cache")]
- unique_range: Range<usize>,
-}
-
-impl Stack {
- pub fn retain(&mut self, tags: &FxHashSet<SbTag>) {
- let mut first_removed = None;
-
- // We never consider removing the bottom-most tag. For stacks without an unknown
- // bottom this preserves the base tag.
- // Note that the algorithm below is based on considering the tag at read_idx - 1,
- // so precisely considering the tag at index 0 for removal when we have an unknown
- // bottom would complicate the implementation. The simplification of not considering
- // it does not have a significant impact on the degree to which the GC mititages
- // memory growth.
- let mut read_idx = 1;
- let mut write_idx = read_idx;
- while read_idx < self.borrows.len() {
- let left = self.borrows[read_idx - 1];
- let this = self.borrows[read_idx];
- let should_keep = match this.perm() {
- // SharedReadWrite is the simplest case, if it's unreachable we can just remove it.
- Permission::SharedReadWrite => tags.contains(&this.tag()),
- // Only retain a Disabled tag if it is terminating a SharedReadWrite block.
- Permission::Disabled => left.perm() == Permission::SharedReadWrite,
- // Unique and SharedReadOnly can terminate a SharedReadWrite block, so only remove
- // them if they are both unreachable and not directly after a SharedReadWrite.
- Permission::Unique | Permission::SharedReadOnly =>
- left.perm() == Permission::SharedReadWrite || tags.contains(&this.tag()),
- };
-
- if should_keep {
- if read_idx != write_idx {
- self.borrows[write_idx] = self.borrows[read_idx];
- }
- write_idx += 1;
- } else if first_removed.is_none() {
- first_removed = Some(read_idx);
- }
-
- read_idx += 1;
- }
- self.borrows.truncate(write_idx);
-
- #[cfg(not(feature = "stack-cache"))]
- drop(first_removed); // This is only needed for the stack-cache
-
- #[cfg(feature = "stack-cache")]
- if let Some(first_removed) = first_removed {
- // Either end of unique_range may have shifted, all we really know is that we can't
- // have introduced a new Unique.
- if !self.unique_range.is_empty() {
- self.unique_range = 0..self.len();
- }
-
- // Replace any Items which have been collected with the base item, a known-good value.
- for i in 0..CACHE_LEN {
- if self.cache.idx[i] >= first_removed {
- self.cache.items[i] = self.borrows[0];
- self.cache.idx[i] = 0;
- }
- }
- }
- }
-}
-
-/// A very small cache of searches of a borrow stack, mapping `Item`s to their position in said stack.
-///
-/// It may seem like maintaining this cache is a waste for small stacks, but
-/// (a) iterating over small fixed-size arrays is super fast, and (b) empirically this helps *a lot*,
-/// probably because runtime is dominated by large stacks.
-#[cfg(feature = "stack-cache")]
-#[derive(Clone, Debug)]
-struct StackCache {
- items: [Item; CACHE_LEN], // Hot in find_granting
- idx: [usize; CACHE_LEN], // Hot in grant
-}
-
-#[cfg(feature = "stack-cache")]
-impl StackCache {
- /// When a tag is used, we call this function to add or refresh it in the cache.
- ///
- /// We use the position in the cache to represent how recently a tag was used; the first position
- /// is the most recently used tag. So an add shifts every element towards the end, and inserts
- /// the new element at the start. We lose the last element.
- /// This strategy is effective at keeping the most-accessed items in the cache, but it costs a
- /// linear shift across the entire cache when we add a new tag.
- fn add(&mut self, idx: usize, item: Item) {
- self.items.copy_within(0..CACHE_LEN - 1, 1);
- self.items[0] = item;
- self.idx.copy_within(0..CACHE_LEN - 1, 1);
- self.idx[0] = idx;
- }
-}
-
-impl PartialEq for Stack {
- fn eq(&self, other: &Self) -> bool {
- // All the semantics of Stack are in self.borrows, everything else is caching
- self.borrows == other.borrows
- }
-}
-
-impl Eq for Stack {}
-
-impl<'tcx> Stack {
- /// Panics if any of the caching mechanisms have broken,
- /// - The StackCache indices don't refer to the parallel items,
- /// - There are no Unique items outside of first_unique..last_unique
- #[cfg(all(feature = "stack-cache", debug_assertions))]
- fn verify_cache_consistency(&self) {
- // Only a full cache needs to be valid. Also see the comments in find_granting_cache
- // and set_unknown_bottom.
- if self.borrows.len() >= CACHE_LEN {
- for (tag, stack_idx) in self.cache.items.iter().zip(self.cache.idx.iter()) {
- assert_eq!(self.borrows[*stack_idx], *tag);
- }
- }
-
- // Check that all Unique items fall within unique_range.
- for (idx, item) in self.borrows.iter().enumerate() {
- if item.perm() == Permission::Unique {
- assert!(
- self.unique_range.contains(&idx),
- "{:?} {:?}",
- self.unique_range,
- self.borrows
- );
- }
- }
-
- // Check that the unique_range is a valid index into the borrow stack.
- // This asserts that the unique_range's start <= end.
- let _uniques = &self.borrows[self.unique_range.clone()];
-
- // We cannot assert that the unique range is precise.
- // Both ends may shift around when `Stack::retain` is called. Additionally,
- // when we pop items within the unique range, setting the end of the range precisely
- // requires doing a linear search of the borrow stack, which is exactly the kind of
- // operation that all this caching exists to avoid.
- }
-
- /// Find the item granting the given kind of access to the given tag, and return where
- /// it is on the stack. For wildcard tags, the given index is approximate, but if *no*
- /// index is given it means the match was *not* in the known part of the stack.
- /// `Ok(None)` indicates it matched the "unknown" part of the stack.
- /// `Err` indicates it was not found.
- pub(super) fn find_granting(
- &mut self,
- access: AccessKind,
- tag: ProvenanceExtra,
- exposed_tags: &FxHashSet<SbTag>,
- ) -> Result<Option<usize>, ()> {
- #[cfg(all(feature = "stack-cache", debug_assertions))]
- self.verify_cache_consistency();
-
- let ProvenanceExtra::Concrete(tag) = tag else {
- // Handle the wildcard case.
- // Go search the stack for an exposed tag.
- if let Some(idx) =
- self.borrows
- .iter()
- .enumerate() // we also need to know *where* in the stack
- .rev() // search top-to-bottom
- .find_map(|(idx, item)| {
- // If the item fits and *might* be this wildcard, use it.
- if item.perm().grants(access) && exposed_tags.contains(&item.tag()) {
- Some(idx)
- } else {
- None
- }
- })
- {
- return Ok(Some(idx));
- }
- // If we couldn't find it in the stack, check the unknown bottom.
- return if self.unknown_bottom.is_some() { Ok(None) } else { Err(()) };
- };
-
- if let Some(idx) = self.find_granting_tagged(access, tag) {
- return Ok(Some(idx));
- }
-
- // Couldn't find it in the stack; but if there is an unknown bottom it might be there.
- let found = self.unknown_bottom.is_some_and(|unknown_limit| {
- tag.0 < unknown_limit.0 // unknown_limit is an upper bound for what can be in the unknown bottom.
- });
- if found { Ok(None) } else { Err(()) }
- }
-
- fn find_granting_tagged(&mut self, access: AccessKind, tag: SbTag) -> Option<usize> {
- #[cfg(feature = "stack-cache")]
- if let Some(idx) = self.find_granting_cache(access, tag) {
- return Some(idx);
- }
-
- // If we didn't find the tag in the cache, fall back to a linear search of the
- // whole stack, and add the tag to the cache.
- for (stack_idx, item) in self.borrows.iter().enumerate().rev() {
- if tag == item.tag() && item.perm().grants(access) {
- #[cfg(feature = "stack-cache")]
- self.cache.add(stack_idx, *item);
- return Some(stack_idx);
- }
- }
- None
- }
-
- #[cfg(feature = "stack-cache")]
- fn find_granting_cache(&mut self, access: AccessKind, tag: SbTag) -> Option<usize> {
- // This looks like a common-sense optimization; we're going to do a linear search of the
- // cache or the borrow stack to scan the shorter of the two. This optimization is miniscule
- // and this check actually ensures we do not access an invalid cache.
- // When a stack is created and when items are removed from the top of the borrow stack, we
- // need some valid value to populate the cache. In both cases, we try to use the bottom
- // item. But when the stack is cleared in `set_unknown_bottom` there is nothing we could
- // place in the cache that is correct. But due to the way we populate the cache in
- // `StackCache::add`, we know that when the borrow stack has grown larger than the cache,
- // every slot in the cache is valid.
- if self.borrows.len() <= CACHE_LEN {
- return None;
- }
- // Search the cache for the tag we're looking up
- let cache_idx = self.cache.items.iter().position(|t| t.tag() == tag)?;
- let stack_idx = self.cache.idx[cache_idx];
- // If we found the tag, look up its position in the stack to see if it grants
- // the required permission
- if self.cache.items[cache_idx].perm().grants(access) {
- // If it does, and it's not already in the most-recently-used position, re-insert it at
- // the most-recently-used position. This technically reduces the efficiency of the
- // cache by duplicating elements, but current benchmarks do not seem to benefit from
- // avoiding this duplication.
- // But if the tag is in position 1, avoiding the duplicating add is trivial.
- // If it does, and it's not already in the most-recently-used position, move it there.
- // Except if the tag is in position 1, this is equivalent to just a swap, so do that.
- if cache_idx == 1 {
- self.cache.items.swap(0, 1);
- self.cache.idx.swap(0, 1);
- } else if cache_idx > 1 {
- self.cache.add(stack_idx, self.cache.items[cache_idx]);
- }
- Some(stack_idx)
- } else {
- // Tag is in the cache, but it doesn't grant the required permission
- None
- }
- }
-
- pub fn insert(&mut self, new_idx: usize, new: Item) {
- self.borrows.insert(new_idx, new);
-
- #[cfg(feature = "stack-cache")]
- self.insert_cache(new_idx, new);
- }
-
- #[cfg(feature = "stack-cache")]
- fn insert_cache(&mut self, new_idx: usize, new: Item) {
- // Adjust the possibly-unique range if an insert occurs before or within it
- if self.unique_range.start >= new_idx {
- self.unique_range.start += 1;
- }
- if self.unique_range.end >= new_idx {
- self.unique_range.end += 1;
- }
- if new.perm() == Permission::Unique {
- // If this is the only Unique, set the range to contain just the new item.
- if self.unique_range.is_empty() {
- self.unique_range = new_idx..new_idx + 1;
- } else {
- // We already have other Unique items, expand the range to include the new item
- self.unique_range.start = self.unique_range.start.min(new_idx);
- self.unique_range.end = self.unique_range.end.max(new_idx + 1);
- }
- }
-
- // The above insert changes the meaning of every index in the cache >= new_idx, so now
- // we need to find every one of those indexes and increment it.
- // But if the insert is at the end (equivalent to a push), we can skip this step because
- // it didn't change the position of any other items.
- if new_idx != self.borrows.len() - 1 {
- for idx in &mut self.cache.idx {
- if *idx >= new_idx {
- *idx += 1;
- }
- }
- }
-
- // This primes the cache for the next access, which is almost always the just-added tag.
- self.cache.add(new_idx, new);
-
- #[cfg(debug_assertions)]
- self.verify_cache_consistency();
- }
-
- /// Construct a new `Stack` using the passed `Item` as the base tag.
- pub fn new(item: Item) -> Self {
- Stack {
- borrows: vec![item],
- unknown_bottom: None,
- #[cfg(feature = "stack-cache")]
- cache: StackCache { idx: [0; CACHE_LEN], items: [item; CACHE_LEN] },
- #[cfg(feature = "stack-cache")]
- unique_range: if item.perm() == Permission::Unique { 0..1 } else { 0..0 },
- }
- }
-
- pub fn get(&self, idx: usize) -> Option<Item> {
- self.borrows.get(idx).cloned()
- }
-
- #[allow(clippy::len_without_is_empty)] // Stacks are never empty
- pub fn len(&self) -> usize {
- self.borrows.len()
- }
-
- pub fn unknown_bottom(&self) -> Option<SbTag> {
- self.unknown_bottom
- }
-
- pub fn set_unknown_bottom(&mut self, tag: SbTag) {
- // We clear the borrow stack but the lookup cache doesn't support clearing per se. Instead,
- // there is a check explained in `find_granting_cache` which protects against accessing the
- // cache when it has been cleared and not yet refilled.
- self.borrows.clear();
- self.unknown_bottom = Some(tag);
- #[cfg(feature = "stack-cache")]
- {
- self.unique_range = 0..0;
- }
- }
-
- /// Find all `Unique` elements in this borrow stack above `granting_idx`, pass a copy of them
- /// to the `visitor`, then set their `Permission` to `Disabled`.
- pub fn disable_uniques_starting_at(
- &mut self,
- disable_start: usize,
- mut visitor: impl FnMut(Item) -> crate::InterpResult<'tcx>,
- ) -> crate::InterpResult<'tcx> {
- #[cfg(feature = "stack-cache")]
- let unique_range = self.unique_range.clone();
- #[cfg(not(feature = "stack-cache"))]
- let unique_range = 0..self.len();
-
- if disable_start <= unique_range.end {
- let lower = unique_range.start.max(disable_start);
- let upper = unique_range.end;
- for item in &mut self.borrows[lower..upper] {
- if item.perm() == Permission::Unique {
- log::trace!("access: disabling item {:?}", item);
- visitor(*item)?;
- item.set_permission(Permission::Disabled);
- // Also update all copies of this item in the cache.
- #[cfg(feature = "stack-cache")]
- for it in &mut self.cache.items {
- if it.tag() == item.tag() {
- it.set_permission(Permission::Disabled);
- }
- }
- }
- }
- }
-
- #[cfg(feature = "stack-cache")]
- if disable_start <= self.unique_range.start {
- // We disabled all Unique items
- self.unique_range.start = 0;
- self.unique_range.end = 0;
- } else {
- // Truncate the range to only include items up to the index that we started disabling
- // at.
- self.unique_range.end = self.unique_range.end.min(disable_start);
- }
-
- #[cfg(all(feature = "stack-cache", debug_assertions))]
- self.verify_cache_consistency();
-
- Ok(())
- }
-
- /// Produces an iterator which iterates over `range` in reverse, and when dropped removes that
- /// range of `Item`s from this `Stack`.
- pub fn pop_items_after<V: FnMut(Item) -> crate::InterpResult<'tcx>>(
- &mut self,
- start: usize,
- mut visitor: V,
- ) -> crate::InterpResult<'tcx> {
- while self.borrows.len() > start {
- let item = self.borrows.pop().unwrap();
- visitor(item)?;
- }
-
- #[cfg(feature = "stack-cache")]
- if !self.borrows.is_empty() {
- // After we remove from the borrow stack, every aspect of our caching may be invalid, but it is
- // also possible that the whole cache is still valid. So we call this method to repair what
- // aspects of the cache are now invalid, instead of resetting the whole thing to a trivially
- // valid default state.
- let base_tag = self.borrows[0];
- let mut removed = 0;
- let mut cursor = 0;
- // Remove invalid entries from the cache by rotating them to the end of the cache, then
- // keep track of how many invalid elements there are and overwrite them with the base tag.
- // The base tag here serves as a harmless default value.
- for _ in 0..CACHE_LEN - 1 {
- if self.cache.idx[cursor] >= start {
- self.cache.idx[cursor..CACHE_LEN - removed].rotate_left(1);
- self.cache.items[cursor..CACHE_LEN - removed].rotate_left(1);
- removed += 1;
- } else {
- cursor += 1;
- }
- }
- for i in CACHE_LEN - removed - 1..CACHE_LEN {
- self.cache.idx[i] = 0;
- self.cache.items[i] = base_tag;
- }
-
- if start <= self.unique_range.start {
- // We removed all the Unique items
- self.unique_range = 0..0;
- } else {
- // Ensure the range doesn't extend past the new top of the stack
- self.unique_range.end = self.unique_range.end.min(start);
- }
- } else {
- self.unique_range = 0..0;
- }
-
- #[cfg(all(feature = "stack-cache", debug_assertions))]
- self.verify_cache_consistency();
- Ok(())
- }
-}
use crate::*;
pub trait VisitTags {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag));
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag));
}
impl<T: VisitTags> VisitTags for Option<T> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
if let Some(x) = self {
x.visit_tags(visit);
}
}
impl<T: VisitTags> VisitTags for std::cell::RefCell<T> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
self.borrow().visit_tags(visit)
}
}
-impl VisitTags for SbTag {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+impl VisitTags for BorTag {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
visit(*self)
}
}
impl VisitTags for Provenance {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
- if let Provenance::Concrete { sb, .. } = self {
- visit(*sb);
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
+ if let Provenance::Concrete { tag, .. } = self {
+ visit(*tag);
}
}
}
impl VisitTags for Pointer<Provenance> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let (prov, _offset) = self.into_parts();
prov.visit_tags(visit);
}
}
impl VisitTags for Pointer<Option<Provenance>> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let (prov, _offset) = self.into_parts();
prov.visit_tags(visit);
}
}
impl VisitTags for Scalar<Provenance> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
match self {
Scalar::Ptr(ptr, _) => ptr.visit_tags(visit),
Scalar::Int(_) => (),
}
impl VisitTags for Immediate<Provenance> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
match self {
Immediate::Scalar(s) => {
s.visit_tags(visit);
}
impl VisitTags for MemPlaceMeta<Provenance> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
match self {
MemPlaceMeta::Meta(m) => m.visit_tags(visit),
MemPlaceMeta::None => {}
}
impl VisitTags for MemPlace<Provenance> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
let MemPlace { ptr, meta } = self;
ptr.visit_tags(visit);
meta.visit_tags(visit);
}
impl VisitTags for MPlaceTy<'_, Provenance> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
(**self).visit_tags(visit)
}
}
impl VisitTags for Place<Provenance> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
match self {
Place::Ptr(p) => p.visit_tags(visit),
Place::Local { .. } => {
}
impl VisitTags for PlaceTy<'_, Provenance> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
(**self).visit_tags(visit)
}
}
impl VisitTags for Operand<Provenance> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
match self {
Operand::Immediate(imm) => {
imm.visit_tags(visit);
}
impl VisitTags for Allocation<Provenance, AllocExtra> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
for prov in self.provenance().provenances() {
prov.visit_tags(visit);
}
}
impl VisitTags for crate::MiriInterpCx<'_, '_> {
- fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
+ fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
// Memory.
self.memory.alloc_map().iter(|it| {
for (_id, (_kind, alloc)) in it {
fn garbage_collect_tags(&mut self) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
// No reason to do anything at all if stacked borrows is off.
- if this.machine.stacked_borrows.is_none() {
+ if this.machine.borrow_tracker.is_none() {
return Ok(());
}
Ok(())
}
- fn remove_unreachable_tags(&mut self, tags: FxHashSet<SbTag>) {
+ fn remove_unreachable_tags(&mut self, tags: FxHashSet<BorTag>) {
let this = self.eval_context_mut();
this.memory.alloc_map().iter(|it| {
for (_id, (_kind, alloc)) in it {
- alloc
- .extra
- .stacked_borrows
- .as_ref()
- .unwrap()
- .borrow_mut()
- .remove_unreachable_tags(&tags);
+ if let Some(bt) = &alloc.extra.borrow_tracker {
+ bt.remove_unreachable_tags(&tags);
+ }
}
});
}
| |_^ the program aborted execution
|
= note: inside `panic_abort` at $DIR/abort-terminator.rs:LL:CC
-note: inside `main` at $DIR/abort-terminator.rs:LL:CC
+note: inside `main`
--> $DIR/abort-terminator.rs:LL:CC
|
LL | panic_abort();
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
-note: inside `main` at $DIR/deallocate-bad-alignment.rs:LL:CC
+note: inside `main`
--> $DIR/deallocate-bad-alignment.rs:LL:CC
|
LL | dealloc(x, Layout::from_size_align_unchecked(1, 2));
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
-note: inside `main` at $DIR/deallocate-bad-size.rs:LL:CC
+note: inside `main`
--> $DIR/deallocate-bad-size.rs:LL:CC
|
LL | dealloc(x, Layout::from_size_align_unchecked(2, 1));
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
-note: inside `main` at $DIR/deallocate-twice.rs:LL:CC
+note: inside `main`
--> $DIR/deallocate-twice.rs:LL:CC
|
LL | dealloc(x, Layout::from_size_align_unchecked(1, 1));
= note: BACKTRACE:
= note: inside `std::sys::PLATFORM::alloc::<impl std::alloc::GlobalAlloc for std::alloc::System>::dealloc` at RUSTLIB/std/src/sys/PLATFORM/alloc.rs:LL:CC
= note: inside `<std::alloc::System as std::alloc::Allocator>::deallocate` at RUSTLIB/std/src/alloc.rs:LL:CC
-note: inside `main` at $DIR/global_system_mixup.rs:LL:CC
+note: inside `main`
--> $DIR/global_system_mixup.rs:LL:CC
|
LL | System.deallocate(ptr, l);
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `std::alloc::realloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
-note: inside `main` at $DIR/reallocate-bad-size.rs:LL:CC
+note: inside `main`
--> $DIR/reallocate-bad-size.rs:LL:CC
|
LL | let _y = realloc(x, Layout::from_size_align_unchecked(2, 1), 1);
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `std::alloc::realloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
-note: inside `main` at $DIR/reallocate-dangling.rs:LL:CC
+note: inside `main`
--> $DIR/reallocate-dangling.rs:LL:CC
|
LL | let _z = realloc(x, Layout::from_size_align_unchecked(1, 1), 1);
= note: inside `alloc::alloc::box_free::<i32, std::alloc::Global>` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `std::ptr::drop_in_place::<std::boxed::Box<i32>> - shim(Some(std::boxed::Box<i32>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
= note: inside `std::mem::drop::<std::boxed::Box<i32>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
-note: inside `main` at $DIR/stack_free.rs:LL:CC
+note: inside `main`
--> $DIR/stack_free.rs:LL:CC
|
LL | drop(bad_box);
| ^^^
= note: BACKTRACE:
= note: inside `helper` at $DIR/box-cell-alias.rs:LL:CC
-note: inside `main` at $DIR/box-cell-alias.rs:LL:CC
+note: inside `main`
--> $DIR/box-cell-alias.rs:LL:CC
|
LL | let res = helper(val, ptr);
= note: inside `std::sys::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/PLATFORM/thread.rs:LL:CC
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
-note: inside `main` at $DIR/windows_join_detached.rs:LL:CC
+note: inside `main`
--> $DIR/windows_join_detached.rs:LL:CC
|
LL | thread.join().unwrap();
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
= note: BACKTRACE:
-note: inside `main` at $DIR/tokio_mvp.rs:LL:CC
+note: inside `main`
--> $DIR/tokio_mvp.rs:LL:CC
|
LL | #[tokio::main]
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `evil` at $DIR/storage_dead_dangling.rs:LL:CC
-note: inside `main` at $DIR/storage_dead_dangling.rs:LL:CC
+note: inside `main`
--> $DIR/storage_dead_dangling.rs:LL:CC
|
LL | evil();
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `race` at $DIR/stack_pop_race.rs:LL:CC
-note: inside `main` at $DIR/stack_pop_race.rs:LL:CC
+note: inside `main`
--> $DIR/stack_pop_race.rs:LL:CC
|
LL | race(0);
#[cfg(fn_ptr)]
unsafe {
std::mem::transmute::<unsafe fn(), unsafe extern "C" fn()>(foo)();
- //[fn_ptr]~^ ERROR: calling a function with calling convention Rust using calling convention C
+ //~[fn_ptr]^ ERROR: calling a function with calling convention Rust using calling convention C
}
// `Instance` caching should not suppress ABI check.
}
unsafe {
foo();
- //[no_cache]~^ ERROR: calling a function with calling convention Rust using calling convention C
- //[cache]~| ERROR: calling a function with calling convention Rust using calling convention C
+ //~[no_cache]^ ERROR: calling a function with calling convention Rust using calling convention C
+ //~[cache]| ERROR: calling a function with calling convention Rust using calling convention C
}
}
}
| |_^ the program aborted execution
|
= note: inside `nounwind` at $DIR/exported_symbol_bad_unwind2.rs:LL:CC
-note: inside `main` at $DIR/exported_symbol_bad_unwind2.rs:LL:CC
+note: inside `main`
--> $DIR/exported_symbol_bad_unwind2.rs:LL:CC
|
LL | unsafe { nounwind() }
| |_^ the program aborted execution
|
= note: inside `nounwind` at $DIR/exported_symbol_bad_unwind2.rs:LL:CC
-note: inside `main` at $DIR/exported_symbol_bad_unwind2.rs:LL:CC
+note: inside `main`
--> $DIR/exported_symbol_bad_unwind2.rs:LL:CC
|
LL | unsafe { nounwind() }
#[cfg_attr(any(definition, both), rustc_nounwind)]
#[no_mangle]
extern "C-unwind" fn nounwind() {
- //[definition]~^ ERROR: abnormal termination: the program aborted execution
- //[both]~^^ ERROR: abnormal termination: the program aborted execution
+ //~[definition]^ ERROR: abnormal termination: the program aborted execution
+ //~[both]^^ ERROR: abnormal termination: the program aborted execution
panic!();
}
fn nounwind();
}
unsafe { nounwind() }
- //[extern_block]~^ ERROR: unwinding past a stack frame that does not allow unwinding
+ //~[extern_block]^ ERROR: unwinding past a stack frame that does not allow unwinding
}
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside closure at $DIR/generator-pinned-moved.rs:LL:CC
-note: inside `<GeneratorIteratorAdapter<[static generator@$DIR/generator-pinned-moved.rs:LL:CC]> as std::iter::Iterator>::next` at $DIR/generator-pinned-moved.rs:LL:CC
+note: inside `<GeneratorIteratorAdapter<[static generator@$DIR/generator-pinned-moved.rs:LL:CC]> as std::iter::Iterator>::next`
--> $DIR/generator-pinned-moved.rs:LL:CC
|
LL | match me.resume(()) {
| ^^^^^^^^^^^^^
= note: inside `<std::boxed::Box<GeneratorIteratorAdapter<[static generator@$DIR/generator-pinned-moved.rs:LL:CC]>> as std::iter::Iterator>::next` at RUSTLIB/alloc/src/boxed.rs:LL:CC
-note: inside `main` at $DIR/generator-pinned-moved.rs:LL:CC
+note: inside `main`
--> $DIR/generator-pinned-moved.rs:LL:CC
|
LL | generator_iterator_2.next(); // and use moved value
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `std::simd::Simd::<f32, 2>::to_int_unchecked::<i32>` at RUSTLIB/core/src/../../portable-simd/crates/core_simd/src/vector.rs:LL:CC
-note: inside `main` at $DIR/simd-float-to-int.rs:LL:CC
+note: inside `main`
--> $DIR/simd-float-to-int.rs:LL:CC
|
LL | let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked();
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `std::simd::Simd::<i8, 4>::gather_select_unchecked` at RUSTLIB/core/src/../../portable-simd/crates/core_simd/src/vector.rs:LL:CC
-note: inside `main` at $DIR/simd-gather.rs:LL:CC
+note: inside `main`
--> $DIR/simd-gather.rs:LL:CC
|
LL | let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0));
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `std::simd::Simd::<i8, 4>::scatter_select_unchecked` at RUSTLIB/core/src/../../portable-simd/crates/core_simd/src/vector.rs:LL:CC
-note: inside `main` at $DIR/simd-scatter.rs:LL:CC
+note: inside `main`
--> $DIR/simd-scatter.rs:LL:CC
|
LL | / Simd::from_array([-27, 82, -41, 124]).scatter_select_unchecked(
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `FunnyPointer::from_data_ptr` at $DIR/issue-miri-1112.rs:LL:CC
-note: inside `main` at $DIR/issue-miri-1112.rs:LL:CC
+note: inside `main`
--> $DIR/issue-miri-1112.rs:LL:CC
|
LL | let _raw: &FunnyPointer = FunnyPointer::from_data_ptr(&hello, &meta as *const _);
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `m::f` at $DIR/never_transmute_void.rs:LL:CC
-note: inside `main` at $DIR/never_transmute_void.rs:LL:CC
+note: inside `main`
--> $DIR/never_transmute_void.rs:LL:CC
|
LL | m::f(v);
= note: inside `std::panicking::r#try::do_call::<[closure@$DIR/bad_unwind.rs:LL:CC], ()>` at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside `std::panicking::r#try::<(), [closure@$DIR/bad_unwind.rs:LL:CC]>` at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside `std::panic::catch_unwind::<[closure@$DIR/bad_unwind.rs:LL:CC], ()>` at RUSTLIB/std/src/panic.rs:LL:CC
-note: inside `main` at $DIR/bad_unwind.rs:LL:CC
+note: inside `main`
--> $DIR/bad_unwind.rs:LL:CC
|
LL | std::panic::catch_unwind(|| unwind()).unwrap_err();
= note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::rt::begin_panic<&str>::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC
-note: inside `<Foo as std::ops::Drop>::drop` at RUSTLIB/std/src/panic.rs:LL:CC
+note: inside `<Foo as std::ops::Drop>::drop`
--> $DIR/double_panic.rs:LL:CC
|
LL | panic!("second");
| ^
= note: inside `std::ptr::drop_in_place::<Foo> - shim(Some(Foo))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
-note: inside `main` at $DIR/double_panic.rs:LL:CC
+note: inside `main`
--> $DIR/double_panic.rs:LL:CC
|
LL | }
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution
|
= note: inside `panic_handler` at $DIR/no_std.rs:LL:CC
-note: inside `start` at RUSTLIB/core/src/panic.rs:LL:CC
+note: inside `start`
--> $DIR/no_std.rs:LL:CC
|
LL | panic!("blarg I am dead")
= note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::rt::begin_panic<&str>::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC
-note: inside `main` at RUSTLIB/std/src/panic.rs:LL:CC
+note: inside `main`
--> $DIR/panic_abort1.rs:LL:CC
|
LL | std::panic!("panicking from libstd");
= note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC
= note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC
-note: inside `main` at RUSTLIB/std/src/panic.rs:LL:CC
+note: inside `main`
--> $DIR/panic_abort2.rs:LL:CC
|
LL | std::panic!("{}-panicking from libstd", 42);
= note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC
= note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC
-note: inside `main` at RUSTLIB/core/src/panic.rs:LL:CC
+note: inside `main`
--> $DIR/panic_abort3.rs:LL:CC
|
LL | core::panic!("panicking from libcore");
= note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
= note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC
= note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC
-note: inside `main` at RUSTLIB/core/src/panic.rs:LL:CC
+note: inside `main`
--> $DIR/panic_abort4.rs:LL:CC
|
LL | core::panic!("{}-panicking from libcore", 42);
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `deref` at $DIR/provenance_transmute.rs:LL:CC
-note: inside `main` at $DIR/provenance_transmute.rs:LL:CC
+note: inside `main`
--> $DIR/provenance_transmute.rs:LL:CC
|
LL | deref(ptr1, ptr2.with_addr(ptr1.addr()));
= note: inside `std::fs::OpenOptions::_open` at RUSTLIB/std/src/fs.rs:LL:CC
= note: inside `std::fs::OpenOptions::open::<&std::path::Path>` at RUSTLIB/std/src/fs.rs:LL:CC
= note: inside `std::fs::File::open::<&str>` at RUSTLIB/std/src/fs.rs:LL:CC
-note: inside `main` at $DIR/isolated_file.rs:LL:CC
+note: inside `main`
--> $DIR/isolated_file.rs:LL:CC
|
LL | let _file = std::fs::File::open("file.txt").unwrap();
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `test_mkstemp_immutable_arg` at $DIR/mkstemp_immutable_arg.rs:LL:CC
-note: inside `main` at $DIR/mkstemp_immutable_arg.rs:LL:CC
+note: inside `main`
--> $DIR/mkstemp_immutable_arg.rs:LL:CC
|
LL | test_mkstemp_immutable_arg();
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `test_file_open_missing_needed_mode` at $DIR/unix_open_missing_required_mode.rs:LL:CC
-note: inside `main` at $DIR/unix_open_missing_required_mode.rs:LL:CC
+note: inside `main`
--> $DIR/unix_open_missing_required_mode.rs:LL:CC
|
LL | test_file_open_missing_needed_mode();
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `test_cpp20_rwc_syncs` at $DIR/cpp20_rwc_syncs.rs:LL:CC
-note: inside `main` at $DIR/cpp20_rwc_syncs.rs:LL:CC
+note: inside `main`
--> $DIR/cpp20_rwc_syncs.rs:LL:CC
|
LL | test_cpp20_rwc_syncs();
| ^^
= note: BACKTRACE:
= note: inside `safe` at $DIR/aliasing_mut1.rs:LL:CC
-note: inside `main` at $DIR/aliasing_mut1.rs:LL:CC
+note: inside `main`
--> $DIR/aliasing_mut1.rs:LL:CC
|
LL | safe_raw(xraw, xraw);
| ^^
= note: BACKTRACE:
= note: inside `safe` at $DIR/aliasing_mut2.rs:LL:CC
-note: inside `main` at $DIR/aliasing_mut2.rs:LL:CC
+note: inside `main`
--> $DIR/aliasing_mut2.rs:LL:CC
|
LL | safe_raw(xshr, xraw);
| ^^^^^^^^^^^^^^^^^^^^
= note: BACKTRACE:
= note: inside `safe` at $DIR/aliasing_mut3.rs:LL:CC
-note: inside `main` at $DIR/aliasing_mut3.rs:LL:CC
+note: inside `main`
--> $DIR/aliasing_mut3.rs:LL:CC
|
LL | safe_raw(xraw, xshr);
| ^^
= note: BACKTRACE:
= note: inside `safe` at $DIR/aliasing_mut4.rs:LL:CC
-note: inside `main` at $DIR/aliasing_mut4.rs:LL:CC
+note: inside `main`
--> $DIR/aliasing_mut4.rs:LL:CC
|
LL | safe_raw(xshr, xraw as *mut _);
| ^^^^^^^^
= note: BACKTRACE:
= note: inside `unknown_code_2` at $DIR/box_exclusive_violation1.rs:LL:CC
-note: inside `demo_box_advanced_unique` at $DIR/box_exclusive_violation1.rs:LL:CC
+note: inside `demo_box_advanced_unique`
--> $DIR/box_exclusive_violation1.rs:LL:CC
|
LL | unknown_code_2();
| ^^^^^^^^^^^^^^^^
-note: inside `main` at $DIR/box_exclusive_violation1.rs:LL:CC
+note: inside `main`
--> $DIR/box_exclusive_violation1.rs:LL:CC
|
LL | demo_box_advanced_unique(Box::new(0));
| ^^^^^
= note: BACKTRACE:
= note: inside `test` at $DIR/box_noalias_violation.rs:LL:CC
-note: inside `main` at $DIR/box_noalias_violation.rs:LL:CC
+note: inside `main`
--> $DIR/box_noalias_violation.rs:LL:CC
|
LL | test(Box::from_raw(ptr), ptr);
= note: inside `alloc::alloc::box_free::<i32, std::alloc::Global>` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `std::ptr::drop_in_place::<std::boxed::Box<i32>> - shim(Some(std::boxed::Box<i32>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
= note: inside `std::mem::drop::<std::boxed::Box<i32>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
-note: inside closure at $DIR/deallocate_against_protector1.rs:LL:CC
+note: inside closure
--> $DIR/deallocate_against_protector1.rs:LL:CC
|
LL | drop(unsafe { Box::from_raw(raw) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: inside `<[closure@$DIR/deallocate_against_protector1.rs:LL:CC] as std::ops::FnOnce<(&mut i32,)>>::call_once - shim` at RUSTLIB/core/src/ops/function.rs:LL:CC
-note: inside `inner` at $DIR/deallocate_against_protector1.rs:LL:CC
+note: inside `inner`
--> $DIR/deallocate_against_protector1.rs:LL:CC
|
LL | f(x)
| ^^^^
-note: inside `main` at $DIR/deallocate_against_protector1.rs:LL:CC
+note: inside `main`
--> $DIR/deallocate_against_protector1.rs:LL:CC
|
LL | / inner(Box::leak(Box::new(0)), |x| {
= note: inside `alloc::alloc::box_free::<NotUnpin, std::alloc::Global>` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `std::ptr::drop_in_place::<std::boxed::Box<NotUnpin>> - shim(Some(std::boxed::Box<NotUnpin>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
= note: inside `std::mem::drop::<std::boxed::Box<NotUnpin>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
-note: inside closure at $DIR/deallocate_against_protector2.rs:LL:CC
+note: inside closure
--> $DIR/deallocate_against_protector2.rs:LL:CC
|
LL | drop(unsafe { Box::from_raw(raw) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: inside `<[closure@$DIR/deallocate_against_protector2.rs:LL:CC] as std::ops::FnOnce<(&mut NotUnpin,)>>::call_once - shim` at RUSTLIB/core/src/ops/function.rs:LL:CC
-note: inside `inner` at $DIR/deallocate_against_protector2.rs:LL:CC
+note: inside `inner`
--> $DIR/deallocate_against_protector2.rs:LL:CC
|
LL | f(x)
| ^^^^
-note: inside `main` at $DIR/deallocate_against_protector2.rs:LL:CC
+note: inside `main`
--> $DIR/deallocate_against_protector2.rs:LL:CC
|
LL | / inner(Box::leak(Box::new(NotUnpin(0, PhantomPinned))), |x| {
| ^^^^^^^^^^^^^
= note: BACKTRACE:
= note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
-note: inside `main` at $DIR/illegal_deALLOC.rs:LL:CC
+note: inside `main`
--> $DIR/illegal_deALLOC.rs:LL:CC
|
LL | dealloc(ptr2, Layout::from_size_align_unchecked(1, 1));
| ^
= note: BACKTRACE:
= note: inside `foo` at $DIR/illegal_write6.rs:LL:CC
-note: inside `main` at $DIR/illegal_write6.rs:LL:CC
+note: inside `main`
--> $DIR/illegal_write6.rs:LL:CC
|
LL | foo(x, p);
| ^^
= note: BACKTRACE:
= note: inside `inner` at $DIR/invalidate_against_protector1.rs:LL:CC
-note: inside `main` at $DIR/invalidate_against_protector1.rs:LL:CC
+note: inside `main`
--> $DIR/invalidate_against_protector1.rs:LL:CC
|
LL | inner(xraw, xref);
| ^^
= note: BACKTRACE:
= note: inside `inner` at $DIR/invalidate_against_protector2.rs:LL:CC
-note: inside `main` at $DIR/invalidate_against_protector2.rs:LL:CC
+note: inside `main`
--> $DIR/invalidate_against_protector2.rs:LL:CC
|
LL | inner(xraw, xref);
| ^^
= note: BACKTRACE:
= note: inside `inner` at $DIR/invalidate_against_protector3.rs:LL:CC
-note: inside `main` at $DIR/invalidate_against_protector3.rs:LL:CC
+note: inside `main`
--> $DIR/invalidate_against_protector3.rs:LL:CC
|
LL | inner(ptr, &*ptr);
= note: BACKTRACE:
= note: inside `std::boxed::Box::<u32>::from_raw_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC
= note: inside `std::boxed::Box::<u32>::from_raw` at RUSTLIB/alloc/src/boxed.rs:LL:CC
-note: inside `main` at $DIR/issue-miri-1050-1.rs:LL:CC
+note: inside `main`
--> $DIR/issue-miri-1050-1.rs:LL:CC
|
LL | drop(Box::from_raw(ptr as *mut u32));
= note: BACKTRACE:
= note: inside `std::boxed::Box::<i32>::from_raw_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC
= note: inside `std::boxed::Box::<i32>::from_raw` at RUSTLIB/alloc/src/boxed.rs:LL:CC
-note: inside `main` at $DIR/issue-miri-1050-2.rs:LL:CC
+note: inside `main`
--> $DIR/issue-miri-1050-2.rs:LL:CC
|
LL | drop(Box::from_raw(ptr.as_ptr()));
| ^^^^^^^^
= note: BACKTRACE:
= note: inside `unknown_code_2` at $DIR/mut_exclusive_violation1.rs:LL:CC
-note: inside `demo_mut_advanced_unique` at $DIR/mut_exclusive_violation1.rs:LL:CC
+note: inside `demo_mut_advanced_unique`
--> $DIR/mut_exclusive_violation1.rs:LL:CC
|
LL | unknown_code_2();
| ^^^^^^^^^^^^^^^^
-note: inside `main` at $DIR/mut_exclusive_violation1.rs:LL:CC
+note: inside `main`
--> $DIR/mut_exclusive_violation1.rs:LL:CC
|
LL | demo_mut_advanced_unique(&mut 0);
= note: BACKTRACE:
= note: inside `std::boxed::Box::<i32>::from_raw_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC
= note: inside `std::boxed::Box::<i32>::from_raw` at RUSTLIB/alloc/src/boxed.rs:LL:CC
-note: inside closure at $DIR/newtype_pair_retagging.rs:LL:CC
+note: inside closure
--> $DIR/newtype_pair_retagging.rs:LL:CC
|
LL | || drop(Box::from_raw(ptr)),
| ^^^^^^^^^^^^^^^^^^
-note: inside `dealloc_while_running::<[closure@$DIR/newtype_pair_retagging.rs:LL:CC]>` at $DIR/newtype_pair_retagging.rs:LL:CC
+note: inside `dealloc_while_running::<[closure@$DIR/newtype_pair_retagging.rs:LL:CC]>`
--> $DIR/newtype_pair_retagging.rs:LL:CC
|
LL | dealloc();
| ^^^^^^^^^
-note: inside `main` at $DIR/newtype_pair_retagging.rs:LL:CC
+note: inside `main`
--> $DIR/newtype_pair_retagging.rs:LL:CC
|
LL | / dealloc_while_running(
= note: BACKTRACE:
= note: inside `std::boxed::Box::<i32>::from_raw_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC
= note: inside `std::boxed::Box::<i32>::from_raw` at RUSTLIB/alloc/src/boxed.rs:LL:CC
-note: inside closure at $DIR/newtype_retagging.rs:LL:CC
+note: inside closure
--> $DIR/newtype_retagging.rs:LL:CC
|
LL | || drop(Box::from_raw(ptr)),
| ^^^^^^^^^^^^^^^^^^
-note: inside `dealloc_while_running::<[closure@$DIR/newtype_retagging.rs:LL:CC]>` at $DIR/newtype_retagging.rs:LL:CC
+note: inside `dealloc_while_running::<[closure@$DIR/newtype_retagging.rs:LL:CC]>`
--> $DIR/newtype_retagging.rs:LL:CC
|
LL | dealloc();
| ^^^^^^^^^
-note: inside `main` at $DIR/newtype_retagging.rs:LL:CC
+note: inside `main`
--> $DIR/newtype_retagging.rs:LL:CC
|
LL | / dealloc_while_running(
+++ /dev/null
-//! Reborrowing a `&mut !Unpin` must still act like a (fake) read.
-use std::marker::PhantomPinned;
-
-struct NotUnpin(i32, PhantomPinned);
-
-fn main() {
- unsafe {
- let mut x = NotUnpin(0, PhantomPinned);
- // Mutable borrow of `Unpin` field (with lifetime laundering)
- let fieldref = &mut *(&mut x.0 as *mut i32);
- // Mutable reborrow of the entire `x`, which is `!Unpin` but should
- // still count as a read since we would add `dereferenceable`.
- let _xref = &mut x;
- // That read should have invalidated `fieldref`.
- *fieldref = 0; //~ ERROR: /write access .* tag does not exist in the borrow stack/
- }
-}
+++ /dev/null
-error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
- --> $DIR/notunpin_dereferenceable_fakeread.rs:LL:CC
- |
-LL | *fieldref = 0;
- | ^^^^^^^^^^^^^
- | |
- | attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
- | this error occurs as part of an access at ALLOC[0x0..0x4]
- |
- = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
- = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
-help: <TAG> was created by a Unique retag at offsets [0x0..0x4]
- --> $DIR/notunpin_dereferenceable_fakeread.rs:LL:CC
- |
-LL | let fieldref = &mut *(&mut x.0 as *mut i32);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: <TAG> was later invalidated at offsets [0x0..0x4] by a SharedReadWrite retag
- --> $DIR/notunpin_dereferenceable_fakeread.rs:LL:CC
- |
-LL | let _xref = &mut x;
- | ^^^^^^
- = note: BACKTRACE:
- = note: inside `main` at $DIR/notunpin_dereferenceable_fakeread.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to previous error
-
| ^^^^^^^^
= note: BACKTRACE:
= note: inside `fun2` at $DIR/pointer_smuggling.rs:LL:CC
-note: inside `main` at $DIR/pointer_smuggling.rs:LL:CC
+note: inside `main`
--> $DIR/pointer_smuggling.rs:LL:CC
|
LL | fun2(); // if they now use a raw ptr they break our reference
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `thread_2` at $DIR/retag_data_race_read.rs:LL:CC
-note: inside closure at $DIR/retag_data_race_read.rs:LL:CC
+note: inside closure
--> $DIR/retag_data_race_read.rs:LL:CC
|
LL | let t2 = std::thread::spawn(move || thread_2(p));
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `thread_2` at $DIR/retag_data_race_write.rs:LL:CC
-note: inside closure at $DIR/retag_data_race_write.rs:LL:CC
+note: inside closure
--> $DIR/retag_data_race_write.rs:LL:CC
|
LL | let t2 = std::thread::spawn(move || thread_2(p));
| ^^^^^
= note: BACKTRACE:
= note: inside `foo` at $DIR/return_invalid_mut.rs:LL:CC
-note: inside `main` at $DIR/return_invalid_mut.rs:LL:CC
+note: inside `main`
--> $DIR/return_invalid_mut.rs:LL:CC
|
LL | foo(&mut (1, 2));
| ^^^^^
= note: BACKTRACE:
= note: inside `foo` at $DIR/return_invalid_mut_option.rs:LL:CC
-note: inside `main` at $DIR/return_invalid_mut_option.rs:LL:CC
+note: inside `main`
--> $DIR/return_invalid_mut_option.rs:LL:CC
|
LL | match foo(&mut (1, 2)) {
| ^^^^^
= note: BACKTRACE:
= note: inside `foo` at $DIR/return_invalid_mut_tuple.rs:LL:CC
-note: inside `main` at $DIR/return_invalid_mut_tuple.rs:LL:CC
+note: inside `main`
--> $DIR/return_invalid_mut_tuple.rs:LL:CC
|
LL | foo(&mut (1, 2)).0;
| ^^^^^^^^^^^^^^^^
= note: BACKTRACE:
= note: inside `foo` at $DIR/return_invalid_shr.rs:LL:CC
-note: inside `main` at $DIR/return_invalid_shr.rs:LL:CC
+note: inside `main`
--> $DIR/return_invalid_shr.rs:LL:CC
|
LL | foo(&mut (1, 2));
| ^^^^^^^^^^^^^^^^
= note: BACKTRACE:
= note: inside `foo` at $DIR/return_invalid_shr_option.rs:LL:CC
-note: inside `main` at $DIR/return_invalid_shr_option.rs:LL:CC
+note: inside `main`
--> $DIR/return_invalid_shr_option.rs:LL:CC
|
LL | match foo(&mut (1, 2)) {
| ^^^^^^^^^^^^^^^^
= note: BACKTRACE:
= note: inside `foo` at $DIR/return_invalid_shr_tuple.rs:LL:CC
-note: inside `main` at $DIR/return_invalid_shr_tuple.rs:LL:CC
+note: inside `main`
--> $DIR/return_invalid_shr_tuple.rs:LL:CC
|
LL | foo(&mut (1, 2)).0;
| ^
= note: BACKTRACE:
= note: inside `unknown_code` at $DIR/shr_frozen_violation1.rs:LL:CC
-note: inside `foo` at $DIR/shr_frozen_violation1.rs:LL:CC
+note: inside `foo`
--> $DIR/shr_frozen_violation1.rs:LL:CC
|
LL | unknown_code(&*x);
| ^^^^^^^^^^^^^^^^^
-note: inside `main` at $DIR/shr_frozen_violation1.rs:LL:CC
+note: inside `main`
--> $DIR/shr_frozen_violation1.rs:LL:CC
|
LL | println!("{}", foo(&mut 0));
| ^^^^^^^^^^^^^^^^^^
= note: BACKTRACE:
= note: inside `core::slice::<impl [i32]>::get_unchecked::<usize>` at RUSTLIB/core/src/slice/mod.rs:LL:CC
-note: inside `main` at $DIR/zst_slice.rs:LL:CC
+note: inside `main`
--> $DIR/zst_slice.rs:LL:CC
|
LL | assert_eq!(*s.get_unchecked(1), 2);
= note: BACKTRACE:
= note: inside `<u8 as core::slice::cmp::SliceOrd>::compare` at RUSTLIB/core/src/slice/cmp.rs:LL:CC
= note: inside `core::slice::cmp::<impl std::cmp::Ord for [u8]>::cmp` at RUSTLIB/core/src/slice/cmp.rs:LL:CC
-note: inside `main` at $DIR/uninit_buffer.rs:LL:CC
+note: inside `main`
--> $DIR/uninit_buffer.rs:LL:CC
|
LL | drop(slice1.cmp(slice2));
= note: BACKTRACE:
= note: inside `<u8 as core::slice::cmp::SliceOrd>::compare` at RUSTLIB/core/src/slice/cmp.rs:LL:CC
= note: inside `core::slice::cmp::<impl std::cmp::Ord for [u8]>::cmp` at RUSTLIB/core/src/slice/cmp.rs:LL:CC
-note: inside `main` at $DIR/uninit_buffer_with_provenance.rs:LL:CC
+note: inside `main`
--> $DIR/uninit_buffer_with_provenance.rs:LL:CC
|
LL | drop(slice1.cmp(slice2));
--- /dev/null
+//! Regression test for https://github.com/rust-lang/miri/issues/2629
+use std::thread;
+
+fn main() {
+ thread::scope(|s| {
+ s.spawn(|| {});
+ });
+}
= help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning.
= note: BACKTRACE:
= note: inside `into_raw` at $DIR/box.rs:LL:CC
-note: inside `main` at $DIR/box.rs:LL:CC
+note: inside `main`
--> $DIR/box.rs:LL:CC
|
LL | into_raw();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
|
= note: inside `into_unique` at $DIR/box.rs:LL:CC
-note: inside `main` at $DIR/box.rs:LL:CC
+note: inside `main`
--> $DIR/box.rs:LL:CC
|
LL | into_unique();
--- /dev/null
+use std::thread;
+
+fn main() {
+ let mut a = vec![1, 2, 3];
+ let mut x = 0;
+
+ thread::scope(|s| {
+ s.spawn(|| {
+ // We can borrow `a` here.
+ let _s = format!("hello from the first scoped thread: {a:?}");
+ });
+ s.spawn(|| {
+ let _s = format!("hello from the second scoped thread");
+ // We can even mutably borrow `x` here,
+ // because no other threads are using it.
+ x += a[0] + a[2];
+ });
+ let _s = format!("hello from the main thread");
+ });
+
+ // After the scope, we can modify and access our variables again:
+ a.push(4);
+ assert_eq!(x, a.len());
+}
--- /dev/null
+#![feature(pin_macro)]
+
+use std::future::*;
+use std::marker::PhantomPinned;
+use std::pin::*;
+use std::ptr;
+use std::task::*;
+
+struct Delay {
+ delay: usize,
+}
+
+impl Delay {
+ fn new(delay: usize) -> Self {
+ Delay { delay }
+ }
+}
+
+impl Future for Delay {
+ type Output = ();
+ fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
+ if self.delay > 0 {
+ self.delay -= 1;
+ Poll::Pending
+ } else {
+ Poll::Ready(())
+ }
+ }
+}
+
+async fn do_stuff() {
+ (&mut Delay::new(1)).await;
+}
+
+// Same thing implemented by hand
+struct DoStuff {
+ state: usize,
+ delay: Delay,
+ delay_ref: *mut Delay,
+ _marker: PhantomPinned,
+}
+
+impl DoStuff {
+ fn new() -> Self {
+ DoStuff {
+ state: 0,
+ delay: Delay::new(1),
+ delay_ref: ptr::null_mut(),
+ _marker: PhantomPinned,
+ }
+ }
+}
+
+impl Future for DoStuff {
+ type Output = ();
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
+ unsafe {
+ let this = self.get_unchecked_mut();
+ match this.state {
+ 0 => {
+ // Set up self-ref.
+ this.delay_ref = &mut this.delay;
+ // Move to next state.
+ this.state = 1;
+ Poll::Pending
+ }
+ 1 => {
+ let delay = &mut *this.delay_ref;
+ Pin::new_unchecked(delay).poll(cx)
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+}
+
+fn run_fut<T>(fut: impl Future<Output = T>) -> T {
+ use std::sync::Arc;
+
+ struct MyWaker;
+ impl Wake for MyWaker {
+ fn wake(self: Arc<Self>) {
+ unimplemented!()
+ }
+ }
+
+ let waker = Waker::from(Arc::new(MyWaker));
+ let mut context = Context::from_waker(&waker);
+
+ let mut pinned = pin!(fut);
+ loop {
+ match pinned.as_mut().poll(&mut context) {
+ Poll::Pending => continue,
+ Poll::Ready(v) => return v,
+ }
+ }
+}
+
+fn main() {
+ run_fut(do_stuff());
+ run_fut(DoStuff::new());
+}
ref fn_decl,
ref body,
fn_decl_span: _,
+ fn_arg_span: _,
} = **closure;
let body = match body.kind {
ast::ExprKind::Block(ref block, _)
);
result.path.push(UseSegment { kind, version });
}
- UseTreeKind::Simple(ref rename, ..) => {
+ UseTreeKind::Simple(ref rename) => {
// If the path has leading double colons and is composed of only 2 segments, then we
// bypass the call to path_to_imported_ident which would get only the ident and
// lose the path root, e.g., `that` in `::that`.
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std/test).
const EXCEPTIONS: &[(&str, &str)] = &[
- ("mdbook", "MPL-2.0"), // mdbook
- ("openssl", "Apache-2.0"), // cargo, mdbook
- ("colored", "MPL-2.0"), // rustfmt
- ("ryu", "Apache-2.0 OR BSL-1.0"), // cargo/... (because of serde)
- ("bytesize", "Apache-2.0"), // cargo
- ("im-rc", "MPL-2.0+"), // cargo
- ("sized-chunks", "MPL-2.0+"), // cargo via im-rc
- ("bitmaps", "MPL-2.0+"), // cargo via im-rc
- ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
- ("snap", "BSD-3-Clause"), // rustc
+ ("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc
+ ("mdbook", "MPL-2.0"), // mdbook
+ ("openssl", "Apache-2.0"), // cargo, mdbook
+ ("colored", "MPL-2.0"), // rustfmt
+ ("ryu", "Apache-2.0 OR BSL-1.0"), // cargo/... (because of serde)
+ ("bytesize", "Apache-2.0"), // cargo
+ ("im-rc", "MPL-2.0+"), // cargo
+ ("sized-chunks", "MPL-2.0+"), // cargo via im-rc
+ ("bitmaps", "MPL-2.0+"), // cargo via im-rc
+ ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot
+ ("snap", "BSD-3-Clause"), // rustc
("fluent-langneg", "Apache-2.0"), // rustc (fluent translations)
- ("self_cell", "Apache-2.0"), // rustc (fluent translations)
+ ("self_cell", "Apache-2.0"), // rustc (fluent translations)
// FIXME: this dependency violates the documentation comment above:
("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target
("dunce", "CC0-1.0"), // cargo (dev dependency)
("similar", "Apache-2.0"), // cargo (dev dependency)
("normalize-line-endings", "Apache-2.0"), // cargo (dev dependency)
+ ("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps)
];
const EXCEPTIONS_CRANELIFT: &[(&str, &str)] = &[
"aho-corasick",
"annotate-snippets",
"ansi_term",
+ "ar_archive_writer",
"arrayvec",
"atty",
"autocfg",
"cstr",
"datafrog",
"derive_more",
- "difference",
"digest",
"displaydoc",
+ "dissimilar",
"dlmalloc",
"either",
"ena",
"snap",
"stable_deref_trait",
"stacker",
+ "static_assertions",
"syn",
"synstructure",
"tempfile",
"tracing-log",
"tracing-subscriber",
"tracing-tree",
+ "twox-hash",
"type-map",
"typenum",
"unic-char-property",
const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
"ahash",
"anyhow",
- "ar",
"arrayvec",
"autocfg",
"bumpalo",
cc = ["@rust-lang/wg-mir-opt"]
[mentions."compiler/rustc_trait_selection/src/traits/const_evaluatable.rs"]
-message = "Some changes occurred in const_evaluatable.rs"
-cc = ["@lcnr"]
+message = "Some changes occurred in `const_evaluatable.rs`"
+cc = ["@BoxyUwU"]
+
+[mentions."compiler/rustc_middle/src/ty/abstract_const.rs"]
+message = "Some changes occured in `abstract_const.rs`"
+cc = ["@BoxyUwU"]
+
+[mentions."compiler/rustc_ty_utils/src/consts.rs"]
+message = "Some changes occured in `rustc_ty_utils::consts.rs`"
+cc = ["@BoxyUwU"]
[mentions."compiler/rustc_trait_selection/src/traits/engine.rs"]
message = """
"@jackh726",
"@fee1-dead",
"@TaKO8Ki",
+ "@Nilstrieb",
]
compiler = [
"compiler-team",